summaryrefslogtreecommitdiff
path: root/tools/gfx/vulkan/render-vk.h
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2022-03-09 15:59:40 -0800
committerGitHub <noreply@github.com>2022-03-09 15:59:40 -0800
commitf6c2a0f81340a0efd10e286a3cbe33e1b564a11b (patch)
treea53f9b05d2563548149ea51abf669380cd42d97f /tools/gfx/vulkan/render-vk.h
parentf67d929c24babc302eb2807251fc09b084abac2e (diff)
gfx: restructure render-vk.cpp (#2157)
Co-authored-by: Yong He <yhe@nvidia.com>
Diffstat (limited to 'tools/gfx/vulkan/render-vk.h')
-rw-r--r--tools/gfx/vulkan/render-vk.h1839
1 files changed, 1835 insertions, 4 deletions
diff --git a/tools/gfx/vulkan/render-vk.h b/tools/gfx/vulkan/render-vk.h
index b980653c7..ca18f50aa 100644
--- a/tools/gfx/vulkan/render-vk.h
+++ b/tools/gfx/vulkan/render-vk.h
@@ -1,11 +1,1842 @@
// render-vk.h
#pragma once
-#include <cstdint>
+#include "../command-encoder-com-forward.h"
+#include "../mutable-shader-object.h"
#include "../renderer-shared.h"
+#include "../transient-resource-heap-base.h"
+#include "core/slang-chunked-list.h"
+#include "vk-api.h"
+#include "vk-descriptor-allocator.h"
+#include "vk-device-queue.h"
-namespace gfx {
+namespace gfx
+{
+namespace vk
+{
+using namespace Slang;
-SlangResult SLANG_MCALL createVKDevice(const IDevice::Desc* desc, IDevice** outDevice);
+enum
+{
+ kMaxRenderTargets = 8,
+ kMaxAttachments = kMaxRenderTargets + 1,
+ kMaxPushConstantSize = 256,
+ kMaxDescriptorSets = 8,
+};
-} // gfx
+class FramebufferImpl;
+class TransientResourceHeapImpl;
+class PipelineCommandEncoder;
+
+class DeviceImpl : public RendererBase
+{
+public:
+ // Renderer implementation
+ Result initVulkanInstanceAndDevice(const InteropHandle* handles, bool useValidationLayer);
+ virtual SLANG_NO_THROW Result SLANG_MCALL initialize(const Desc& desc) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getFormatSupportedResourceStates(Format format, ResourceStateSet* outStates) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createTransientResourceHeap(
+ const ITransientResourceHeap::Desc& desc, ITransientResourceHeap** outHeap) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createCommandQueue(const ICommandQueue::Desc& desc, ICommandQueue** outQueue) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createSwapchain(
+ const ISwapchain::Desc& desc, WindowHandle window, ISwapchain** outSwapchain) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createFramebufferLayout(
+ const IFramebufferLayout::Desc& desc, IFramebufferLayout** outLayout) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createFramebuffer(const IFramebuffer::Desc& desc, IFramebuffer** outFramebuffer) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createRenderPassLayout(
+ const IRenderPassLayout::Desc& desc, IRenderPassLayout** outRenderPassLayout) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createTextureResource(
+ const ITextureResource::Desc& desc,
+ const ITextureResource::SubresourceData* initData,
+ ITextureResource** outResource) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createBufferResource(
+ const IBufferResource::Desc& desc,
+ const void* initData,
+ IBufferResource** outResource) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createBufferFromNativeHandle(
+ InteropHandle handle,
+ const IBufferResource::Desc& srcDesc,
+ IBufferResource** outResource) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createSamplerState(ISamplerState::Desc const& desc, ISamplerState** outSampler) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL createTextureView(
+ ITextureResource* texture,
+ IResourceView::Desc const& desc,
+ IResourceView** outView) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createBufferView(
+ IBufferResource* buffer,
+ IBufferResource* counterBuffer,
+ IResourceView::Desc const& desc,
+ IResourceView** outView) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createInputLayout(IInputLayout::Desc const& desc, IInputLayout** outLayout) override;
+
+ virtual Result createShaderObjectLayout(
+ slang::TypeLayoutReflection* typeLayout, ShaderObjectLayoutBase** outLayout) override;
+ virtual Result createShaderObject(
+ ShaderObjectLayoutBase* layout, IShaderObject** outObject) override;
+ virtual Result createMutableShaderObject(
+ ShaderObjectLayoutBase* layout, IShaderObject** outObject) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createMutableRootShaderObject(IShaderProgram* program, IShaderObject** outObject) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createShaderTable(const IShaderTable::Desc& desc, IShaderTable** outShaderTable) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createProgram(
+ const IShaderProgram::Desc& desc,
+ IShaderProgram** outProgram,
+ ISlangBlob** outDiagnosticBlob) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createGraphicsPipelineState(
+ const GraphicsPipelineStateDesc& desc, IPipelineState** outState) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createComputePipelineState(
+ const ComputePipelineStateDesc& desc, IPipelineState** outState) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createRayTracingPipelineState(
+ const RayTracingPipelineStateDesc& desc, IPipelineState** outState) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createQueryPool(const IQueryPool::Desc& desc, IQueryPool** outPool) override;
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL readTextureResource(
+ ITextureResource* texture,
+ ResourceState state,
+ ISlangBlob** outBlob,
+ size_t* outRowPitch,
+ size_t* outPixelSize) override;
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL readBufferResource(
+ IBufferResource* buffer, size_t offset, size_t size, ISlangBlob** outBlob) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getAccelerationStructurePrebuildInfo(
+ const IAccelerationStructure::BuildInputs& buildInputs,
+ IAccelerationStructure::PrebuildInfo* outPrebuildInfo) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL createAccelerationStructure(
+ const IAccelerationStructure::CreateDesc& desc, IAccelerationStructure** outView) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getTextureAllocationInfo(
+ const ITextureResource::Desc& desc, size_t* outSize, size_t* outAlignment) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getTextureRowAlignment(size_t* outAlignment) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createFence(const IFence::Desc& desc, IFence** outFence) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL waitForFences(
+ uint32_t fenceCount,
+ IFence** fences,
+ uint64_t* fenceValues,
+ bool waitForAll,
+ uint64_t timeout) override;
+
+ void waitForGpu();
+
+ virtual SLANG_NO_THROW const DeviceInfo& SLANG_MCALL getDeviceInfo() const override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getNativeDeviceHandles(InteropHandles* outHandles) override;
+
+ ~DeviceImpl();
+
+public:
+ VkBool32 handleDebugMessage(
+ VkDebugReportFlagsEXT flags,
+ VkDebugReportObjectTypeEXT objType,
+ uint64_t srcObject,
+ size_t location,
+ int32_t msgCode,
+ const char* pLayerPrefix,
+ const char* pMsg);
+
+ static VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(
+ VkDebugReportFlagsEXT flags,
+ VkDebugReportObjectTypeEXT objType,
+ uint64_t srcObject,
+ size_t location,
+ int32_t msgCode,
+ const char* pLayerPrefix,
+ const char* pMsg,
+ void* pUserData);
+
+ void _transitionImageLayout(
+ VkImage image,
+ VkFormat format,
+ const TextureResource::Desc& desc,
+ VkImageLayout oldLayout,
+ VkImageLayout newLayout);
+ void _transitionImageLayout(
+ VkCommandBuffer commandBuffer,
+ VkImage image,
+ VkFormat format,
+ const TextureResource::Desc& desc,
+ VkImageLayout oldLayout,
+ VkImageLayout newLayout);
+
+ uint32_t getQueueFamilyIndex(ICommandQueue::QueueType queueType);
+
+public:
+ // DeviceImpl members.
+
+ DeviceInfo m_info;
+ String m_adapterName;
+
+ VkDebugReportCallbackEXT m_debugReportCallback = VK_NULL_HANDLE;
+
+ VkDevice m_device = VK_NULL_HANDLE;
+
+ VulkanModule m_module;
+ VulkanApi m_api;
+
+ VulkanDeviceQueue m_deviceQueue;
+ uint32_t m_queueFamilyIndex;
+
+ Desc m_desc;
+
+ DescriptorSetAllocator descriptorSetAllocator;
+
+ uint32_t m_queueAllocCount;
+
+ // A list to hold objects that may have a strong back reference to the device
+ // instance. Because of the pipeline cache in `RendererBase`, there could be a reference
+ // cycle among `DeviceImpl`->`PipelineStateImpl`->`ShaderProgramImpl`->`DeviceImpl`.
+ // Depending on whether a `PipelineState` objects gets stored in pipeline cache, there
+ // may or may not be such a reference cycle.
+ // We need to hold strong references to any objects that may become part of the reference
+ // cycle here, so that when objects like `ShaderProgramImpl` lost all public refernces, we
+ // can always safely break the strong reference in `ShaderProgramImpl::m_device` without
+ // worrying the `ShaderProgramImpl` object getting destroyed after the completion of
+ // `DeviceImpl::~DeviceImpl()'.
+ ChunkedList<RefPtr<RefObject>, 1024> m_deviceObjectsWithPotentialBackReferences;
+
+ VkSampler m_defaultSampler;
+
+ RefPtr<FramebufferImpl> m_emptyFramebuffer;
+};
+
+class VKBufferHandleRAII
+{
+public:
+ /// Initialize a buffer with specified size, and memory props
+ Result init(
+ const VulkanApi& api,
+ size_t bufferSize,
+ VkBufferUsageFlags usage,
+ VkMemoryPropertyFlags reqMemoryProperties,
+ bool isShared = false,
+ VkExternalMemoryHandleTypeFlagsKHR extMemHandleType = 0);
+
+ /// Returns true if has been initialized
+ bool isInitialized() const { return m_api != nullptr; }
+
+ VKBufferHandleRAII()
+ : m_api(nullptr)
+ {}
+
+ ~VKBufferHandleRAII()
+ {
+ if (m_api)
+ {
+ m_api->vkDestroyBuffer(m_api->m_device, m_buffer, nullptr);
+ m_api->vkFreeMemory(m_api->m_device, m_memory, nullptr);
+ }
+ }
+
+ VkBuffer m_buffer;
+ VkDeviceMemory m_memory;
+ const VulkanApi* m_api;
+};
+
+class InputLayoutImpl : public InputLayoutBase
+{
+public:
+ List<VkVertexInputAttributeDescription> m_attributeDescs;
+ List<VkVertexInputBindingDescription> m_streamDescs;
+};
+
+class BufferResourceImpl : public BufferResource
+{
+public:
+ typedef BufferResource Parent;
+
+ BufferResourceImpl(const IBufferResource::Desc& desc, DeviceImpl* renderer);
+
+ ~BufferResourceImpl();
+
+ RefPtr<DeviceImpl> m_renderer;
+ VKBufferHandleRAII m_buffer;
+ VKBufferHandleRAII m_uploadBuffer;
+
+ virtual SLANG_NO_THROW DeviceAddress SLANG_MCALL getDeviceAddress() override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getNativeResourceHandle(InteropHandle* outHandle) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getSharedHandle(InteropHandle* outHandle) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ map(MemoryRange* rangeToRead, void** outPointer) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL unmap(MemoryRange* writtenRange) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL setDebugName(const char* name) override;
+};
+
+class FenceImpl : public FenceBase
+{
+public:
+ VkSemaphore m_semaphore = VK_NULL_HANDLE;
+ RefPtr<DeviceImpl> m_device;
+
+ FenceImpl(DeviceImpl* device);
+
+ ~FenceImpl();
+
+ Result init(const IFence::Desc& desc);
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getCurrentValue(uint64_t* outValue) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL setCurrentValue(uint64_t value) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getSharedHandle(InteropHandle* outHandle) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getNativeHandle(InteropHandle* outNativeHandle) override;
+};
+
+class TextureResourceImpl : public TextureResource
+{
+public:
+ typedef TextureResource Parent;
+ TextureResourceImpl(const Desc& desc, DeviceImpl* device);
+ ~TextureResourceImpl();
+
+ VkImage m_image = VK_NULL_HANDLE;
+ VkFormat m_vkformat = VK_FORMAT_R8G8B8A8_UNORM;
+ VkDeviceMemory m_imageMemory = VK_NULL_HANDLE;
+ bool m_isWeakImageReference = false;
+ RefPtr<DeviceImpl> m_device;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getNativeResourceHandle(InteropHandle* outHandle) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getSharedHandle(InteropHandle* outHandle) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL setDebugName(const char* name) override;
+};
+
+class SamplerStateImpl : public SamplerStateBase
+{
+public:
+ VkSampler m_sampler;
+ RefPtr<DeviceImpl> m_device;
+ SamplerStateImpl(DeviceImpl* device);
+ ~SamplerStateImpl();
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
+};
+
+class ResourceViewImpl : public ResourceViewBase
+{
+public:
+ enum class ViewType
+ {
+ Texture,
+ TexelBuffer,
+ PlainBuffer,
+ };
+
+public:
+ ResourceViewImpl(ViewType viewType, DeviceImpl* device)
+ : m_type(viewType)
+ , m_device(device)
+ {}
+ ViewType m_type;
+ RefPtr<DeviceImpl> m_device;
+};
+
+class TextureResourceViewImpl : public ResourceViewImpl
+{
+public:
+ TextureResourceViewImpl(DeviceImpl* device)
+ : ResourceViewImpl(ViewType::Texture, device)
+ {}
+ ~TextureResourceViewImpl();
+ RefPtr<TextureResourceImpl> m_texture;
+ VkImageView m_view;
+ VkImageLayout m_layout;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
+};
+
+class TexelBufferResourceViewImpl : public ResourceViewImpl
+{
+public:
+ TexelBufferResourceViewImpl(DeviceImpl* device);
+ ~TexelBufferResourceViewImpl();
+ RefPtr<BufferResourceImpl> m_buffer;
+ VkBufferView m_view;
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
+};
+
+class PlainBufferResourceViewImpl : public ResourceViewImpl
+{
+public:
+ PlainBufferResourceViewImpl(DeviceImpl* device);
+ RefPtr<BufferResourceImpl> m_buffer;
+ VkDeviceSize offset;
+ VkDeviceSize size;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
+};
+
+class AccelerationStructureImpl : public AccelerationStructureBase
+{
+public:
+ VkAccelerationStructureKHR m_vkHandle = VK_NULL_HANDLE;
+ RefPtr<BufferResourceImpl> m_buffer;
+ VkDeviceSize m_offset;
+ VkDeviceSize m_size;
+ RefPtr<DeviceImpl> m_device;
+
+public:
+ virtual SLANG_NO_THROW DeviceAddress SLANG_MCALL getDeviceAddress() override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
+ ~AccelerationStructureImpl();
+};
+
+class FramebufferLayoutImpl : public FramebufferLayoutBase
+{
+public:
+ VkRenderPass m_renderPass;
+ DeviceImpl* m_renderer;
+ Array<VkAttachmentDescription, kMaxAttachments> m_attachmentDescs;
+ Array<VkAttachmentReference, kMaxRenderTargets> m_colorReferences;
+ VkAttachmentReference m_depthReference;
+ bool m_hasDepthStencilAttachment;
+ uint32_t m_renderTargetCount;
+ VkSampleCountFlagBits m_sampleCount = VK_SAMPLE_COUNT_1_BIT;
+
+public:
+ ~FramebufferLayoutImpl();
+ Result init(DeviceImpl* renderer, const IFramebufferLayout::Desc& desc);
+};
+
+class RenderPassLayoutImpl
+ : public IRenderPassLayout
+ , public ComObject
+{
+public:
+ SLANG_COM_OBJECT_IUNKNOWN_ALL
+ IRenderPassLayout* getInterface(const Guid& guid);
+
+public:
+ VkRenderPass m_renderPass;
+ RefPtr<DeviceImpl> m_renderer;
+ ~RenderPassLayoutImpl();
+
+ Result init(DeviceImpl* renderer, const IRenderPassLayout::Desc& desc);
+};
+
+class FramebufferImpl : public FramebufferBase
+{
+public:
+ VkFramebuffer m_handle;
+ ShortList<ComPtr<IResourceView>> renderTargetViews;
+ ComPtr<IResourceView> depthStencilView;
+ uint32_t m_width;
+ uint32_t m_height;
+ BreakableReference<DeviceImpl> m_renderer;
+ VkClearValue m_clearValues[kMaxAttachments];
+ RefPtr<FramebufferLayoutImpl> m_layout;
+
+public:
+ ~FramebufferImpl();
+
+ Result init(DeviceImpl* renderer, const IFramebuffer::Desc& desc);
+};
+
+struct BoundVertexBuffer
+{
+ RefPtr<BufferResourceImpl> m_buffer;
+ int m_offset;
+};
+
+class PipelineStateImpl : public PipelineStateBase
+{
+public:
+ PipelineStateImpl(DeviceImpl* device);
+ ~PipelineStateImpl();
+
+ // Turns `m_device` into a strong reference.
+ // This method should be called before returning the pipeline state object to
+ // external users (i.e. via an `IPipelineState` pointer).
+ void establishStrongDeviceReference();
+
+ virtual void comFree() override;
+
+ void init(const GraphicsPipelineStateDesc& inDesc);
+ void init(const ComputePipelineStateDesc& inDesc);
+ void init(const RayTracingPipelineStateDesc& inDesc);
+
+ Result createVKGraphicsPipelineState();
+
+ Result createVKComputePipelineState();
+
+ virtual Result ensureAPIPipelineStateCreated() override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
+
+ BreakableReference<DeviceImpl> m_device;
+
+ VkPipeline m_pipeline = VK_NULL_HANDLE;
+};
+
+class RayTracingPipelineStateImpl : public PipelineStateImpl
+{
+public:
+ Dictionary<String, Index> shaderGroupNameToIndex;
+ Int shaderGroupCount;
+
+ RayTracingPipelineStateImpl(DeviceImpl* device);
+
+ uint32_t findEntryPointIndexByName(
+ const Dictionary<String, Index>& entryPointNameToIndex, const char* name);
+
+ Result createVKRayTracingPipelineState();
+
+ virtual Result ensureAPIPipelineStateCreated() override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
+};
+
+// In order to bind shader parameters to the correct locations, we need to
+// be able to describe those locations. Most shader parameters in Vulkan
+// simply consume a single `binding`, but we also need to deal with
+// parameters that represent push-constant ranges.
+//
+// In more complex cases we might be binding an entire "sub-object" like
+// a parameter block, an entry point, etc. For the general case, we need
+// to be able to represent a composite offset that includes offsets for
+// each of the cases that Vulkan supports.
+
+/// A "simple" binding offset that records `binding`, `set`, etc. offsets
+struct SimpleBindingOffset
+{
+ /// An offset in GLSL/SPIR-V `binding`s
+ uint32_t binding = 0;
+
+ /// The descriptor `set` that the `binding` field should be understood as an index into
+ uint32_t bindingSet = 0;
+
+ /// The offset in push-constant ranges (not bytes)
+ uint32_t pushConstantRange = 0;
+
+ /// Create a default (zero) offset
+ SimpleBindingOffset() {}
+
+ /// Create an offset based on offset information in the given Slang `varLayout`
+ SimpleBindingOffset(slang::VariableLayoutReflection* varLayout)
+ {
+ if (varLayout)
+ {
+ bindingSet = (uint32_t)varLayout->getBindingSpace(
+ SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT);
+ binding =
+ (uint32_t)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT);
+ pushConstantRange =
+ (uint32_t)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_PUSH_CONSTANT_BUFFER);
+ }
+ }
+
+ /// Add any values in the given `offset`
+ void operator+=(SimpleBindingOffset const& offset)
+ {
+ binding += offset.binding;
+ bindingSet += offset.bindingSet;
+ pushConstantRange += offset.pushConstantRange;
+ }
+};
+
+// While a "simple" binding offset representation will work in many cases,
+// once we need to deal with layout for programs with interface-type parameters
+// that have been statically specialized, we also need to track the offset
+// for where to bind any "pending" data that arises from the process of static
+// specialization.
+//
+// In order to conveniently track both the "primary" and "pending" offset information,
+// we will define a more complete `BindingOffset` type that combines simple
+// binding offsets for the primary and pending parts.
+
+/// A representation of the offset at which to bind a shader parameter or sub-object
+struct BindingOffset : SimpleBindingOffset
+{
+ // Offsets for "primary" data are stored directly in the `BindingOffset`
+ // via the inheritance from `SimpleBindingOffset`.
+
+ /// Offset for any "pending" data
+ SimpleBindingOffset pending;
+
+ /// Create a default (zero) offset
+ BindingOffset() {}
+
+ /// Create an offset from a simple offset
+ explicit BindingOffset(SimpleBindingOffset const& offset)
+ : SimpleBindingOffset(offset)
+ {}
+
+ /// Create an offset based on offset information in the given Slang `varLayout`
+ BindingOffset(slang::VariableLayoutReflection* varLayout)
+ : SimpleBindingOffset(varLayout)
+ , pending(varLayout->getPendingDataLayout())
+ {}
+
+ /// Add any values in the given `offset`
+ void operator+=(SimpleBindingOffset const& offset) { SimpleBindingOffset::operator+=(offset); }
+
+ /// Add any values in the given `offset`
+ void operator+=(BindingOffset const& offset)
+ {
+ SimpleBindingOffset::operator+=(offset);
+ pending += offset.pending;
+ }
+};
+
+class ShaderObjectLayoutImpl : public ShaderObjectLayoutBase
+{
+public:
+ // A shader object comprises three main kinds of state:
+ //
+ // * Zero or more bytes of ordinary ("uniform") data
+ // * Zero or more *bindings* for textures, buffers, and samplers
+ // * Zero or more *sub-objects* representing nested parameter blocks, etc.
+ //
+ // A shader object *layout* stores information that can be used to
+ // organize these different kinds of state and optimize access to them.
+ //
+ // For example, both texture/buffer/sampler bindings and sub-objects
+ // are organized into logical *binding ranges* by the Slang reflection
+ // API, and a shader object layout will store information about those
+ // ranges in a form that is usable for the Vulkan API:
+
+ struct BindingRangeInfo
+ {
+ slang::BindingType bindingType;
+ Index count;
+ Index baseIndex;
+
+ /// An index into the sub-object array if this binding range is treated
+ /// as a sub-object.
+ Index subObjectIndex;
+
+ /// The `binding` offset to apply for this range
+ uint32_t bindingOffset;
+
+ /// The `set` offset to apply for this range
+ uint32_t setOffset;
+
+ // Note: The 99% case is that `setOffset` will be zero. For any shader object
+ // that was allocated from an ordinary Slang type (anything other than a root
+ // shader object in fact), all of the bindings will have been allocated into
+ // a single logical descriptor set.
+ //
+ // TODO: Ideally we could refactor so that only the root shader object layout
+ // stores a set offset for its binding ranges, and all other objects skip
+ // storing a field that never actually matters.
+ };
+
+ // Sometimes we just want to iterate over the ranges that represnet
+ // sub-objects while skipping over the others, because sub-object
+ // ranges often require extra handling or more state.
+ //
+ // For that reason we also store pre-computed information about each
+ // sub-object range.
+
+ /// Offset information for a sub-object range
+ struct SubObjectRangeOffset : BindingOffset
+ {
+ SubObjectRangeOffset() {}
+
+ SubObjectRangeOffset(slang::VariableLayoutReflection* varLayout)
+ : BindingOffset(varLayout)
+ {
+ if (auto pendingLayout = varLayout->getPendingDataLayout())
+ {
+ pendingOrdinaryData =
+ (uint32_t)pendingLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNIFORM);
+ }
+ }
+
+ /// The offset for "pending" ordinary data related to this range
+ uint32_t pendingOrdinaryData = 0;
+ };
+
+ /// Stride information for a sub-object range
+ struct SubObjectRangeStride : BindingOffset
+ {
+ SubObjectRangeStride() {}
+
+ SubObjectRangeStride(slang::TypeLayoutReflection* typeLayout)
+ {
+ if (auto pendingLayout = typeLayout->getPendingDataTypeLayout())
+ {
+ pendingOrdinaryData = (uint32_t)pendingLayout->getStride();
+ }
+ }
+
+ /// The strid for "pending" ordinary data related to this range
+ uint32_t pendingOrdinaryData = 0;
+ };
+
+ /// Information about a logical binding range as reported by Slang reflection
+ struct SubObjectRangeInfo
+ {
+ /// The index of the binding range that corresponds to this sub-object range
+ Index bindingRangeIndex;
+
+ /// The layout expected for objects bound to this range (if known)
+ RefPtr<ShaderObjectLayoutImpl> layout;
+
+ /// The offset to use when binding the first object in this range
+ SubObjectRangeOffset offset;
+
+ /// Stride between consecutive objects in this range
+ SubObjectRangeStride stride;
+ };
+
+ struct DescriptorSetInfo
+ {
+ List<VkDescriptorSetLayoutBinding> vkBindings;
+ Slang::Int space = -1;
+ VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
+ };
+
+ struct Builder
+ {
+ public:
+ Builder(DeviceImpl* renderer)
+ : m_renderer(renderer)
+ {}
+
+ DeviceImpl* m_renderer;
+ slang::TypeLayoutReflection* m_elementTypeLayout;
+
+ /// The container type of this shader object. When `m_containerType` is
+ /// `StructuredBuffer` or `UnsizedArray`, this shader object represents a collection
+ /// instead of a single object.
+ ShaderObjectContainerType m_containerType = ShaderObjectContainerType::None;
+
+ List<BindingRangeInfo> m_bindingRanges;
+ List<SubObjectRangeInfo> m_subObjectRanges;
+
+ Index m_resourceViewCount = 0;
+ Index m_samplerCount = 0;
+ Index m_combinedTextureSamplerCount = 0;
+ Index m_subObjectCount = 0;
+ Index m_varyingInputCount = 0;
+ Index m_varyingOutputCount = 0;
+ List<DescriptorSetInfo> m_descriptorSetBuildInfos;
+ Dictionary<Index, Index> m_mapSpaceToDescriptorSetIndex;
+
+ /// The number of descriptor sets allocated by child/descendent objects
+ uint32_t m_childDescriptorSetCount = 0;
+
+ /// The total number of `binding`s consumed by this object and its children/descendents
+ uint32_t m_totalBindingCount = 0;
+
+ /// The push-constant ranges that belong to this object itself (if any)
+ List<VkPushConstantRange> m_ownPushConstantRanges;
+
+ /// The number of push-constant ranges owned by child/descendent objects
+ uint32_t m_childPushConstantRangeCount = 0;
+
+ uint32_t m_totalOrdinaryDataSize = 0;
+
+ Index findOrAddDescriptorSet(Index space);
+
+ static VkDescriptorType _mapDescriptorType(slang::BindingType slangBindingType);
+
+ /// Add any descriptor ranges implied by this object containing a leaf
+ /// sub-object described by `typeLayout`, at the given `offset`.
+ void _addDescriptorRangesAsValue(
+ slang::TypeLayoutReflection* typeLayout, BindingOffset const& offset);
+
+ /// Add the descriptor ranges implied by a `ConstantBuffer<X>` where `X` is
+ /// described by `elementTypeLayout`.
+ ///
+ /// The `containerOffset` and `elementOffset` are the binding offsets that
+ /// should apply to the buffer itself and the contents of the buffer, respectively.
+ ///
+ void _addDescriptorRangesAsConstantBuffer(
+ slang::TypeLayoutReflection* elementTypeLayout,
+ BindingOffset const& containerOffset,
+ BindingOffset const& elementOffset);
+
+ /// Add the descriptor ranges implied by a `PushConstantBuffer<X>` where `X` is
+ /// described by `elementTypeLayout`.
+ ///
+ /// The `containerOffset` and `elementOffset` are the binding offsets that
+ /// should apply to the buffer itself and the contents of the buffer, respectively.
+ ///
+ void _addDescriptorRangesAsPushConstantBuffer(
+ slang::TypeLayoutReflection* elementTypeLayout,
+ BindingOffset const& containerOffset,
+ BindingOffset const& elementOffset);
+
+ /// Add binding ranges to this shader object layout, as implied by the given
+ /// `typeLayout`
+ void addBindingRanges(slang::TypeLayoutReflection* typeLayout);
+
+ Result setElementTypeLayout(slang::TypeLayoutReflection* typeLayout);
+
+ SlangResult build(ShaderObjectLayoutImpl** outLayout);
+ };
+
+ static Result createForElementType(
+ DeviceImpl* renderer,
+ slang::TypeLayoutReflection* elementType,
+ ShaderObjectLayoutImpl** outLayout);
+
+ ~ShaderObjectLayoutImpl();
+
+ /// Get the number of descriptor sets that are allocated for this object itself
+ /// (if it needed to be bound as a parameter block).
+ ///
+ uint32_t getOwnDescriptorSetCount() { return uint32_t(m_descriptorSetInfos.getCount()); }
+
+ /// Get information about the descriptor sets that would be allocated to
+ /// represent this object itself as a parameter block.
+ ///
+ List<DescriptorSetInfo> const& getOwnDescriptorSets() { return m_descriptorSetInfos; }
+
+ /// Get the number of descriptor sets that would need to be allocated and bound
+ /// to represent the children of this object if it were bound as a parameter
+ /// block.
+ ///
+ /// To a first approximation, this is the number of (transitive) children
+ /// that are declared as `ParameterBlock<X>`.
+ ///
+ uint32_t getChildDescriptorSetCount() { return m_childDescriptorSetCount; }
+
+ /// Get the total number of descriptor sets that would need to be allocated and bound
+ /// to represent this object and its children (transitively) as a parameter block.
+ ///
+ uint32_t getTotalDescriptorSetCount()
+ {
+ return getOwnDescriptorSetCount() + getChildDescriptorSetCount();
+ }
+
+ /// Get the total number of `binding`s required to represent this type and its
+ /// (transitive) children.
+ ///
+ /// Note that this count does *not* include bindings that would be part of child
+ /// parameter blocks, nor does it include the binding for an ordinary data buffer,
+ /// if one is needed.
+ ///
+ uint32_t getTotalBindingCount() { return m_totalBindingCount; }
+
+ /// Get the list of push constant ranges required to bind the state of this object itself.
+ List<VkPushConstantRange> const& getOwnPushConstantRanges() const
+ {
+ return m_ownPushConstantRanges;
+ }
+
+ /// Get the number of push constant ranges required to bind the state of this object itself.
+ uint32_t getOwnPushConstantRangeCount() { return (uint32_t)m_ownPushConstantRanges.getCount(); }
+
+ /// Get the number of push constant ranges required to bind the state of the (transitive)
+ /// children of this object.
+ uint32_t getChildPushConstantRangeCount() { return m_childPushConstantRangeCount; }
+
+ /// Get the total number of push constant ranges required to bind the state of this object
+ /// and its (transitive) children.
+ uint32_t getTotalPushConstantRangeCount()
+ {
+ return getOwnPushConstantRangeCount() + getChildPushConstantRangeCount();
+ }
+
+ uint32_t getTotalOrdinaryDataSize() const { return m_totalOrdinaryDataSize; }
+
+ List<BindingRangeInfo> const& getBindingRanges() { return m_bindingRanges; }
+
+ Index getBindingRangeCount() { return m_bindingRanges.getCount(); }
+
+ BindingRangeInfo const& getBindingRange(Index index) { return m_bindingRanges[index]; }
+
+ Index getResourceViewCount() { return m_resourceViewCount; }
+ Index getSamplerCount() { return m_samplerCount; }
+ Index getCombinedTextureSamplerCount() { return m_combinedTextureSamplerCount; }
+ Index getSubObjectCount() { return m_subObjectCount; }
+
+ SubObjectRangeInfo const& getSubObjectRange(Index index) { return m_subObjectRanges[index]; }
+ List<SubObjectRangeInfo> const& getSubObjectRanges() { return m_subObjectRanges; }
+
+ DeviceImpl* getDevice() { return static_cast<DeviceImpl*>(m_renderer); }
+
+ slang::TypeReflection* getType() { return m_elementTypeLayout->getType(); }
+
+protected:
+ Result _init(Builder const* builder);
+
+ List<DescriptorSetInfo> m_descriptorSetInfos;
+ List<BindingRangeInfo> m_bindingRanges;
+ Index m_resourceViewCount = 0;
+ Index m_samplerCount = 0;
+ Index m_combinedTextureSamplerCount = 0;
+ Index m_subObjectCount = 0;
+ List<VkPushConstantRange> m_ownPushConstantRanges;
+ uint32_t m_childPushConstantRangeCount = 0;
+
+ uint32_t m_childDescriptorSetCount = 0;
+ uint32_t m_totalBindingCount = 0;
+ uint32_t m_totalOrdinaryDataSize = 0;
+
+ List<SubObjectRangeInfo> m_subObjectRanges;
+};
+
+class EntryPointLayout : public ShaderObjectLayoutImpl
+{
+ typedef ShaderObjectLayoutImpl Super;
+
+public:
+ struct Builder : Super::Builder
+ {
+ Builder(DeviceImpl* device)
+ : Super::Builder(device)
+ {}
+
+ Result build(EntryPointLayout** outLayout);
+
+ void addEntryPointParams(slang::EntryPointLayout* entryPointLayout);
+
+ slang::EntryPointLayout* m_slangEntryPointLayout = nullptr;
+
+ VkShaderStageFlags m_shaderStageFlag;
+ };
+
+ Result _init(Builder const* builder);
+
+ VkShaderStageFlags getShaderStageFlag() const { return m_shaderStageFlag; }
+
+ slang::EntryPointLayout* getSlangLayout() const { return m_slangEntryPointLayout; };
+
+ slang::EntryPointLayout* m_slangEntryPointLayout;
+ VkShaderStageFlags m_shaderStageFlag;
+};
+
+class RootShaderObjectLayout : public ShaderObjectLayoutImpl
+{
+ typedef ShaderObjectLayoutImpl Super;
+
+public:
+ ~RootShaderObjectLayout();
+
+ /// Information stored for each entry point of the program
+ struct EntryPointInfo
+ {
+ /// Layout of the entry point
+ RefPtr<EntryPointLayout> layout;
+
+ /// Offset for binding the entry point, relative to the start of the program
+ BindingOffset offset;
+ };
+
+ struct Builder : Super::Builder
+ {
+ Builder(
+ DeviceImpl* renderer,
+ slang::IComponentType* program,
+ slang::ProgramLayout* programLayout)
+ : Super::Builder(renderer)
+ , m_program(program)
+ , m_programLayout(programLayout)
+ {}
+
+ Result build(RootShaderObjectLayout** outLayout);
+
+ void addGlobalParams(slang::VariableLayoutReflection* globalsLayout);
+
+ void addEntryPoint(EntryPointLayout* entryPointLayout);
+
+ slang::IComponentType* m_program;
+ slang::ProgramLayout* m_programLayout;
+ List<EntryPointInfo> m_entryPoints;
+
+ /// Offset to apply to "pending" data from this object, sub-objects, and entry points
+ SimpleBindingOffset m_pendingDataOffset;
+ };
+
+ Index findEntryPointIndex(VkShaderStageFlags stage);
+
+ EntryPointInfo const& getEntryPoint(Index index) { return m_entryPoints[index]; }
+
+ List<EntryPointInfo> const& getEntryPoints() const { return m_entryPoints; }
+
+ static Result create(
+ DeviceImpl* renderer,
+ slang::IComponentType* program,
+ slang::ProgramLayout* programLayout,
+ RootShaderObjectLayout** outLayout);
+
+ SimpleBindingOffset const& getPendingDataOffset() const { return m_pendingDataOffset; }
+
+ slang::IComponentType* getSlangProgram() const { return m_program; }
+ slang::ProgramLayout* getSlangProgramLayout() const { return m_programLayout; }
+
+ /// Get all of the push constant ranges that will be bound for this object and all
+ /// (transitive) sub-objects
+ List<VkPushConstantRange> const& getAllPushConstantRanges() { return m_allPushConstantRanges; }
+
+protected:
+ Result _init(Builder const* builder);
+
+ /// Add all the descriptor sets implied by this root object and sub-objects
+ Result addAllDescriptorSets();
+
+ /// Recurisvely add descriptor sets defined by `layout` and sub-objects
+ Result addAllDescriptorSetsRec(ShaderObjectLayoutImpl* layout);
+
+ /// Recurisvely add descriptor sets defined by sub-objects of `layout`
+ Result addChildDescriptorSetsRec(ShaderObjectLayoutImpl* layout);
+
+ /// Add all the push-constant ranges implied by this root object and sub-objects
+ Result addAllPushConstantRanges();
+
+ /// Recurisvely add push-constant ranges defined by `layout` and sub-objects
+ Result addAllPushConstantRangesRec(ShaderObjectLayoutImpl* layout);
+
+ /// Recurisvely add push-constant ranges defined by sub-objects of `layout`
+ Result addChildPushConstantRangesRec(ShaderObjectLayoutImpl* layout);
+
+public:
+ ComPtr<slang::IComponentType> m_program;
+ slang::ProgramLayout* m_programLayout = nullptr;
+ List<EntryPointInfo> m_entryPoints;
+ VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
+ Array<VkDescriptorSetLayout, kMaxDescriptorSets> m_vkDescriptorSetLayouts;
+ List<VkPushConstantRange> m_allPushConstantRanges;
+ uint32_t m_totalPushConstantSize = 0;
+
+ SimpleBindingOffset m_pendingDataOffset;
+ DeviceImpl* m_renderer = nullptr;
+};
+
+class ShaderProgramImpl : public ShaderProgramBase
+{
+public:
+ ShaderProgramImpl(DeviceImpl* device);
+
+ ~ShaderProgramImpl();
+
+ virtual void comFree() override;
+
+ BreakableReference<DeviceImpl> m_device;
+
+ Array<VkPipelineShaderStageCreateInfo, 8> m_stageCreateInfos;
+ Array<String, 8> m_entryPointNames;
+ Array<ComPtr<ISlangBlob>, 8> m_codeBlobs; //< To keep storage of code in scope
+ Array<VkShaderModule, 8> m_modules;
+ RefPtr<RootShaderObjectLayout> m_rootObjectLayout;
+
+ VkPipelineShaderStageCreateInfo compileEntryPoint(
+ const char* entryPointName,
+ ISlangBlob* code,
+ VkShaderStageFlagBits stage,
+ VkShaderModule& outShaderModule);
+
+ virtual Result createShaderModule(
+ slang::EntryPointReflection* entryPointInfo, ComPtr<ISlangBlob> kernelCode) override;
+};
+
+class CommandBufferImpl;
+
+class PipelineCommandEncoder : public RefObject
+{
+public:
+ CommandBufferImpl* m_commandBuffer;
+ VkCommandBuffer m_vkCommandBuffer;
+ VkCommandBuffer m_vkPreCommandBuffer = VK_NULL_HANDLE;
+ VkPipeline m_boundPipelines[3] = {};
+ DeviceImpl* m_device = nullptr;
+ RefPtr<PipelineStateImpl> m_currentPipeline;
+
+ VulkanApi* m_api;
+
+ static int getBindPointIndex(VkPipelineBindPoint bindPoint);
+
+ void init(CommandBufferImpl* commandBuffer);
+
+ void endEncodingImpl();
+
+ static void _uploadBufferData(
+ VkCommandBuffer commandBuffer,
+ TransientResourceHeapImpl* transientHeap,
+ BufferResourceImpl* buffer,
+ size_t offset,
+ size_t size,
+ void* data);
+
+ void uploadBufferDataImpl(IBufferResource* buffer, size_t offset, size_t size, void* data);
+
+ Result bindRootShaderObjectImpl(VkPipelineBindPoint bindPoint);
+
+ Result setPipelineStateImpl(IPipelineState* state, IShaderObject** outRootObject);
+
+ Result setPipelineStateWithRootObjectImpl(IPipelineState* state, IShaderObject* inObject);
+
+ void bindRenderState(VkPipelineBindPoint pipelineBindPoint);
+};
+
+/// Context information required when binding shader objects to the pipeline
+struct RootBindingContext
+{
+ /// The pipeline layout being used for binding
+ VkPipelineLayout pipelineLayout;
+
+ /// An allocator to use for descriptor sets during binding
+ DescriptorSetAllocator* descriptorSetAllocator;
+
+ /// The dvice being used
+ DeviceImpl* device;
+
+ /// The descriptor sets that are being allocated and bound
+ VkDescriptorSet* descriptorSets;
+
+ /// Information about all the push-constant ranges that should be bound
+ ConstArrayView<VkPushConstantRange> pushConstantRanges;
+
+ uint32_t descriptorSetCounter = 0;
+};
+
+struct CombinedTextureSamplerSlot
+{
+ RefPtr<TextureResourceViewImpl> textureView;
+ RefPtr<SamplerStateImpl> sampler;
+ operator bool() { return textureView && sampler; }
+};
+
+class ShaderObjectImpl
+ : public ShaderObjectBaseImpl<ShaderObjectImpl, ShaderObjectLayoutImpl, SimpleShaderObjectData>
+{
+public:
+ static Result create(
+ IDevice* device, ShaderObjectLayoutImpl* layout, ShaderObjectImpl** outShaderObject);
+
+ RendererBase* getDevice();
+
+ virtual SLANG_NO_THROW UInt SLANG_MCALL getEntryPointCount() override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getEntryPoint(UInt index, IShaderObject** outEntryPoint) override;
+
+ virtual SLANG_NO_THROW const void* SLANG_MCALL getRawData() override;
+
+ virtual SLANG_NO_THROW size_t SLANG_MCALL getSize() override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ setData(ShaderOffset const& inOffset, void const* data, size_t inSize) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ setResource(ShaderOffset const& offset, IResourceView* resourceView) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ setSampler(ShaderOffset const& offset, ISamplerState* sampler) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL setCombinedTextureSampler(
+ ShaderOffset const& offset, IResourceView* textureView, ISamplerState* sampler) override;
+
+protected:
+ friend class RootShaderObjectLayout;
+
+ Result init(IDevice* device, ShaderObjectLayoutImpl* layout);
+
+ /// Write the uniform/ordinary data of this object into the given `dest` buffer at the given
+ /// `offset`
+ Result _writeOrdinaryData(
+ PipelineCommandEncoder* encoder,
+ IBufferResource* buffer,
+ size_t offset,
+ size_t destSize,
+ ShaderObjectLayoutImpl* specializedLayout);
+
+public:
+ /// Write a single desriptor using the Vulkan API
+ static void writeDescriptor(RootBindingContext& context, VkWriteDescriptorSet const& write);
+
+ static void writeBufferDescriptor(
+ RootBindingContext& context,
+ BindingOffset const& offset,
+ VkDescriptorType descriptorType,
+ BufferResourceImpl* buffer,
+ size_t bufferOffset,
+ size_t bufferSize);
+
+ static void writeBufferDescriptor(
+ RootBindingContext& context,
+ BindingOffset const& offset,
+ VkDescriptorType descriptorType,
+ BufferResourceImpl* buffer);
+
+ static void writePlainBufferDescriptor(
+ RootBindingContext& context,
+ BindingOffset const& offset,
+ VkDescriptorType descriptorType,
+ ArrayView<RefPtr<ResourceViewInternalBase>> resourceViews);
+
+ static void writeTexelBufferDescriptor(
+ RootBindingContext& context,
+ BindingOffset const& offset,
+ VkDescriptorType descriptorType,
+ ArrayView<RefPtr<ResourceViewInternalBase>> resourceViews);
+
+ static void writeTextureSamplerDescriptor(
+ RootBindingContext& context,
+ BindingOffset const& offset,
+ VkDescriptorType descriptorType,
+ ArrayView<CombinedTextureSamplerSlot> slots);
+
+ static void writeAccelerationStructureDescriptor(
+ RootBindingContext& context,
+ BindingOffset const& offset,
+ VkDescriptorType descriptorType,
+ ArrayView<RefPtr<ResourceViewInternalBase>> resourceViews);
+
+ static void writeTextureDescriptor(
+ RootBindingContext& context,
+ BindingOffset const& offset,
+ VkDescriptorType descriptorType,
+ ArrayView<RefPtr<ResourceViewInternalBase>> resourceViews);
+
+ static void writeSamplerDescriptor(
+ RootBindingContext& context,
+ BindingOffset const& offset,
+ VkDescriptorType descriptorType,
+ ArrayView<RefPtr<SamplerStateImpl>> samplers);
+
+ bool shouldAllocateConstantBuffer(TransientResourceHeapImpl* transientHeap);
+
+ /// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed
+ Result _ensureOrdinaryDataBufferCreatedIfNeeded(
+ PipelineCommandEncoder* encoder, ShaderObjectLayoutImpl* specializedLayout);
+
+public:
+ /// Bind this shader object as a "value"
+ ///
+ /// This is the mode used for binding sub-objects for existential-type
+ /// fields, and is also used as part of the implementation of the
+ /// parameter-block and constant-buffer cases.
+ ///
+ Result bindAsValue(
+ PipelineCommandEncoder* encoder,
+ RootBindingContext& context,
+ BindingOffset const& offset,
+ ShaderObjectLayoutImpl* specializedLayout);
+
+ /// Allocate the descriptor sets needed for binding this object (but not nested parameter
+ /// blocks)
+ Result allocateDescriptorSets(
+ PipelineCommandEncoder* encoder,
+ RootBindingContext& context,
+ BindingOffset const& offset,
+ ShaderObjectLayoutImpl* specializedLayout);
+
+ /// Bind this object as a `ParameterBlock<X>`.
+ Result bindAsParameterBlock(
+ PipelineCommandEncoder* encoder,
+ RootBindingContext& context,
+ BindingOffset const& inOffset,
+ ShaderObjectLayoutImpl* specializedLayout);
+
+ /// Bind the ordinary data buffer if needed.
+ Result bindOrdinaryDataBufferIfNeeded(
+ PipelineCommandEncoder* encoder,
+ RootBindingContext& context,
+ BindingOffset& ioOffset,
+ ShaderObjectLayoutImpl* specializedLayout);
+
+ /// Bind this object as a `ConstantBuffer<X>`.
+ Result bindAsConstantBuffer(
+ PipelineCommandEncoder* encoder,
+ RootBindingContext& context,
+ BindingOffset const& inOffset,
+ ShaderObjectLayoutImpl* specializedLayout);
+
+ List<RefPtr<ResourceViewInternalBase>> m_resourceViews;
+
+ List<RefPtr<SamplerStateImpl>> m_samplers;
+
+ List<CombinedTextureSamplerSlot> m_combinedTextureSamplers;
+
+ // The transient constant buffer that holds the GPU copy of the constant data,
+ // weak referenced.
+ IBufferResource* m_constantBuffer = nullptr;
+ // The offset into the transient constant buffer where the constant data starts.
+ size_t m_constantBufferOffset = 0;
+ size_t m_constantBufferSize = 0;
+
+ /// Dirty bit tracking whether the constant buffer needs to be updated.
+ bool m_isConstantBufferDirty = true;
+ /// The transient heap from which the constant buffer is allocated.
+ TransientResourceHeapImpl* m_constantBufferTransientHeap;
+ /// The version of the transient heap when the constant buffer is allocated.
+ uint64_t m_constantBufferTransientHeapVersion;
+
+ /// Get the layout of this shader object with specialization arguments considered
+ ///
+ /// This operation should only be called after the shader object has been
+ /// fully filled in and finalized.
+ ///
+ Result _getSpecializedLayout(ShaderObjectLayoutImpl** outLayout);
+
+ /// Create the layout for this shader object with specialization arguments considered
+ ///
+ /// This operation is virtual so that it can be customized by `ProgramVars`.
+ ///
+ virtual Result _createSpecializedLayout(ShaderObjectLayoutImpl** outLayout);
+
+ RefPtr<ShaderObjectLayoutImpl> m_specializedLayout;
+};
+
+class MutableShaderObjectImpl
+ : public MutableShaderObject<MutableShaderObjectImpl, ShaderObjectLayoutImpl>
+{};
+
+class EntryPointShaderObject : public ShaderObjectImpl
+{
+ typedef ShaderObjectImpl Super;
+
+public:
+ static Result create(
+ IDevice* device, EntryPointLayout* layout, EntryPointShaderObject** outShaderObject);
+
+ EntryPointLayout* getLayout();
+
+ /// Bind this shader object as an entry point
+ Result bindAsEntryPoint(
+ PipelineCommandEncoder* encoder,
+ RootBindingContext& context,
+ BindingOffset const& inOffset,
+ EntryPointLayout* layout);
+
+protected:
+ Result init(IDevice* device, EntryPointLayout* layout);
+};
+
+class RootShaderObjectImpl : public ShaderObjectImpl
+{
+ using Super = ShaderObjectImpl;
+
+public:
+ // Override default reference counting behavior to disable lifetime management.
+ // Root objects are managed by command buffer and does not need to be freed by the user.
+ virtual SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return 1; }
+ virtual SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return 1; }
+
+public:
+ RootShaderObjectLayout* getLayout();
+
+ RootShaderObjectLayout* getSpecializedLayout();
+
+ List<RefPtr<EntryPointShaderObject>> const& getEntryPoints() const;
+
+ virtual UInt SLANG_MCALL getEntryPointCount() override;
+ virtual Result SLANG_MCALL getEntryPoint(UInt index, IShaderObject** outEntryPoint) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ copyFrom(IShaderObject* object, ITransientResourceHeap* transientHeap) override;
+
+ /// Bind this object as a root shader object
+ Result bindAsRoot(
+ PipelineCommandEncoder* encoder,
+ RootBindingContext& context,
+ RootShaderObjectLayout* layout);
+
+ virtual Result collectSpecializationArgs(ExtendedShaderObjectTypeList& args) override;
+
+public:
+ Result init(IDevice* device, RootShaderObjectLayout* layout);
+
+protected:
+ virtual Result _createSpecializedLayout(ShaderObjectLayoutImpl** outLayout) override;
+
+ List<RefPtr<EntryPointShaderObject>> m_entryPoints;
+};
+
+class ShaderTableImpl : public ShaderTableBase
+{
+public:
+ uint32_t m_raygenTableSize;
+ uint32_t m_missTableSize;
+ uint32_t m_hitTableSize;
+ uint32_t m_callableTableSize;
+
+ DeviceImpl* m_device;
+
+ virtual RefPtr<BufferResource> createDeviceBuffer(
+ PipelineStateBase* pipeline,
+ TransientResourceHeapBase* transientHeap,
+ IResourceCommandEncoder* encoder) override;
+};
+
+class ResourceCommandEncoder
+ : public IResourceCommandEncoder
+ , public PipelineCommandEncoder
+{
+public:
+ virtual SLANG_NO_THROW void SLANG_MCALL copyBuffer(
+ IBufferResource* dst,
+ size_t dstOffset,
+ IBufferResource* src,
+ size_t srcOffset,
+ size_t size) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ uploadBufferData(IBufferResource* buffer, size_t offset, size_t size, void* data) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL textureBarrier(
+ size_t count,
+ ITextureResource* const* textures,
+ ResourceState src,
+ ResourceState dst) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL bufferBarrier(
+ size_t count,
+ IBufferResource* const* buffers,
+ ResourceState src,
+ ResourceState dst) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL endEncoding() override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ writeTimestamp(IQueryPool* queryPool, SlangInt index) override;
+
+ VkImageAspectFlags getAspectMask(TextureAspect aspect);
+
+ virtual SLANG_NO_THROW void SLANG_MCALL copyTexture(
+ ITextureResource* dst,
+ ResourceState dstState,
+ SubresourceRange dstSubresource,
+ ITextureResource::Offset3D dstOffset,
+ ITextureResource* src,
+ ResourceState srcState,
+ SubresourceRange srcSubresource,
+ ITextureResource::Offset3D srcOffset,
+ ITextureResource::Size extent) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL uploadTextureData(
+ ITextureResource* dst,
+ SubresourceRange subResourceRange,
+ ITextureResource::Offset3D offset,
+ ITextureResource::Size extend,
+ ITextureResource::SubresourceData* subResourceData,
+ size_t subResourceDataCount) override;
+
+ void _clearColorImage(TextureResourceViewImpl* viewImpl, ClearValue* clearValue);
+
+ void _clearDepthImage(
+ TextureResourceViewImpl* viewImpl,
+ ClearValue* clearValue,
+ ClearResourceViewFlags::Enum flags);
+
+ void _clearBuffer(
+ VkBuffer buffer, uint64_t bufferSize, const IResourceView::Desc& desc, uint32_t clearValue);
+
+ virtual SLANG_NO_THROW void SLANG_MCALL clearResourceView(
+ IResourceView* view, ClearValue* clearValue, ClearResourceViewFlags::Enum flags) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL resolveResource(
+ ITextureResource* source,
+ ResourceState sourceState,
+ SubresourceRange sourceRange,
+ ITextureResource* dest,
+ ResourceState destState,
+ SubresourceRange destRange) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL resolveQuery(
+ IQueryPool* queryPool,
+ uint32_t index,
+ uint32_t count,
+ IBufferResource* buffer,
+ uint64_t offset) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL copyTextureToBuffer(
+ IBufferResource* dst,
+ size_t dstOffset,
+ size_t dstSize,
+ size_t dstRowStride,
+ ITextureResource* src,
+ ResourceState srcState,
+ SubresourceRange srcSubresource,
+ ITextureResource::Offset3D srcOffset,
+ ITextureResource::Size extent) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL textureSubresourceBarrier(
+ ITextureResource* texture,
+ SubresourceRange subresourceRange,
+ ResourceState src,
+ ResourceState dst) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ beginDebugEvent(const char* name, float rgbColor[3]) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL endDebugEvent() override;
+};
+
+class RenderCommandEncoder
+ : public IRenderCommandEncoder
+ , public ResourceCommandEncoder
+{
+public:
+ SLANG_GFX_FORWARD_RESOURCE_COMMAND_ENCODER_IMPL(ResourceCommandEncoder)
+public:
+ List<VkViewport> m_viewports;
+ List<VkRect2D> m_scissorRects;
+
+public:
+ void beginPass(IRenderPassLayout* renderPass, IFramebuffer* framebuffer);
+
+ virtual SLANG_NO_THROW void SLANG_MCALL endEncoding() override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ bindPipeline(IPipelineState* pipelineState, IShaderObject** outRootObject) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL bindPipelineWithRootObject(
+ IPipelineState* pipelineState, IShaderObject* rootObject) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setViewports(uint32_t count, const Viewport* viewports) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setScissorRects(uint32_t count, const ScissorRect* rects) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setPrimitiveTopology(PrimitiveTopology topology) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL setVertexBuffers(
+ uint32_t startSlot,
+ uint32_t slotCount,
+ IBufferResource* const* buffers,
+ const uint32_t* offsets) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setIndexBuffer(IBufferResource* buffer, Format indexFormat, uint32_t offset = 0) override;
+
+ void prepareDraw();
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ draw(uint32_t vertexCount, uint32_t startVertex = 0) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ drawIndexed(uint32_t indexCount, uint32_t startIndex = 0, uint32_t baseVertex = 0) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL setStencilReference(uint32_t referenceValue) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL drawIndirect(
+ uint32_t maxDrawCount,
+ IBufferResource* argBuffer,
+ uint64_t argOffset,
+ IBufferResource* countBuffer,
+ uint64_t countOffset) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL drawIndexedIndirect(
+ uint32_t maxDrawCount,
+ IBufferResource* argBuffer,
+ uint64_t argOffset,
+ IBufferResource* countBuffer,
+ uint64_t countOffset) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL setSamplePositions(
+ uint32_t samplesPerPixel,
+ uint32_t pixelCount,
+ const SamplePosition* samplePositions) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL drawInstanced(
+ uint32_t vertexCount,
+ uint32_t instanceCount,
+ uint32_t startVertex,
+ uint32_t startInstanceLocation) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL drawIndexedInstanced(
+ uint32_t indexCount,
+ uint32_t instanceCount,
+ uint32_t startIndexLocation,
+ int32_t baseVertexLocation,
+ uint32_t startInstanceLocation) override;
+};
+
+class ComputeCommandEncoder
+ : public IComputeCommandEncoder
+ , public ResourceCommandEncoder
+{
+public:
+ SLANG_GFX_FORWARD_RESOURCE_COMMAND_ENCODER_IMPL(ResourceCommandEncoder)
+public:
+ virtual SLANG_NO_THROW void SLANG_MCALL endEncoding() override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ bindPipeline(IPipelineState* pipelineState, IShaderObject** outRootObject) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL bindPipelineWithRootObject(
+ IPipelineState* pipelineState, IShaderObject* rootObject) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL dispatchCompute(int x, int y, int z) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ dispatchComputeIndirect(IBufferResource* argBuffer, uint64_t offset) override;
+};
+
+class RayTracingCommandEncoder
+ : public IRayTracingCommandEncoder
+ , public ResourceCommandEncoder
+{
+public:
+ SLANG_GFX_FORWARD_RESOURCE_COMMAND_ENCODER_IMPL(ResourceCommandEncoder)
+public:
+ void _memoryBarrier(
+ int count,
+ IAccelerationStructure* const* structures,
+ AccessFlag srcAccess,
+ AccessFlag destAccess);
+
+ void _queryAccelerationStructureProperties(
+ int accelerationStructureCount,
+ IAccelerationStructure* const* accelerationStructures,
+ int queryCount,
+ AccelerationStructureQueryDesc* queryDescs);
+
+ virtual SLANG_NO_THROW void SLANG_MCALL buildAccelerationStructure(
+ const IAccelerationStructure::BuildDesc& desc,
+ int propertyQueryCount,
+ AccelerationStructureQueryDesc* queryDescs) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL copyAccelerationStructure(
+ IAccelerationStructure* dest,
+ IAccelerationStructure* src,
+ AccelerationStructureCopyMode mode) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL queryAccelerationStructureProperties(
+ int accelerationStructureCount,
+ IAccelerationStructure* const* accelerationStructures,
+ int queryCount,
+ AccelerationStructureQueryDesc* queryDescs) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ serializeAccelerationStructure(DeviceAddress dest, IAccelerationStructure* source) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL deserializeAccelerationStructure(
+ IAccelerationStructure* dest, DeviceAddress source) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ bindPipeline(IPipelineState* pipeline, IShaderObject** outRootObject) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL bindPipelineWithRootObject(
+ IPipelineState* pipelineState, IShaderObject* rootObject) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL dispatchRays(
+ uint32_t raygenShaderIndex,
+ IShaderTable* shaderTable,
+ int32_t width,
+ int32_t height,
+ int32_t depth) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL endEncoding() override;
+};
+
+class CommandBufferImpl
+ : public ICommandBuffer
+ , public ComObject
+{
+public:
+ // There are a pair of cyclic references between a `TransientResourceHeap` and
+ // a `CommandBuffer` created from the heap. We need to break the cycle when
+ // the public reference count of a command buffer drops to 0.
+ SLANG_COM_OBJECT_IUNKNOWN_ALL
+ ICommandBuffer* getInterface(const Guid& guid);
+ virtual void comFree() override;
+
+public:
+ VkCommandBuffer m_commandBuffer;
+ VkCommandBuffer m_preCommandBuffer = VK_NULL_HANDLE;
+ VkCommandPool m_pool;
+ DeviceImpl* m_renderer;
+ BreakableReference<TransientResourceHeapImpl> m_transientHeap;
+ bool m_isPreCommandBufferEmpty = true;
+ RootShaderObjectImpl m_rootObject;
+
+ RefPtr<ResourceCommandEncoder> m_resourceCommandEncoder;
+ RefPtr<ComputeCommandEncoder> m_computeCommandEncoder;
+ RefPtr<RenderCommandEncoder> m_renderCommandEncoder;
+ RefPtr<RayTracingCommandEncoder> m_rayTracingCommandEncoder;
+
+ // Command buffers are deallocated by its command pool,
+ // so no need to free individually.
+ ~CommandBufferImpl() = default;
+
+ Result init(DeviceImpl* renderer, VkCommandPool pool, TransientResourceHeapImpl* transientHeap);
+
+ void beginCommandBuffer();
+
+ Result createPreCommandBuffer();
+
+ VkCommandBuffer getPreCommandBuffer();
+
+public:
+ virtual SLANG_NO_THROW void SLANG_MCALL encodeRenderCommands(
+ IRenderPassLayout* renderPass,
+ IFramebuffer* framebuffer,
+ IRenderCommandEncoder** outEncoder) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ encodeComputeCommands(IComputeCommandEncoder** outEncoder) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ encodeResourceCommands(IResourceCommandEncoder** outEncoder) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ encodeRayTracingCommands(IRayTracingCommandEncoder** outEncoder) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL close() override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
+};
+
+class CommandQueueImpl
+ : public ICommandQueue
+ , public ComObject
+{
+public:
+ SLANG_COM_OBJECT_IUNKNOWN_ALL
+ ICommandQueue* getInterface(const Guid& guid);
+
+public:
+ Desc m_desc;
+ RefPtr<DeviceImpl> m_renderer;
+ VkQueue m_queue;
+ uint32_t m_queueFamilyIndex;
+ struct FenceWaitInfo
+ {
+ RefPtr<FenceImpl> fence;
+ uint64_t waitValue;
+ };
+ List<FenceWaitInfo> m_pendingWaitFences;
+ VkSemaphore m_pendingWaitSemaphores[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE};
+ List<VkCommandBuffer> m_submitCommandBuffers;
+ VkSemaphore m_semaphore;
+ ~CommandQueueImpl();
+
+ void init(DeviceImpl* renderer, VkQueue queue, uint32_t queueFamilyIndex);
+
+ virtual SLANG_NO_THROW void SLANG_MCALL waitOnHost() override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
+
+ virtual SLANG_NO_THROW const Desc& SLANG_MCALL getDesc() override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL waitForFenceValuesOnDevice(
+ uint32_t fenceCount, IFence** fences, uint64_t* waitValues) override;
+
+ void queueSubmitImpl(
+ uint32_t count,
+ ICommandBuffer* const* commandBuffers,
+ IFence* fence,
+ uint64_t valueToSignal);
+
+ virtual SLANG_NO_THROW void SLANG_MCALL executeCommandBuffers(
+ uint32_t count,
+ ICommandBuffer* const* commandBuffers,
+ IFence* fence,
+ uint64_t valueToSignal) override;
+};
+
+class TransientResourceHeapImpl
+ : public TransientResourceHeapBaseImpl<DeviceImpl, BufferResourceImpl>
+{
+private:
+ typedef TransientResourceHeapBaseImpl<DeviceImpl, BufferResourceImpl> Super;
+
+public:
+ VkCommandPool m_commandPool;
+ DescriptorSetAllocator m_descSetAllocator;
+ List<VkFence> m_fences;
+ Index m_fenceIndex = -1;
+ List<RefPtr<CommandBufferImpl>> m_commandBufferPool;
+ uint32_t m_commandBufferAllocId = 0;
+ VkFence getCurrentFence() { return m_fences[m_fenceIndex]; }
+ void advanceFence();
+
+ Result init(const ITransientResourceHeap::Desc& desc, DeviceImpl* device);
+ ~TransientResourceHeapImpl();
+
+public:
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createCommandBuffer(ICommandBuffer** outCommandBuffer) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL synchronizeAndReset() override;
+};
+
+class QueryPoolImpl : public QueryPoolBase
+{
+public:
+ Result init(const IQueryPool::Desc& desc, DeviceImpl* device);
+ ~QueryPoolImpl();
+
+public:
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getResult(SlangInt index, SlangInt count, uint64_t* data) override;
+
+public:
+ VkQueryPool m_pool;
+ RefPtr<DeviceImpl> m_device;
+};
+
+class SwapchainImpl
+ : public ISwapchain
+ , public ComObject
+{
+public:
+ SLANG_COM_OBJECT_IUNKNOWN_ALL
+ ISwapchain* getInterface(const Guid& guid);
+
+public:
+ struct PlatformDesc
+ {};
+
+#if SLANG_WINDOWS_FAMILY
+ struct WinPlatformDesc : public PlatformDesc
+ {
+ HINSTANCE m_hinstance;
+ HWND m_hwnd;
+ };
+#else
+ struct XPlatformDesc : public PlatformDesc
+ {
+ Display* m_display;
+ Window m_window;
+ };
+#endif
+public:
+ VkSwapchainKHR m_swapChain;
+ VkSurfaceKHR m_surface;
+ VkSemaphore m_nextImageSemaphore; // Semaphore to signal after `acquireNextImage`.
+ ISwapchain::Desc m_desc;
+ VkFormat m_vkformat;
+ RefPtr<CommandQueueImpl> m_queue;
+ ShortList<RefPtr<TextureResourceImpl>> m_images;
+ RefPtr<DeviceImpl> m_renderer;
+ VulkanApi* m_api;
+ uint32_t m_currentImageIndex = 0;
+ WindowHandle m_windowHandle;
+ void destroySwapchainAndImages();
+
+ void getWindowSize(int* widthOut, int* heightOut) const;
+
+ Result createSwapchainAndImages();
+
+public:
+ ~SwapchainImpl();
+
+ static Index _indexOfFormat(List<VkSurfaceFormatKHR>& formatsIn, VkFormat format);
+
+ Result init(DeviceImpl* renderer, const ISwapchain::Desc& desc, WindowHandle window);
+
+ virtual SLANG_NO_THROW const Desc& SLANG_MCALL getDesc() override { return m_desc; }
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getImage(uint32_t index, ITextureResource** outResource) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL resize(uint32_t width, uint32_t height) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL present() override;
+ virtual SLANG_NO_THROW int SLANG_MCALL acquireNextImage() override;
+ virtual SLANG_NO_THROW bool SLANG_MCALL isOccluded() override { return false; }
+ virtual SLANG_NO_THROW Result SLANG_MCALL setFullScreenMode(bool mode) override;
+};
+
+} // namespace vk
+} // namespace gfx