summaryrefslogtreecommitdiffstats
path: root/tools/gfx/vulkan/render-vk.cpp
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2019-09-13 15:59:15 -0400
committerGitHub <noreply@github.com>2019-09-13 15:59:15 -0400
commitc2e5d2468ad6a38cdb8a067da0678302f6cc6066 (patch)
tree97c448d28e54068d84c422e9f172996b7a95f1ed /tools/gfx/vulkan/render-vk.cpp
parent0b6321b3f08c48e37e6b8256d420f05d9727fb5a (diff)
Refactor render-test to make cross platform (#1053)
* First pass of render-test refactor. * Make window construction a function that can choose an implementation. * Remove OpenGL as currently has windows dependency. * Disable Vulkan as Renderer impl has dependency on windows. * Pass Window in as parameter of 'update'. * Add win-window.cpp as was missing. * Fix warning on windows about signs during comparison.
Diffstat (limited to 'tools/gfx/vulkan/render-vk.cpp')
-rw-r--r--tools/gfx/vulkan/render-vk.cpp2793
1 files changed, 2793 insertions, 0 deletions
diff --git a/tools/gfx/vulkan/render-vk.cpp b/tools/gfx/vulkan/render-vk.cpp
new file mode 100644
index 000000000..2221187ed
--- /dev/null
+++ b/tools/gfx/vulkan/render-vk.cpp
@@ -0,0 +1,2793 @@
+// render-vk.cpp
+#include "render-vk.h"
+
+//WORKING:#include "options.h"
+#include "../render.h"
+
+#include "../../source/core/slang-smart-pointer.h"
+
+#include "vk-api.h"
+#include "vk-util.h"
+#include "vk-device-queue.h"
+#include "vk-swap-chain.h"
+
+#include "../surface.h"
+
+// Vulkan has a different coordinate system to ogl
+// http://anki3d.org/vulkan-coordinate-system/
+
+#define ENABLE_VALIDATION_LAYER 1
+
+#ifdef _MSC_VER
+# include <stddef.h>
+# pragma warning(disable: 4996)
+# if (_MSC_VER < 1900)
+# define snprintf sprintf_s
+# endif
+#endif
+
+namespace gfx {
+using namespace Slang;
+
+class VKRenderer : public Renderer
+{
+public:
+ enum
+ {
+ kMaxRenderTargets = 8,
+ kMaxAttachments = kMaxRenderTargets + 1,
+
+ kMaxDescriptorSets = 4,
+ };
+
+ // Renderer implementation
+ virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override;
+ virtual const List<String>& getFeatures() override { return m_features; }
+ virtual void setClearColor(const float color[4]) override;
+ virtual void clearFrame() override;
+ virtual void presentFrame() override;
+ TextureResource::Desc getSwapChainTextureDesc() override;
+
+ Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) override;
+ Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) override;
+ Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) override;
+
+ Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) override;
+ Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) override;
+
+ Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) override;
+
+ Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) override;
+ Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) override;
+ Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) override;
+
+ Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) override;
+ Result createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) override;
+ Result createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) override;
+
+ virtual SlangResult captureScreenSurface(Surface& surface) override;
+
+ virtual void* map(BufferResource* buffer, MapFlavor flavor) override;
+ virtual void unmap(BufferResource* buffer) override;
+ virtual void setPrimitiveTopology(PrimitiveTopology topology) override;
+
+ virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) override;
+
+ virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override;
+ virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override;
+ virtual void setDepthStencilTarget(ResourceView* depthStencilView) override;
+ void setViewports(UInt count, Viewport const* viewports) override;
+ void setScissorRects(UInt count, ScissorRect const* rects) override;
+ virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override;
+ virtual void draw(UInt vertexCount, UInt startVertex) override;
+ virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override;
+ virtual void dispatchCompute(int x, int y, int z) override;
+ virtual void submitGpuWork() override;
+ virtual void waitForGpu() override;
+ virtual RendererType getRendererType() const override { return RendererType::Vulkan; }
+
+ /// Dtor
+ ~VKRenderer();
+
+ protected:
+
+ class Buffer
+ {
+ public:
+ /// Initialize a buffer with specified size, and memory props
+ Result init(const VulkanApi& api, size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties);
+
+ /// Returns true if has been initialized
+ bool isInitialized() const { return m_api != nullptr; }
+
+ // Default Ctor
+ Buffer():
+ m_api(nullptr)
+ {}
+
+ /// Dtor
+ ~Buffer()
+ {
+ 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 InputLayout
+ {
+ public:
+ List<VkVertexInputAttributeDescription> m_vertexDescs;
+ int m_vertexSize;
+ };
+
+ class BufferResourceImpl: public BufferResource
+ {
+ public:
+ typedef BufferResource Parent;
+
+ BufferResourceImpl(Resource::Usage initialUsage, const BufferResource::Desc& desc, VKRenderer* renderer):
+ Parent(desc),
+ m_renderer(renderer),
+ m_initialUsage(initialUsage)
+ {
+ assert(renderer);
+ }
+
+ Resource::Usage m_initialUsage;
+ VKRenderer* m_renderer;
+ Buffer m_buffer;
+ Buffer m_uploadBuffer;
+ List<uint8_t> m_readBuffer; ///< Stores the contents when a map read is performed
+
+ MapFlavor m_mapFlavor = MapFlavor::Unknown; ///< If resource is mapped, records what kind of mapping else Unknown (if not mapped)
+ };
+
+ class TextureResourceImpl : public TextureResource
+ {
+ public:
+ typedef TextureResource Parent;
+
+ TextureResourceImpl(const Desc& desc, Usage initialUsage, const VulkanApi* api) :
+ Parent(desc),
+ m_initialUsage(initialUsage),
+ m_api(api)
+ {
+ }
+ ~TextureResourceImpl()
+ {
+ if (m_api)
+ {
+ if (m_imageMemory != VK_NULL_HANDLE)
+ {
+ m_api->vkFreeMemory(m_api->m_device, m_imageMemory, nullptr);
+ }
+ if (m_image != VK_NULL_HANDLE)
+ {
+ m_api->vkDestroyImage(m_api->m_device, m_image, nullptr);
+ }
+ }
+ }
+
+ Usage m_initialUsage;
+
+ VkImage m_image = VK_NULL_HANDLE;
+ VkDeviceMemory m_imageMemory = VK_NULL_HANDLE;
+
+ const VulkanApi* m_api;
+ };
+
+ class SamplerStateImpl : public SamplerState
+ {
+ public:
+ VkSampler m_sampler;
+ };
+
+ class ResourceViewImpl : public ResourceView
+ {
+ public:
+ enum class ViewType
+ {
+ Texture,
+ TexelBuffer,
+ PlainBuffer,
+ };
+ ViewType m_type;
+ };
+
+ class TextureResourceViewImpl : public ResourceViewImpl
+ {
+ public:
+ TextureResourceViewImpl()
+ {
+ m_type = ViewType::Texture;
+ }
+
+ RefPtr<TextureResourceImpl> m_texture;
+ VkImageView m_view;
+ VkImageLayout m_layout;
+ };
+
+ class TexelBufferResourceViewImpl : public ResourceViewImpl
+ {
+ public:
+ TexelBufferResourceViewImpl()
+ {
+ m_type = ViewType::TexelBuffer;
+ }
+
+ RefPtr<BufferResourceImpl> m_buffer;
+ VkBufferView m_view;
+ };
+
+ class PlainBufferResourceViewImpl : public ResourceViewImpl
+ {
+ public:
+ PlainBufferResourceViewImpl()
+ {
+ m_type = ViewType::PlainBuffer;
+ }
+
+ RefPtr<BufferResourceImpl> m_buffer;
+ VkDeviceSize offset;
+ VkDeviceSize size;
+ };
+
+ class ShaderProgramImpl: public ShaderProgram
+ {
+ public:
+
+ ShaderProgramImpl(PipelineType pipelineType):
+ m_pipelineType(pipelineType)
+ {}
+
+ PipelineType m_pipelineType;
+
+ VkPipelineShaderStageCreateInfo m_compute;
+ VkPipelineShaderStageCreateInfo m_vertex;
+ VkPipelineShaderStageCreateInfo m_fragment;
+
+ List<char> m_buffers[2]; //< To keep storage of code in scope
+ };
+
+ class DescriptorSetLayoutImpl : public DescriptorSetLayout
+ {
+ public:
+ DescriptorSetLayoutImpl(const VulkanApi& api)
+ : m_api(&api)
+ {
+ }
+
+ ~DescriptorSetLayoutImpl()
+ {
+ if(m_descriptorSetLayout != VK_NULL_HANDLE)
+ {
+ m_api->vkDestroyDescriptorSetLayout(m_api->m_device, m_descriptorSetLayout, nullptr);
+ }
+ if (m_descriptorPool != VK_NULL_HANDLE)
+ {
+ m_api->vkDestroyDescriptorPool(m_api->m_device, m_descriptorPool, nullptr);
+ }
+ }
+
+ VulkanApi const* m_api;
+ VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE;
+ VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE;
+
+ struct RangeInfo
+ {
+ VkDescriptorType descriptorType;
+ };
+ List<RangeInfo> m_ranges;
+ };
+
+ class PipelineLayoutImpl : public PipelineLayout
+ {
+ public:
+ PipelineLayoutImpl(const VulkanApi& api)
+ : m_api(&api)
+ {
+ }
+
+ ~PipelineLayoutImpl()
+ {
+ if (m_pipelineLayout != VK_NULL_HANDLE)
+ {
+ m_api->vkDestroyPipelineLayout(m_api->m_device, m_pipelineLayout, nullptr);
+ }
+ }
+
+ VulkanApi const* m_api;
+ VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
+ UInt m_descriptorSetCount = 0;
+ };
+
+ class DescriptorSetImpl : public DescriptorSet
+ {
+ public:
+ // Record the view binding
+ struct Binding
+ {
+ enum class Type : uint8_t
+ {
+ Unknown,
+ ResourceView,
+ SamplerState,
+ BufferResource,
+ CountOf,
+ };
+ Type type;
+ uint32_t range;
+ uint32_t index;
+ RefPtr<RefObject> obj;
+ };
+
+ DescriptorSetImpl(VKRenderer* renderer)
+ : m_renderer(renderer)
+ {
+ }
+
+ ~DescriptorSetImpl()
+ {
+ }
+
+ virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) override;
+ virtual void setResource(UInt range, UInt index, ResourceView* view) override;
+ virtual void setSampler(UInt range, UInt index, SamplerState* sampler) override;
+ virtual void setCombinedTextureSampler(
+ UInt range,
+ UInt index,
+ ResourceView* textureView,
+ SamplerState* sampler) override;
+
+ static Binding::Type _getBindingType(RefObject* ptr);
+ void _setBinding(Binding::Type type, UInt range, UInt index, RefObject* ptr);
+
+ VKRenderer* m_renderer = nullptr; ///< Weak pointer, can't be strong, because if set will become circular reference
+ RefPtr<DescriptorSetLayoutImpl> m_layout;
+ VkDescriptorSet m_descriptorSet = VK_NULL_HANDLE;
+
+ List<Binding> m_bindings; ///< Records entities are bound to this descriptor set, and keeps the associated resources/views/state in scope
+ };
+
+#if 0
+ struct BindingDetail
+ {
+ VkImageView m_srv = VK_NULL_HANDLE;
+ VkBufferView m_uav = VK_NULL_HANDLE;
+ VkSampler m_sampler = VK_NULL_HANDLE;
+ };
+
+ class BindingStateImpl: public BindingState
+ {
+ public:
+ typedef BindingState Parent;
+
+ BindingStateImpl(const Desc& desc, const VulkanApi* api):
+ Parent(desc),
+ m_api(api)
+ {
+ }
+ ~BindingStateImpl()
+ {
+ for (int i = 0; i < int(m_bindingDetails.Count()); ++i)
+ {
+ BindingDetail& detail = m_bindingDetails[i];
+ if (detail.m_sampler != VK_NULL_HANDLE)
+ {
+ m_api->vkDestroySampler(m_api->m_device, detail.m_sampler, nullptr);
+ }
+ if (detail.m_srv != VK_NULL_HANDLE)
+ {
+ m_api->vkDestroyImageView(m_api->m_device, detail.m_srv, nullptr);
+ }
+ if (detail.m_uav != VK_NULL_HANDLE)
+ {
+ m_api->vkDestroyBufferView(m_api->m_device, detail.m_uav, nullptr);
+ }
+ }
+ }
+
+ const VulkanApi* m_api;
+ List<BindingDetail> m_bindingDetails;
+ };
+#endif
+
+ struct BoundVertexBuffer
+ {
+ RefPtr<BufferResourceImpl> m_buffer;
+ int m_stride;
+ int m_offset;
+ };
+
+ class PipelineStateImpl : public PipelineState
+ {
+ public:
+ PipelineStateImpl(const VulkanApi& api):
+ m_api(&api)
+ {
+ }
+ ~PipelineStateImpl()
+ {
+ if (m_pipeline != VK_NULL_HANDLE)
+ {
+ m_api->vkDestroyPipeline(m_api->m_device, m_pipeline, nullptr);
+ }
+ }
+
+ const VulkanApi* m_api;
+
+// VkPrimitiveTopology m_primitiveTopology;
+
+ RefPtr<PipelineLayoutImpl> m_pipelineLayout;
+
+// RefPtr<InputLayoutImpl> m_inputLayout;
+ RefPtr<ShaderProgramImpl> m_shaderProgram;
+
+ VkPipeline m_pipeline = VK_NULL_HANDLE;
+ };
+
+ VkBool32 handleDebugMessage(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject,
+ size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg);
+
+ VkPipelineShaderStageCreateInfo compileEntryPoint(
+ ShaderProgram::KernelDesc const& kernelDesc,
+ VkShaderStageFlagBits stage,
+ List<char>& bufferOut);
+
+ 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);
+
+ /// Returns true if m_currentPipeline matches the current configuration
+// Pipeline* _getPipeline();
+// bool _isEqual(const Pipeline& pipeline) const;
+// Slang::Result _createPipeline(RefPtr<Pipeline>& pipelineOut);
+ void _beginRender();
+ void _endRender();
+
+ Slang::Result _beginPass();
+ void _endPass();
+ void _transitionImageLayout(VkImage image, VkFormat format, const TextureResource::Desc& desc, VkImageLayout oldLayout, VkImageLayout newLayout);
+
+ VkDebugReportCallbackEXT m_debugReportCallback;
+
+// RefPtr<InputLayoutImpl> m_currentInputLayout;
+
+// RefPtr<BindingStateImpl> m_currentBindingState;
+ RefPtr<PipelineLayoutImpl> m_currentPipelineLayout;
+
+ RefPtr<DescriptorSetImpl> m_currentDescriptorSetImpls [kMaxDescriptorSets];
+ VkDescriptorSet m_currentDescriptorSets [kMaxDescriptorSets];
+
+// RefPtr<ShaderProgramImpl> m_currentProgram;
+
+// List<RefPtr<Pipeline> > m_pipelineCache;
+ RefPtr<PipelineStateImpl> m_currentPipeline;
+
+ List<BoundVertexBuffer> m_boundVertexBuffers;
+
+ VkPrimitiveTopology m_primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+
+ VkDevice m_device = VK_NULL_HANDLE;
+
+ VulkanModule m_module;
+ VulkanApi m_api;
+
+ VulkanDeviceQueue m_deviceQueue;
+ VulkanSwapChain m_swapChain;
+
+ VkRenderPass m_renderPass = VK_NULL_HANDLE;
+
+ int m_swapChainImageIndex = -1;
+
+ float m_clearColor[4] = { 0, 0, 0, 0 };
+
+ Desc m_desc;
+ List<String> m_features;
+};
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VkRenderer::Buffer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+Result VKRenderer::Buffer::init(const VulkanApi& api, size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties)
+{
+ assert(!isInitialized());
+
+ m_api = &api;
+ m_memory = VK_NULL_HANDLE;
+ m_buffer = VK_NULL_HANDLE;
+
+ VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+ bufferCreateInfo.size = bufferSize;
+ bufferCreateInfo.usage = usage;
+
+ SLANG_VK_CHECK(api.vkCreateBuffer(api.m_device, &bufferCreateInfo, nullptr, &m_buffer));
+
+ VkMemoryRequirements memoryReqs = {};
+ api.vkGetBufferMemoryRequirements(api.m_device, m_buffer, &memoryReqs);
+
+ int memoryTypeIndex = api.findMemoryTypeIndex(memoryReqs.memoryTypeBits, reqMemoryProperties);
+ assert(memoryTypeIndex >= 0);
+
+ VkMemoryPropertyFlags actualMemoryProperites = api.m_deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags;
+
+ VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
+ allocateInfo.allocationSize = memoryReqs.size;
+ allocateInfo.memoryTypeIndex = memoryTypeIndex;
+
+ SLANG_VK_CHECK(api.vkAllocateMemory(api.m_device, &allocateInfo, nullptr, &m_memory));
+ SLANG_VK_CHECK(api.vkBindBufferMemory(api.m_device, m_buffer, m_memory, 0));
+
+ return SLANG_OK;
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VkRenderer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+#if 0
+bool VKRenderer::_isEqual(const Pipeline& pipeline) const
+{
+ return
+ pipeline.m_pipelineLayout == m_currentPipelineLayout &&
+ pipeline.m_primitiveTopology == m_primitiveTopology &&
+ pipeline.m_inputLayout == m_currentInputLayout &&
+ pipeline.m_shaderProgram == m_currentProgram;
+}
+
+VKRenderer::Pipeline* VKRenderer::_getPipeline()
+{
+ if (m_currentPipeline && _isEqual(*m_currentPipeline))
+ {
+ return m_currentPipeline;
+ }
+
+ // Look for a match in the cache
+ for (int i = 0; i < int(m_pipelineCache.Count()); ++i)
+ {
+ Pipeline* pipeline = m_pipelineCache[i];
+ if (_isEqual(*pipeline))
+ {
+ m_currentPipeline = pipeline;
+ return pipeline;
+ }
+ }
+
+ RefPtr<Pipeline> pipeline;
+ SLANG_RETURN_NULL_ON_FAIL(_createPipeline(pipeline));
+ m_pipelineCache.Add(pipeline);
+ m_currentPipeline = pipeline;
+ return pipeline;
+}
+
+Slang::Result VKRenderer::_createPipeline(RefPtr<Pipeline>& pipelineOut)
+{
+ RefPtr<Pipeline> pipeline(new Pipeline(m_api));
+
+ // Initialize the state
+ pipeline->m_primitiveTopology = m_primitiveTopology;
+ pipeline->m_pipelineLayout = m_currentPipelineLayout;
+ pipeline->m_shaderProgram = m_currentProgram;
+ pipeline->m_inputLayout = m_currentInputLayout;
+
+ // Must be equal at this point if all the items are correctly set in pipeline
+ assert(_isEqual(*pipeline));
+
+ VkPipelineCache pipelineCache = VK_NULL_HANDLE;
+
+ if (m_currentProgram->m_pipelineType == PipelineType::Compute)
+ {
+ // Then create a pipeline to use that layout
+
+ VkComputePipelineCreateInfo computePipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
+ computePipelineInfo.stage = m_currentProgram->m_compute;
+ computePipelineInfo.layout = pipeline->m_pipelineLayout->m_pipelineLayout;
+
+ SLANG_VK_CHECK(m_api.vkCreateComputePipelines(m_device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline->m_pipeline));
+ }
+ else if (m_currentProgram->m_pipelineType == PipelineType::Graphics)
+ {
+ // Create the graphics pipeline
+
+ const int width = m_swapChain.getWidth();
+ const int height = m_swapChain.getHeight();
+
+ VkPipelineShaderStageCreateInfo shaderStages[] = { m_currentProgram->m_vertex, m_currentProgram->m_fragment };
+
+ // VertexBuffer/s
+ // Currently only handles one
+
+ VkPipelineVertexInputStateCreateInfo vertexInputInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
+ vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertexInputInfo.vertexBindingDescriptionCount = 0;
+ vertexInputInfo.vertexAttributeDescriptionCount = 0;
+
+ VkVertexInputBindingDescription vertexInputBindingDescription;
+
+ if (m_currentInputLayout)
+ {
+ vertexInputBindingDescription.binding = 0;
+ vertexInputBindingDescription.stride = m_currentInputLayout->m_vertexSize;
+ vertexInputBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+
+ const auto& srcAttributeDescs = m_currentInputLayout->m_vertexDescs;
+
+ vertexInputInfo.vertexBindingDescriptionCount = 1;
+ vertexInputInfo.pVertexBindingDescriptions = &vertexInputBindingDescription;
+
+ vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(srcAttributeDescs.Count());
+ vertexInputInfo.pVertexAttributeDescriptions = srcAttributeDescs.getBuffer();
+ }
+
+ //
+
+ VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
+ inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ inputAssembly.primitiveRestartEnable = VK_FALSE;
+
+ VkViewport viewport = {};
+ viewport.x = 0.0f;
+ viewport.y = 0.0f;
+ viewport.width = (float)width;
+ viewport.height = (float)height;
+ viewport.minDepth = 0.0f;
+ viewport.maxDepth = 1.0f;
+
+ VkRect2D scissor = {};
+ scissor.offset = { 0, 0 };
+ scissor.extent = { uint32_t(width), uint32_t(height) };
+
+ VkPipelineViewportStateCreateInfo viewportState = {};
+ viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewportState.viewportCount = 1;
+ viewportState.pViewports = &viewport;
+ viewportState.scissorCount = 1;
+ viewportState.pScissors = &scissor;
+
+ VkPipelineRasterizationStateCreateInfo rasterizer = {};
+ rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rasterizer.depthClampEnable = VK_FALSE;
+ rasterizer.rasterizerDiscardEnable = VK_FALSE;
+ rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
+ rasterizer.lineWidth = 1.0f;
+ rasterizer.cullMode = VK_CULL_MODE_NONE;
+ rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
+ rasterizer.depthBiasEnable = VK_FALSE;
+
+ VkPipelineMultisampleStateCreateInfo multisampling = {};
+ multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ multisampling.sampleShadingEnable = VK_FALSE;
+ multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+
+ VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
+ colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ colorBlendAttachment.blendEnable = VK_FALSE;
+
+ VkPipelineColorBlendStateCreateInfo colorBlending = {};
+ colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ colorBlending.logicOpEnable = VK_FALSE;
+ colorBlending.logicOp = VK_LOGIC_OP_COPY;
+ colorBlending.attachmentCount = 1;
+ colorBlending.pAttachments = &colorBlendAttachment;
+ colorBlending.blendConstants[0] = 0.0f;
+ colorBlending.blendConstants[1] = 0.0f;
+ colorBlending.blendConstants[2] = 0.0f;
+ colorBlending.blendConstants[3] = 0.0f;
+
+ VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
+
+ pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+ pipelineInfo.stageCount = 2;
+ pipelineInfo.pStages = shaderStages;
+ pipelineInfo.pVertexInputState = &vertexInputInfo;
+ pipelineInfo.pInputAssemblyState = &inputAssembly;
+ pipelineInfo.pViewportState = &viewportState;
+ pipelineInfo.pRasterizationState = &rasterizer;
+ pipelineInfo.pMultisampleState = &multisampling;
+ pipelineInfo.pColorBlendState = &colorBlending;
+ pipelineInfo.layout = pipeline->m_pipelineLayout->m_pipelineLayout;
+ pipelineInfo.renderPass = m_renderPass;
+ pipelineInfo.subpass = 0;
+ pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
+
+ SLANG_VK_CHECK(m_api.vkCreateGraphicsPipelines(m_device, pipelineCache, 1, &pipelineInfo, nullptr, &pipeline->m_pipeline));
+ }
+ else
+ {
+ assert(!"Unhandled program type");
+ return SLANG_FAIL;
+ }
+
+ pipelineOut = pipeline;
+ return SLANG_OK;
+}
+#endif
+
+Result VKRenderer::_beginPass()
+{
+ if (m_swapChainImageIndex < 0)
+ {
+ return SLANG_FAIL;
+ }
+
+ const int numRenderTargets = 1;
+
+ const VulkanSwapChain::Image& image = m_swapChain.getImages()[m_swapChainImageIndex];
+
+ int numAttachments = 0;
+
+ // Start render pass
+ VkClearValue clearValues[kMaxAttachments];
+ clearValues[numAttachments++] = VkClearValue{ m_clearColor[0], m_clearColor[1], m_clearColor[2], m_clearColor[3] };
+
+ bool hasDepthBuffer = false;
+ if (hasDepthBuffer)
+ {
+ VkClearValue& clearValue = clearValues[numAttachments++];
+
+ clearValue.depthStencil.depth = 1.0f;
+ clearValue.depthStencil.stencil = 0;
+ }
+
+ const int width = m_swapChain.getWidth();
+ const int height = m_swapChain.getHeight();
+
+ VkCommandBuffer cmdBuffer = m_deviceQueue.getCommandBuffer();
+
+ VkRenderPassBeginInfo renderPassBegin = {};
+ renderPassBegin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+ renderPassBegin.renderPass = m_renderPass;
+ renderPassBegin.framebuffer = image.m_frameBuffer;
+ renderPassBegin.renderArea.offset.x = 0;
+ renderPassBegin.renderArea.offset.y = 0;
+ renderPassBegin.renderArea.extent.width = width;
+ renderPassBegin.renderArea.extent.height = height;
+ renderPassBegin.clearValueCount = numAttachments;
+ renderPassBegin.pClearValues = clearValues;
+
+ m_api.vkCmdBeginRenderPass(cmdBuffer, &renderPassBegin, VK_SUBPASS_CONTENTS_INLINE);
+
+ // Set up scissor and viewport
+ {
+ VkRect2D rects[kMaxRenderTargets] = {};
+ VkViewport viewports[kMaxRenderTargets] = {};
+ for (int i = 0; i < numRenderTargets; ++i)
+ {
+ rects[i] = VkRect2D{ 0, 0, uint32_t(width), uint32_t(height) };
+
+ VkViewport& dstViewport = viewports[i];
+
+ dstViewport.x = 0.0f;
+ dstViewport.y = 0.0f;
+ dstViewport.width = float(width);
+ dstViewport.height = float(height);
+ dstViewport.minDepth = 0.0f;
+ dstViewport.maxDepth = 1.0f;
+ }
+
+ m_api.vkCmdSetScissor(cmdBuffer, 0, numRenderTargets, rects);
+ m_api.vkCmdSetViewport(cmdBuffer, 0, numRenderTargets, viewports);
+ }
+
+ return SLANG_OK;
+}
+
+void VKRenderer::_endPass()
+{
+ VkCommandBuffer cmdBuffer = m_deviceQueue.getCommandBuffer();
+ m_api.vkCmdEndRenderPass(cmdBuffer);
+}
+
+void VKRenderer::_beginRender()
+{
+ m_swapChainImageIndex = m_swapChain.nextFrontImageIndex();
+
+ if (m_swapChainImageIndex < 0)
+ {
+ return;
+ }
+}
+
+void VKRenderer::_endRender()
+{
+ m_deviceQueue.flush();
+}
+
+Renderer* createVKRenderer()
+{
+ return new VKRenderer;
+}
+
+VKRenderer::~VKRenderer()
+{
+ if (m_renderPass != VK_NULL_HANDLE)
+ {
+ m_api.vkDestroyRenderPass(m_device, m_renderPass, nullptr);
+ m_renderPass = VK_NULL_HANDLE;
+ }
+}
+
+
+VkBool32 VKRenderer::handleDebugMessage(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject,
+ size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg)
+{
+ char const* severity = "message";
+ if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
+ severity = "warning";
+ if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
+ severity = "error";
+
+ // pMsg can be really big (it can be assembler dump for example)
+ // Use a dynamic buffer to store
+ size_t bufferSize = strlen(pMsg) + 1 + 1024;
+ List<char> bufferArray;
+ bufferArray.setCount(bufferSize);
+ char* buffer = bufferArray.getBuffer();
+
+ sprintf_s(buffer,
+ bufferSize,
+ "%s: %s %d: %s\n",
+ pLayerPrefix,
+ severity,
+ msgCode,
+ pMsg);
+
+ fprintf(stderr, "%s", buffer);
+ fflush(stderr);
+
+ OutputDebugStringA(buffer);
+
+ return VK_FALSE;
+}
+
+/* static */VKAPI_ATTR VkBool32 VKAPI_CALL VKRenderer::debugMessageCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject,
+ size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void* pUserData)
+{
+ return ((VKRenderer*)pUserData)->handleDebugMessage(flags, objType, srcObject, location, msgCode, pLayerPrefix, pMsg);
+}
+
+VkPipelineShaderStageCreateInfo VKRenderer::compileEntryPoint(
+ ShaderProgram::KernelDesc const& kernelDesc,
+ VkShaderStageFlagBits stage,
+ List<char>& bufferOut)
+{
+ char const* dataBegin = (char const*) kernelDesc.codeBegin;
+ char const* dataEnd = (char const*) kernelDesc.codeEnd;
+
+ // We need to make a copy of the code, since the Slang compiler
+ // will free the memory after a compile request is closed.
+ size_t codeSize = dataEnd - dataBegin;
+
+ bufferOut.insertRange(0, dataBegin, codeSize);
+
+ char* codeBegin = bufferOut.getBuffer();
+
+ VkShaderModuleCreateInfo moduleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
+ moduleCreateInfo.pCode = (uint32_t*)codeBegin;
+ moduleCreateInfo.codeSize = codeSize;
+
+ VkShaderModule module;
+ SLANG_VK_CHECK(m_api.vkCreateShaderModule(m_device, &moduleCreateInfo, nullptr, &module));
+
+ VkPipelineShaderStageCreateInfo shaderStageCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO };
+ shaderStageCreateInfo.stage = stage;
+
+ shaderStageCreateInfo.module = module;
+ shaderStageCreateInfo.pName = "main";
+
+ return shaderStageCreateInfo;
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!!
+
+SlangResult VKRenderer::initialize(const Desc& desc, void* inWindowHandle)
+{
+ SLANG_RETURN_ON_FAIL(m_module.init());
+ SLANG_RETURN_ON_FAIL(m_api.initGlobalProcs(m_module));
+
+ m_desc = desc;
+
+ VkApplicationInfo applicationInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
+ applicationInfo.pApplicationName = "slang-render-test";
+ applicationInfo.pEngineName = "slang-render-test";
+ applicationInfo.apiVersion = VK_API_VERSION_1_0;
+
+ char const* instanceExtensions[] =
+ {
+ VK_KHR_SURFACE_EXTENSION_NAME,
+
+ VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
+
+#if SLANG_WINDOWS_FAMILY
+ VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
+#else
+ VK_KHR_XLIB_SURFACE_EXTENSION_NAME
+#endif
+
+#if ENABLE_VALIDATION_LAYER
+ VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
+#endif
+ };
+
+ VkInstance instance = VK_NULL_HANDLE;
+
+ VkInstanceCreateInfo instanceCreateInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
+ instanceCreateInfo.pApplicationInfo = &applicationInfo;
+
+ instanceCreateInfo.enabledExtensionCount = SLANG_COUNT_OF(instanceExtensions);
+ instanceCreateInfo.ppEnabledExtensionNames = &instanceExtensions[0];
+
+#if ENABLE_VALIDATION_LAYER
+ const char* layerNames[] = { "VK_LAYER_LUNARG_standard_validation" };
+ instanceCreateInfo.enabledLayerCount = SLANG_COUNT_OF(layerNames);
+ instanceCreateInfo.ppEnabledLayerNames = layerNames;
+#endif
+
+ SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateInstance(&instanceCreateInfo, nullptr, &instance));
+ SLANG_RETURN_ON_FAIL(m_api.initInstanceProcs(instance));
+
+#if ENABLE_VALIDATION_LAYER
+ VkDebugReportFlagsEXT debugFlags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
+
+ VkDebugReportCallbackCreateInfoEXT debugCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT };
+ debugCreateInfo.pfnCallback = &debugMessageCallback;
+ debugCreateInfo.pUserData = this;
+ debugCreateInfo.flags = debugFlags;
+
+ SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateDebugReportCallbackEXT(instance, &debugCreateInfo, nullptr, &m_debugReportCallback));
+#endif
+
+ uint32_t numPhysicalDevices = 0;
+ SLANG_VK_RETURN_ON_FAIL(m_api.vkEnumeratePhysicalDevices(instance, &numPhysicalDevices, nullptr));
+
+ List<VkPhysicalDevice> physicalDevices;
+ physicalDevices.setCount(numPhysicalDevices);
+ SLANG_VK_RETURN_ON_FAIL(m_api.vkEnumeratePhysicalDevices(instance, &numPhysicalDevices, physicalDevices.getBuffer()));
+
+ Index selectedDeviceIndex = 0;
+
+ if (desc.adapter.getLength())
+ {
+ selectedDeviceIndex = -1;
+
+ String lowerAdapter = desc.adapter.toLower();
+
+ for (Index i = 0; i < physicalDevices.getCount(); ++i)
+ {
+ auto physicalDevice = physicalDevices[i];
+
+ VkPhysicalDeviceProperties basicProps = {};
+ m_api.vkGetPhysicalDeviceProperties(physicalDevice, &basicProps);
+
+ String lowerName = String(basicProps.deviceName).toLower();
+
+ if (lowerName.indexOf(lowerAdapter) != Index(-1))
+ {
+ selectedDeviceIndex = i;
+ break;
+ }
+ }
+ if (selectedDeviceIndex < 0)
+ {
+ // Device not found
+ return SLANG_FAIL;
+ }
+ }
+
+ SLANG_RETURN_ON_FAIL(m_api.initPhysicalDevice(physicalDevices[selectedDeviceIndex]));
+
+ List<const char*> deviceExtensions;
+ deviceExtensions.add(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+
+ VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
+ deviceCreateInfo.queueCreateInfoCount = 1;
+
+ deviceCreateInfo.pEnabledFeatures = &m_api.m_deviceFeatures;
+
+ // Get the device features (doesn't use, but useful when debugging)
+ if (m_api.vkGetPhysicalDeviceFeatures2)
+ {
+ VkPhysicalDeviceFeatures2 deviceFeatures2 = {};
+ deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+ m_api.vkGetPhysicalDeviceFeatures2(m_api.m_physicalDevice, &deviceFeatures2);
+ }
+
+ VkPhysicalDeviceProperties basicProps = {};
+ m_api.vkGetPhysicalDeviceProperties(m_api.m_physicalDevice, &basicProps);
+
+ // Get the API version
+ const uint32_t majorVersion = VK_VERSION_MAJOR(basicProps.apiVersion);
+ const uint32_t minorVersion = VK_VERSION_MINOR(basicProps.apiVersion);
+
+ // Float16 features
+ // Need in this scope because it will be linked into the device creation (if it is available)
+ VkPhysicalDeviceFloat16Int8FeaturesKHR float16Features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR };
+
+ // API version check, can't use vkGetPhysicalDeviceProperties2 yet since this device might not support it
+ if (VK_MAKE_VERSION(majorVersion, minorVersion, 0) >= VK_API_VERSION_1_1 &&
+ m_api.vkGetPhysicalDeviceProperties2 &&
+ m_api.vkGetPhysicalDeviceFeatures2)
+ {
+ VkPhysicalDeviceProperties2 physicalDeviceProps2;
+
+ physicalDeviceProps2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
+ physicalDeviceProps2.pNext = nullptr;
+ physicalDeviceProps2.properties = {};
+
+ m_api.vkGetPhysicalDeviceProperties2(m_api.m_physicalDevice, &physicalDeviceProps2);
+
+ // Get device features
+ VkPhysicalDeviceFeatures2 deviceFeatures2 = {};
+ deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+
+ // Link together for lookup
+ float16Features.pNext = deviceFeatures2.pNext;
+ deviceFeatures2.pNext = &float16Features;
+
+ m_api.vkGetPhysicalDeviceFeatures2(m_api.m_physicalDevice, &deviceFeatures2);
+
+ // If we have float16 features then enable
+ if (float16Features.shaderFloat16)
+ {
+ // Link into the creation features
+ float16Features.pNext = (void*)deviceCreateInfo.pNext;
+ deviceCreateInfo.pNext = &float16Features;
+
+ // Add the Float16 extension
+ deviceExtensions.add(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
+
+ // We have half support
+ m_features.add("half");
+ }
+ }
+
+ int queueFamilyIndex = m_api.findQueue(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT);
+ assert(queueFamilyIndex >= 0);
+
+ float queuePriority = 0.0f;
+ VkDeviceQueueCreateInfo queueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
+ queueCreateInfo.queueFamilyIndex = queueFamilyIndex;
+ queueCreateInfo.queueCount = 1;
+ queueCreateInfo.pQueuePriorities = &queuePriority;
+
+ deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
+
+ deviceCreateInfo.enabledExtensionCount = uint32_t(deviceExtensions.getCount());
+ deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.getBuffer();
+
+ SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateDevice(m_api.m_physicalDevice, &deviceCreateInfo, nullptr, &m_device));
+ SLANG_RETURN_ON_FAIL(m_api.initDeviceProcs(m_device));
+
+ {
+ VkQueue queue;
+ m_api.vkGetDeviceQueue(m_device, queueFamilyIndex, 0, &queue);
+ SLANG_RETURN_ON_FAIL(m_deviceQueue.init(m_api, queue, queueFamilyIndex));
+ }
+
+ // set up swap chain
+
+ {
+ VulkanSwapChain::Desc desc;
+ VulkanSwapChain::PlatformDesc* platformDesc = nullptr;
+
+ desc.init();
+ desc.m_format = Format::RGBA_Unorm_UInt8;
+
+#if SLANG_WINDOWS_FAMILY
+ VulkanSwapChain::WinPlatformDesc winPlatformDesc;
+ winPlatformDesc.m_hinstance = ::GetModuleHandle(nullptr);
+ winPlatformDesc.m_hwnd = (HWND)inWindowHandle;
+ platformDesc = &winPlatformDesc;
+#endif
+
+ SLANG_RETURN_ON_FAIL(m_swapChain.init(&m_deviceQueue, desc, platformDesc));
+ }
+
+ // depth/stencil?
+
+ // render pass?
+
+ {
+ const int numRenderTargets = 1;
+ bool shouldClear = true;
+ bool shouldClearDepth = false;
+ bool shouldClearStencil = false;
+ bool hasDepthBuffer = false;
+
+ Format depthFormat = Format::Unknown;
+ VkFormat colorFormat = m_swapChain.getVkFormat();
+
+ int numAttachments = 0;
+ // We need extra space if we have depth buffer
+ VkAttachmentDescription attachmentDesc[kMaxRenderTargets + 1] = {};
+ for (int i = 0; i < numRenderTargets; ++i)
+ {
+ VkAttachmentDescription& dst = attachmentDesc[numAttachments ++];
+
+ dst.flags = 0;
+ dst.format = colorFormat;
+ dst.samples = VK_SAMPLE_COUNT_1_BIT;
+ dst.loadOp = shouldClear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
+ dst.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ dst.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ dst.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ dst.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ dst.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ }
+ if (hasDepthBuffer)
+ {
+ VkAttachmentDescription& dst = attachmentDesc[numAttachments++];
+
+ dst.flags = 0;
+ dst.format = VulkanUtil::getVkFormat(depthFormat);
+ dst.samples = VK_SAMPLE_COUNT_1_BIT;
+ dst.loadOp = shouldClearDepth ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
+ dst.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ dst.stencilLoadOp = shouldClearStencil ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
+ dst.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
+ dst.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ dst.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ }
+
+ VkAttachmentReference colorAttachments[kMaxRenderTargets] = {};
+ for (int i = 0; i < numRenderTargets; ++i)
+ {
+ VkAttachmentReference& dst = colorAttachments[i];
+ dst.attachment = i;
+ dst.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ }
+
+ VkAttachmentReference depthAttachment = {};
+ depthAttachment.attachment = numRenderTargets;
+ depthAttachment.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
+ VkSubpassDescription subpassDesc = {};
+ subpassDesc.flags = 0;
+ subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpassDesc.inputAttachmentCount = 0u;
+ subpassDesc.pInputAttachments = nullptr;
+ subpassDesc.colorAttachmentCount = numRenderTargets;
+ subpassDesc.pColorAttachments = colorAttachments;
+ subpassDesc.pResolveAttachments = nullptr;
+ subpassDesc.pDepthStencilAttachment = hasDepthBuffer ? &depthAttachment : nullptr;
+ subpassDesc.preserveAttachmentCount = 0u;
+ subpassDesc.pPreserveAttachments = nullptr;
+
+ VkRenderPassCreateInfo renderPassCreateInfo = {};
+ renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+ renderPassCreateInfo.attachmentCount = numAttachments;
+ renderPassCreateInfo.pAttachments = attachmentDesc;
+ renderPassCreateInfo.subpassCount = 1;
+ renderPassCreateInfo.pSubpasses = &subpassDesc;
+ SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateRenderPass(m_device, &renderPassCreateInfo, nullptr, &m_renderPass));
+ }
+
+ // frame buffer
+ SLANG_RETURN_ON_FAIL(m_swapChain.createFrameBuffers(m_renderPass));
+
+ _beginRender();
+
+ return SLANG_OK;
+}
+
+void VKRenderer::submitGpuWork()
+{
+ m_deviceQueue.flush();
+}
+
+void VKRenderer::waitForGpu()
+{
+ m_deviceQueue.flushAndWait();
+}
+
+void VKRenderer::setClearColor(const float color[4])
+{
+ for (int ii = 0; ii < 4; ++ii)
+ m_clearColor[ii] = color[ii];
+}
+
+void VKRenderer::clearFrame()
+{
+}
+
+void VKRenderer::presentFrame()
+{
+ _endRender();
+
+ const bool vsync = true;
+ m_swapChain.present(vsync);
+
+ _beginRender();
+}
+
+TextureResource::Desc VKRenderer::getSwapChainTextureDesc()
+{
+ TextureResource::Desc desc;
+ desc.init2D(Resource::Type::Texture2D, Format::Unknown, m_desc.width, m_desc.height, 1);
+ return desc;
+}
+
+SlangResult VKRenderer::captureScreenSurface(Surface& surfaceOut)
+{
+ return SLANG_FAIL;
+}
+
+static VkBufferUsageFlagBits _calcBufferUsageFlags(Resource::BindFlag::Enum bind)
+{
+ typedef Resource::BindFlag BindFlag;
+
+ switch (bind)
+ {
+ case BindFlag::VertexBuffer: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ case BindFlag::IndexBuffer: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
+ case BindFlag::ConstantBuffer: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+ case BindFlag::StreamOutput:
+ case BindFlag::RenderTarget:
+ case BindFlag::DepthStencil:
+ {
+ assert(!"Not supported yet");
+ return VkBufferUsageFlagBits(0);
+ }
+ case BindFlag::UnorderedAccess: return VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
+ case BindFlag::PixelShaderResource: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
+ case BindFlag::NonPixelShaderResource: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
+ default: return VkBufferUsageFlagBits(0);
+ }
+}
+
+static VkBufferUsageFlagBits _calcBufferUsageFlags(int bindFlags)
+{
+ int dstFlags = 0;
+ while (bindFlags)
+ {
+ int lsb = bindFlags & -bindFlags;
+ dstFlags |= _calcBufferUsageFlags(Resource::BindFlag::Enum(lsb));
+ bindFlags &= ~lsb;
+ }
+ return VkBufferUsageFlagBits(dstFlags);
+}
+
+static VkBufferUsageFlags _calcBufferUsageFlags(int bindFlags, int cpuAccessFlags, const void* initData)
+{
+ VkBufferUsageFlags usage = _calcBufferUsageFlags(bindFlags);
+
+ if (cpuAccessFlags & Resource::AccessFlag::Read)
+ {
+ // If it can be read from, set this
+ usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+ }
+ if ((cpuAccessFlags & Resource::AccessFlag::Write) || initData)
+ {
+ usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+ }
+
+ return usage;
+}
+
+static VkImageUsageFlagBits _calcImageUsageFlags(Resource::BindFlag::Enum bind)
+{
+ typedef Resource::BindFlag BindFlag;
+
+ switch (bind)
+ {
+ case BindFlag::RenderTarget: return VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+ case BindFlag::DepthStencil: return VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+ case BindFlag::NonPixelShaderResource:
+ case BindFlag::PixelShaderResource:
+ {
+ // Ignore
+ return VkImageUsageFlagBits(0);
+ }
+ default:
+ {
+ assert(!"Unsupported");
+ return VkImageUsageFlagBits(0);
+ }
+ }
+}
+
+static VkImageUsageFlagBits _calcImageUsageFlags(int bindFlags)
+{
+ int dstFlags = 0;
+ while (bindFlags)
+ {
+ int lsb = bindFlags & -bindFlags;
+ dstFlags |= _calcImageUsageFlags(Resource::BindFlag::Enum(lsb));
+ bindFlags &= ~lsb;
+ }
+ return VkImageUsageFlagBits(dstFlags);
+}
+
+static VkImageUsageFlags _calcImageUsageFlags(int bindFlags, int cpuAccessFlags, const void* initData)
+{
+ VkImageUsageFlags usage = _calcImageUsageFlags(bindFlags);
+
+ usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
+
+ if (cpuAccessFlags & Resource::AccessFlag::Read)
+ {
+ // If it can be read from, set this
+ usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+ }
+ if ((cpuAccessFlags & Resource::AccessFlag::Write) || initData)
+ {
+ usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+ }
+
+ return usage;
+}
+
+void VKRenderer::_transitionImageLayout(VkImage image, VkFormat format, const TextureResource::Desc& desc, VkImageLayout oldLayout, VkImageLayout newLayout)
+{
+ VkImageMemoryBarrier barrier = {};
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.oldLayout = oldLayout;
+ barrier.newLayout = newLayout;
+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.image = image;
+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ barrier.subresourceRange.baseMipLevel = 0;
+ barrier.subresourceRange.levelCount = desc.numMipLevels;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.layerCount = 1;
+
+ VkPipelineStageFlags sourceStage;
+ VkPipelineStageFlags destinationStage;
+
+ if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
+ {
+ barrier.srcAccessMask = 0;
+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+
+ sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ }
+ else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
+ {
+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+
+ sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ }
+ else
+ {
+ assert(!"unsupported layout transition!");
+ return;
+ }
+
+ VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer();
+
+ m_api.vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
+}
+
+Result VKRenderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData, TextureResource** outResource)
+{
+ TextureResource::Desc desc(descIn);
+ desc.setDefaults(initialUsage);
+
+ const VkFormat format = VulkanUtil::getVkFormat(desc.format);
+ if (format == VK_FORMAT_UNDEFINED)
+ {
+ assert(!"Unhandled image format");
+ return SLANG_FAIL;
+ }
+
+ const int arraySize = desc.calcEffectiveArraySize();
+
+ RefPtr<TextureResourceImpl> texture(new TextureResourceImpl(desc, initialUsage, &m_api));
+
+ // Create the image
+ {
+ VkImageCreateInfo imageInfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
+
+ switch (desc.type)
+ {
+ case Resource::Type::Texture1D:
+ {
+ imageInfo.imageType = VK_IMAGE_TYPE_1D;
+ imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), 1, 1 };
+ break;
+ }
+ case Resource::Type::Texture2D:
+ {
+ imageInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 };
+ break;
+ }
+ case Resource::Type::TextureCube:
+ {
+ imageInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 };
+ break;
+ }
+ case Resource::Type::Texture3D:
+ {
+ // Can't have an array and 3d texture
+ assert(desc.arraySize <= 1);
+
+ imageInfo.imageType = VK_IMAGE_TYPE_3D;
+ imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), uint32_t(descIn.size.depth) };
+ break;
+ }
+ default:
+ {
+ assert(!"Unhandled type");
+ return SLANG_FAIL;
+ }
+ }
+
+ imageInfo.mipLevels = desc.numMipLevels;
+ imageInfo.arrayLayers = arraySize;
+
+ imageInfo.format = format;
+
+ imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+ imageInfo.usage = _calcImageUsageFlags(desc.bindFlags, desc.cpuAccessFlags, initData);
+ imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+ imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+ imageInfo.flags = 0; // Optional
+
+ SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateImage(m_device, &imageInfo, nullptr, &texture->m_image));
+ }
+
+ VkMemoryRequirements memRequirements;
+ m_api.vkGetImageMemoryRequirements(m_device, texture->m_image, &memRequirements);
+
+ // Allocate the memory
+ {
+ VkMemoryPropertyFlags reqMemoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+
+ VkMemoryAllocateInfo allocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
+
+ int memoryTypeIndex = m_api.findMemoryTypeIndex(memRequirements.memoryTypeBits, reqMemoryProperties);
+ assert(memoryTypeIndex >= 0);
+
+ VkMemoryPropertyFlags actualMemoryProperites = m_api.m_deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags;
+
+ allocInfo.allocationSize = memRequirements.size;
+ allocInfo.memoryTypeIndex = memoryTypeIndex;
+
+ SLANG_VK_RETURN_ON_FAIL(m_api.vkAllocateMemory(m_device, &allocInfo, nullptr, &texture->m_imageMemory));
+ }
+
+ // Bind the memory to the image
+ m_api.vkBindImageMemory(m_device, texture->m_image, texture->m_imageMemory, 0);
+
+ if (initData)
+ {
+ List<TextureResource::Size> mipSizes;
+
+ VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer();
+
+ const int numMipMaps = desc.numMipLevels;
+ assert(initData->numMips == numMipMaps);
+
+ // Calculate how large the buffer has to be
+ size_t bufferSize = 0;
+ // Calculate how large an array entry is
+ for (int j = 0; j < numMipMaps; ++j)
+ {
+ const TextureResource::Size mipSize = desc.size.calcMipSize(j);
+
+ const int rowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width);
+ const int numRows = Surface::calcNumRows(desc.format, mipSize.height);
+
+ mipSizes.add(mipSize);
+
+ bufferSize += (rowSizeInBytes * numRows) * mipSize.depth;
+ }
+
+
+ // Calculate the total size taking into account the array
+ bufferSize *= arraySize;
+
+ Buffer uploadBuffer;
+ SLANG_RETURN_ON_FAIL(uploadBuffer.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT));
+
+ assert(mipSizes.getCount() == numMipMaps);
+
+ // Copy into upload buffer
+ {
+ int subResourceIndex = 0;
+
+ uint8_t* dstData;
+ m_api.vkMapMemory(m_device, uploadBuffer.m_memory, 0, bufferSize, 0, (void**)&dstData);
+
+ for (int i = 0; i < arraySize; ++i)
+ {
+ for (Index j = 0; j < mipSizes.getCount(); ++j)
+ {
+ const auto& mipSize = mipSizes[j];
+
+ const ptrdiff_t srcRowStride = initData->mipRowStrides[j];
+ const int dstRowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width);
+ const int numRows = Surface::calcNumRows(desc.format, mipSize.height);
+
+ for (int k = 0; k < mipSize.depth; k++)
+ {
+ const uint8_t* srcData = (const uint8_t*)(initData->subResources[subResourceIndex]);
+
+ for (int l = 0; l < numRows; l++)
+ {
+ ::memcpy(dstData, srcData, dstRowSizeInBytes);
+
+ dstData += dstRowSizeInBytes;
+ srcData += srcRowStride;
+ }
+
+ subResourceIndex++;
+ }
+ }
+ }
+
+ m_api.vkUnmapMemory(m_device, uploadBuffer.m_memory);
+ }
+
+ _transitionImageLayout(texture->m_image, format, texture->getDesc(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+
+ {
+ size_t srcOffset = 0;
+ for (int i = 0; i < arraySize; ++i)
+ {
+ for (Index j = 0; j < mipSizes.getCount(); ++j)
+ {
+ const auto& mipSize = mipSizes[j];
+
+ const int rowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width);
+ const int numRows = Surface::calcNumRows(desc.format, mipSize.height);
+
+ // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkBufferImageCopy.html
+ // bufferRowLength and bufferImageHeight specify the data in buffer memory as a subregion of a larger two- or three-dimensional image,
+ // and control the addressing calculations of data in buffer memory. If either of these values is zero, that aspect of the buffer memory
+ // is considered to be tightly packed according to the imageExtent.
+
+ VkBufferImageCopy region = {};
+
+ region.bufferOffset = srcOffset;
+ region.bufferRowLength = 0; //rowSizeInBytes;
+ region.bufferImageHeight = 0;
+
+ region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.imageSubresource.mipLevel = uint32_t(j);
+ region.imageSubresource.baseArrayLayer = i;
+ region.imageSubresource.layerCount = 1;
+ region.imageOffset = { 0, 0, 0 };
+ region.imageExtent = { uint32_t(mipSize.width), uint32_t(mipSize.height), uint32_t(mipSize.depth) };
+
+ // Do the copy (do all depths in a single go)
+ m_api.vkCmdCopyBufferToImage(commandBuffer, uploadBuffer.m_buffer, texture->m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
+
+ // Next
+ srcOffset += rowSizeInBytes * numRows * mipSize.depth;
+ }
+ }
+ }
+
+ _transitionImageLayout(texture->m_image, format, texture->getDesc(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+
+ m_deviceQueue.flushAndWait();
+ }
+
+ *outResource = texture.detach();
+ return SLANG_OK;
+}
+
+Result VKRenderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData, BufferResource** outResource)
+{
+ BufferResource::Desc desc(descIn);
+ desc.setDefaults(initialUsage);
+
+ const size_t bufferSize = desc.sizeInBytes;
+
+ VkMemoryPropertyFlags reqMemoryProperties = 0;
+
+ VkBufferUsageFlags usage = _calcBufferUsageFlags(desc.bindFlags, desc.cpuAccessFlags, initData);
+
+ switch (initialUsage)
+ {
+ case Resource::Usage::ConstantBuffer:
+ {
+ reqMemoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ break;
+ }
+ default: break;
+ }
+
+ RefPtr<BufferResourceImpl> buffer(new BufferResourceImpl(initialUsage, desc, this));
+ SLANG_RETURN_ON_FAIL(buffer->m_buffer.init(m_api, desc.sizeInBytes, usage, reqMemoryProperties));
+
+ if ((desc.cpuAccessFlags & Resource::AccessFlag::Write) || initData)
+ {
+ SLANG_RETURN_ON_FAIL(buffer->m_uploadBuffer.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT));
+ }
+
+ if (initData)
+ {
+ // TODO: only create staging buffer if the memory type
+ // used for the buffer doesn't let us fill things in
+ // directly.
+ // Copy into staging buffer
+ void* mappedData = nullptr;
+ SLANG_VK_CHECK(m_api.vkMapMemory(m_device, buffer->m_uploadBuffer.m_memory, 0, bufferSize, 0, &mappedData));
+ ::memcpy(mappedData, initData, bufferSize);
+ m_api.vkUnmapMemory(m_device, buffer->m_uploadBuffer.m_memory);
+
+ // Copy from staging buffer to real buffer
+ VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer();
+
+ VkBufferCopy copyInfo = {};
+ copyInfo.size = bufferSize;
+ m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, &copyInfo);
+
+ //flushCommandBuffer(commandBuffer);
+ }
+
+ *outResource = buffer.detach();
+ return SLANG_OK;
+}
+
+
+VkFilter translateFilterMode(TextureFilteringMode mode)
+{
+ switch (mode)
+ {
+ default:
+ return VkFilter(0);
+
+#define CASE(SRC, DST) \
+ case TextureFilteringMode::SRC: return VK_FILTER_##DST
+
+ CASE(Point, NEAREST);
+ CASE(Linear, LINEAR);
+
+#undef CASE
+ }
+}
+
+VkSamplerMipmapMode translateMipFilterMode(TextureFilteringMode mode)
+{
+ switch (mode)
+ {
+ default:
+ return VkSamplerMipmapMode(0);
+
+#define CASE(SRC, DST) \
+ case TextureFilteringMode::SRC: return VK_SAMPLER_MIPMAP_MODE_##DST
+
+ CASE(Point, NEAREST);
+ CASE(Linear, LINEAR);
+
+#undef CASE
+ }
+}
+
+VkSamplerAddressMode translateAddressingMode(TextureAddressingMode mode)
+{
+ switch (mode)
+ {
+ default:
+ return VkSamplerAddressMode(0);
+
+#define CASE(SRC, DST) \
+ case TextureAddressingMode::SRC: return VK_SAMPLER_ADDRESS_MODE_##DST
+
+ CASE(Wrap, REPEAT);
+ CASE(ClampToEdge, CLAMP_TO_EDGE);
+ CASE(ClampToBorder, CLAMP_TO_BORDER);
+ CASE(MirrorRepeat, MIRRORED_REPEAT);
+ CASE(MirrorOnce, MIRROR_CLAMP_TO_EDGE);
+
+#undef CASE
+ }
+}
+
+static VkCompareOp translateComparisonFunc(ComparisonFunc func)
+{
+ switch (func)
+ {
+ default:
+ // TODO: need to report failures
+ return VK_COMPARE_OP_ALWAYS;
+
+#define CASE(FROM, TO) \
+ case ComparisonFunc::FROM: return VK_COMPARE_OP_##TO
+
+ CASE(Never, NEVER);
+ CASE(Less, LESS);
+ CASE(Equal, EQUAL);
+ CASE(LessEqual, LESS_OR_EQUAL);
+ CASE(Greater, GREATER);
+ CASE(NotEqual, NOT_EQUAL);
+ CASE(GreaterEqual, GREATER_OR_EQUAL);
+ CASE(Always, ALWAYS);
+#undef CASE
+ }
+}
+
+Result VKRenderer::createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler)
+{
+ VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
+
+ samplerInfo.magFilter = translateFilterMode(desc.minFilter);
+ samplerInfo.minFilter = translateFilterMode(desc.magFilter);
+
+ samplerInfo.addressModeU = translateAddressingMode(desc.addressU);
+ samplerInfo.addressModeV = translateAddressingMode(desc.addressV);
+ samplerInfo.addressModeW = translateAddressingMode(desc.addressW);
+
+ samplerInfo.anisotropyEnable = desc.maxAnisotropy > 1;
+ samplerInfo.maxAnisotropy = (float) desc.maxAnisotropy;
+
+ // TODO: support translation of border color...
+ samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
+
+ samplerInfo.unnormalizedCoordinates = VK_FALSE;
+ samplerInfo.compareEnable = desc.reductionOp == TextureReductionOp::Comparison;
+ samplerInfo.compareOp = translateComparisonFunc(desc.comparisonFunc);
+ samplerInfo.mipmapMode = translateMipFilterMode(desc.mipFilter);
+
+ VkSampler sampler;
+ SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateSampler(m_device, &samplerInfo, nullptr, &sampler));
+
+ RefPtr<SamplerStateImpl> samplerImpl = new SamplerStateImpl();
+ samplerImpl->m_sampler = sampler;
+ *outSampler = samplerImpl.detach();
+ return SLANG_OK;
+}
+
+Result VKRenderer::createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView)
+{
+ assert(!"unimplemented");
+ return SLANG_FAIL;
+}
+
+Result VKRenderer::createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView)
+{
+ auto resourceImpl = (BufferResourceImpl*) buffer;
+
+ // TODO: These should come from the `ResourceView::Desc`
+ VkDeviceSize offset = 0;
+ VkDeviceSize size = resourceImpl->getDesc().sizeInBytes;
+
+ // There are two different cases we need to think about for buffers.
+ //
+ // One is when we have a "uniform texel buffer" or "storage texel buffer,"
+ // in which case we need to construct a `VkBufferView` to represent the
+ // formatting that is applied to the buffer. This case would correspond
+ // to a `textureBuffer` or `imageBuffer` in GLSL, and more or less to
+ // `Buffer<..>` or `RWBuffer<...>` in HLSL.
+ //
+ // The other case is a `storage buffer` which is the catch-all for any
+ // non-formatted R/W access to a buffer. In GLSL this is a `buffer { ... }`
+ // declaration, while in HLSL it covers a bunch of different `RW*Buffer`
+ // cases. In these cases we do *not* need a `VkBufferView`, but in
+ // order to be compatible with other APIs that require views for any
+ // potentially writable access, we will have to create one anyway.
+ //
+ // We will distinguish the two cases by looking at whether the view
+ // is being requested with a format or not.
+ //
+
+ switch(desc.type)
+ {
+ default:
+ assert(!"unhandled");
+ return SLANG_FAIL;
+
+ case ResourceView::Type::UnorderedAccess:
+ // Is this a formatted view?
+ //
+ if(desc.format == Format::Unknown)
+ {
+ // Buffer usage that doesn't involve formatting doesn't
+ // require a view in Vulkan.
+ RefPtr<PlainBufferResourceViewImpl> viewImpl = new PlainBufferResourceViewImpl();
+ viewImpl->m_buffer = resourceImpl;
+ viewImpl->offset = 0;
+ viewImpl->size = size;
+ *outView = viewImpl.detach();
+ return SLANG_OK;
+ }
+ //
+ // If the view is formatted, then we need to handle
+ // it just like we would for a "sampled" buffer:
+ //
+ // FALLTHROUGH
+ case ResourceView::Type::ShaderResource:
+ {
+ VkBufferViewCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO };
+
+ info.format = VulkanUtil::getVkFormat(desc.format);
+ info.buffer = resourceImpl->m_buffer.m_buffer;
+ info.offset = offset;
+ info.range = size;
+
+ VkBufferView view;
+ SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateBufferView(m_device, &info, nullptr, &view));
+
+ RefPtr<TexelBufferResourceViewImpl> viewImpl = new TexelBufferResourceViewImpl();
+ viewImpl->m_buffer = resourceImpl;
+ viewImpl->m_view = view;
+ *outView = viewImpl.detach();
+ return SLANG_OK;
+ }
+ break;
+ }
+}
+
+Result VKRenderer::createInputLayout(const InputElementDesc* elements, UInt numElements, InputLayout** outLayout)
+{
+ RefPtr<InputLayoutImpl> layout(new InputLayoutImpl);
+
+ List<VkVertexInputAttributeDescription>& dstVertexDescs = layout->m_vertexDescs;
+
+ size_t vertexSize = 0;
+ dstVertexDescs.setCount(numElements);
+
+ for (UInt i = 0; i < numElements; ++i)
+ {
+ const InputElementDesc& srcDesc = elements[i];
+ VkVertexInputAttributeDescription& dstDesc = dstVertexDescs[i];
+
+ dstDesc.location = uint32_t(i);
+ dstDesc.binding = 0;
+ dstDesc.format = VulkanUtil::getVkFormat(srcDesc.format);
+ if (dstDesc.format == VK_FORMAT_UNDEFINED)
+ {
+ return SLANG_FAIL;
+ }
+
+ dstDesc.offset = uint32_t(srcDesc.offset);
+
+ const size_t elementSize = RendererUtil::getFormatSize(srcDesc.format);
+ assert(elementSize > 0);
+ const size_t endElement = srcDesc.offset + elementSize;
+
+ vertexSize = (vertexSize < endElement) ? endElement : vertexSize;
+ }
+
+ // Work out the overall size
+ layout->m_vertexSize = int(vertexSize);
+ *outLayout = layout.detach();
+ return SLANG_OK;
+}
+
+void* VKRenderer::map(BufferResource* bufferIn, MapFlavor flavor)
+{
+ BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(bufferIn);
+ assert(buffer->m_mapFlavor == MapFlavor::Unknown);
+
+ // Make sure everything has completed before reading...
+ m_deviceQueue.flushAndWait();
+
+ const size_t bufferSize = buffer->getDesc().sizeInBytes;
+
+ switch (flavor)
+ {
+ case MapFlavor::WriteDiscard:
+ case MapFlavor::HostWrite:
+ {
+ if (!buffer->m_uploadBuffer.isInitialized())
+ {
+ return nullptr;
+ }
+
+ void* mappedData = nullptr;
+ SLANG_VK_CHECK(m_api.vkMapMemory(m_device, buffer->m_uploadBuffer.m_memory, 0, bufferSize, 0, &mappedData));
+ buffer->m_mapFlavor = flavor;
+ return mappedData;
+ }
+ case MapFlavor::HostRead:
+ {
+ // Make sure there is space in the read buffer
+ buffer->m_readBuffer.setCount(bufferSize);
+
+ // create staging buffer
+ Buffer staging;
+
+ SLANG_RETURN_NULL_ON_FAIL(staging.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT));
+
+ // Copy from real buffer to staging buffer
+ VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer();
+
+ VkBufferCopy copyInfo = {};
+ copyInfo.size = bufferSize;
+ m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_buffer.m_buffer, staging.m_buffer, 1, &copyInfo);
+
+ m_deviceQueue.flushAndWait();
+
+ // Write out the data from the buffer
+ void* mappedData = nullptr;
+ SLANG_VK_CHECK(m_api.vkMapMemory(m_device, staging.m_memory, 0, bufferSize, 0, &mappedData));
+
+ ::memcpy(buffer->m_readBuffer.getBuffer(), mappedData, bufferSize);
+ m_api.vkUnmapMemory(m_device, staging.m_memory);
+
+ buffer->m_mapFlavor = flavor;
+
+ return buffer->m_readBuffer.getBuffer();
+ }
+ default:
+ return nullptr;
+ }
+}
+
+void VKRenderer::unmap(BufferResource* bufferIn)
+{
+ BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(bufferIn);
+ assert(buffer->m_mapFlavor != MapFlavor::Unknown);
+
+ const size_t bufferSize = buffer->getDesc().sizeInBytes;
+
+ switch (buffer->m_mapFlavor)
+ {
+ case MapFlavor::WriteDiscard:
+ case MapFlavor::HostWrite:
+ {
+ m_api.vkUnmapMemory(m_device, buffer->m_uploadBuffer.m_memory);
+
+ // Copy from staging buffer to real buffer
+ VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer();
+
+ VkBufferCopy copyInfo = {};
+ copyInfo.size = bufferSize;
+ m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, &copyInfo);
+
+ // TODO: is this necessary?
+ //m_deviceQueue.flushAndWait();
+ break;
+ }
+ default: break;
+ }
+
+ // Mark as no longer mapped
+ buffer->m_mapFlavor = MapFlavor::Unknown;
+}
+
+void VKRenderer::setPrimitiveTopology(PrimitiveTopology topology)
+{
+ m_primitiveTopology = VulkanUtil::getVkPrimitiveTopology(topology);
+}
+
+void VKRenderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets)
+{
+ {
+ const Index num = Index(startSlot + slotCount);
+ if (num > m_boundVertexBuffers.getCount())
+ {
+ m_boundVertexBuffers.setCount(num);
+ }
+ }
+
+ for (Index i = 0; i < Index(slotCount); i++)
+ {
+ BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(buffers[i]);
+ if (buffer)
+ {
+ assert(buffer->m_initialUsage == Resource::Usage::VertexBuffer);
+ }
+
+ BoundVertexBuffer& boundBuffer = m_boundVertexBuffers[startSlot + i];
+ boundBuffer.m_buffer = buffer;
+ boundBuffer.m_stride = int(strides[i]);
+ boundBuffer.m_offset = int(offsets[i]);
+ }
+}
+
+void VKRenderer::setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset)
+{
+}
+
+void VKRenderer::setDepthStencilTarget(ResourceView* depthStencilView)
+{
+}
+
+void VKRenderer::setViewports(UInt count, Viewport const* viewports)
+{
+ static const int kMaxViewports = 8; // TODO: base on device caps
+ assert(count <= kMaxViewports);
+
+ VkViewport vkViewports[kMaxViewports];
+ for(UInt ii = 0; ii < count; ++ii)
+ {
+ auto& inViewport = viewports[ii];
+ auto& vkViewport = vkViewports[ii];
+
+ vkViewport.x = inViewport.originX;
+ vkViewport.y = inViewport.originY;
+ vkViewport.width = inViewport.extentX;
+ vkViewport.height = inViewport.extentY;
+ vkViewport.minDepth = inViewport.minZ;
+ vkViewport.maxDepth = inViewport.maxZ;
+ }
+
+ VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer();
+ m_api.vkCmdSetViewport(commandBuffer, 0, uint32_t(count), vkViewports);
+}
+
+void VKRenderer::setScissorRects(UInt count, ScissorRect const* rects)
+{
+ static const int kMaxScissorRects = 8; // TODO: base on device caps
+ assert(count <= kMaxScissorRects);
+
+ VkRect2D vkRects[kMaxScissorRects];
+ for(UInt ii = 0; ii < count; ++ii)
+ {
+ auto& inRect = rects[ii];
+ auto& vkRect = vkRects[ii];
+
+ vkRect.offset.x = int32_t(inRect.minX);
+ vkRect.offset.y = int32_t(inRect.minY);
+ vkRect.extent.width = uint32_t(inRect.maxX - inRect.minX);
+ vkRect.extent.height = uint32_t(inRect.maxY - inRect.minY);
+ }
+
+ VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer();
+ m_api.vkCmdSetScissor(commandBuffer, 0, uint32_t(count), vkRects);
+}
+
+void VKRenderer::setPipelineState(PipelineType pipelineType, PipelineState* state)
+{
+ m_currentPipeline = (PipelineStateImpl*)state;
+}
+
+void VKRenderer::draw(UInt vertexCount, UInt startVertex = 0)
+{
+ auto pipeline = m_currentPipeline;
+ if (!pipeline || pipeline->m_shaderProgram->m_pipelineType != PipelineType::Graphics)
+ {
+ assert(!"Invalid render pipeline");
+ return;
+ }
+
+ SLANG_RETURN_VOID_ON_FAIL(_beginPass());
+
+ // Also create descriptor sets based on the given pipeline layout
+ VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer();
+
+ m_api.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->m_pipeline);
+
+ auto pipelineLayoutImpl = pipeline->m_pipelineLayout.Ptr();
+ m_api.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayoutImpl->m_pipelineLayout,
+ 0, uint32_t(pipelineLayoutImpl->m_descriptorSetCount),
+ &m_currentDescriptorSets[0],
+ 0, nullptr);
+
+ // Bind the vertex buffer
+ if (m_boundVertexBuffers.getCount() > 0 && m_boundVertexBuffers[0].m_buffer)
+ {
+ const BoundVertexBuffer& boundVertexBuffer = m_boundVertexBuffers[0];
+
+ VkBuffer vertexBuffers[] = { boundVertexBuffer.m_buffer->m_buffer.m_buffer };
+ VkDeviceSize offsets[] = { VkDeviceSize(boundVertexBuffer.m_offset) };
+
+ m_api.vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
+ }
+
+ m_api.vkCmdDraw(commandBuffer, static_cast<uint32_t>(vertexCount), 1, 0, 0);
+
+ _endPass();
+}
+
+void VKRenderer::drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex)
+{
+}
+
+void VKRenderer::dispatchCompute(int x, int y, int z)
+{
+ auto pipeline = m_currentPipeline;
+ if (!pipeline || pipeline->m_shaderProgram->m_pipelineType != PipelineType::Compute)
+ {
+ assert(!"Invalid compute pipeline");
+ return;
+ }
+
+ // Also create descriptor sets based on the given pipeline layout
+ VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer();
+
+ m_api.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->m_pipeline);
+
+ auto pipelineLayoutImpl = pipeline->m_pipelineLayout.Ptr();
+ m_api.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayoutImpl->m_pipelineLayout,
+ 0, uint32_t(pipelineLayoutImpl->m_descriptorSetCount),
+ &m_currentDescriptorSets[0],
+ 0, nullptr);
+
+ m_api.vkCmdDispatch(commandBuffer, x, y, z);
+}
+
+static VkImageViewType _calcImageViewType(TextureResource::Type type, const TextureResource::Desc& desc)
+{
+ switch (type)
+ {
+ case Resource::Type::Texture1D: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D;
+ case Resource::Type::Texture2D: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
+ case Resource::Type::TextureCube: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_CUBE_ARRAY : VK_IMAGE_VIEW_TYPE_CUBE;
+ case Resource::Type::Texture3D:
+ {
+ // Can't have an array and 3d texture
+ assert(desc.arraySize <= 1);
+ if (desc.arraySize <= 1)
+ {
+ return VK_IMAGE_VIEW_TYPE_3D;
+ }
+ break;
+ }
+ default: break;
+ }
+
+ return VK_IMAGE_VIEW_TYPE_MAX_ENUM;
+}
+
+#if 0
+BindingState* VKRenderer::createBindingState(const BindingState::Desc& bindingStateDesc)
+{
+ RefPtr<BindingStateImpl> bindingState(new BindingStateImpl(bindingStateDesc, &m_api));
+
+ const auto& srcBindings = bindingStateDesc.m_bindings;
+ const int numBindings = int(srcBindings.Count());
+
+ auto& dstDetails = bindingState->m_bindingDetails;
+ dstDetails.SetSize(numBindings);
+
+ for (int i = 0; i < numBindings; ++i)
+ {
+ auto& dstDetail = dstDetails[i];
+ const auto& srcBinding = srcBindings[i];
+
+ switch (srcBinding.bindingType)
+ {
+ case BindingType::Buffer:
+ {
+ if (!srcBinding.resource || !srcBinding.resource->isBuffer())
+ {
+ assert(!"Needs to have a buffer resource set");
+ return nullptr;
+ }
+
+ BufferResourceImpl* bufferResource = static_cast<BufferResourceImpl*>(srcBinding.resource.Ptr());
+ const BufferResource::Desc& bufferResourceDesc = bufferResource->getDesc();
+
+ if (bufferResourceDesc.bindFlags & Resource::BindFlag::UnorderedAccess)
+ {
+ // VkBufferView uav
+
+ VkBufferViewCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO };
+
+ info.format = VK_FORMAT_R32_SFLOAT;
+ // TODO:
+ // Not sure how to handle typeless?
+ if (bufferResourceDesc.elementSize == 0)
+ {
+ info.format = VK_FORMAT_R32_SFLOAT; // DXGI_FORMAT_R32_TYPELESS ?
+ }
+
+ info.buffer = bufferResource->m_buffer.m_buffer;
+ info.offset = 0;
+ info.range = bufferResourceDesc.sizeInBytes;
+
+ SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateBufferView(m_device, &info, nullptr, &dstDetail.m_uav));
+ }
+
+ // TODO: Setup views.
+ // VkImageView srv
+
+
+ break;
+ }
+ case BindingType::Sampler:
+ {
+ VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
+
+ samplerInfo.magFilter = VK_FILTER_LINEAR;
+ samplerInfo.minFilter = VK_FILTER_LINEAR;
+
+ samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+
+ samplerInfo.anisotropyEnable = VK_FALSE;
+ samplerInfo.maxAnisotropy = 1;
+
+ samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
+ samplerInfo.unnormalizedCoordinates = VK_FALSE;
+ samplerInfo.compareEnable = VK_FALSE;
+ samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
+ samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+
+ SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateSampler(m_device, &samplerInfo, nullptr, &dstDetail.m_sampler));
+
+ break;
+ }
+ case BindingType::Texture:
+ {
+ if (!srcBinding.resource || !srcBinding.resource->isTexture())
+ {
+ assert(!"Needs to have a texture resource set");
+ return nullptr;
+ }
+
+ TextureResourceImpl* textureResource = static_cast<TextureResourceImpl*>(srcBinding.resource.Ptr());
+ const TextureResource::Desc& texDesc = textureResource->getDesc();
+
+ VkImageViewType imageViewType = _calcImageViewType(textureResource->getType(), texDesc);
+ if (imageViewType == VK_IMAGE_VIEW_TYPE_MAX_ENUM)
+ {
+ assert(!"Invalid view type");
+ return nullptr;
+ }
+ const VkFormat format = VulkanUtil::getVkFormat(texDesc.format);
+ if (format == VK_FORMAT_UNDEFINED)
+ {
+ assert(!"Unhandled image format");
+ return nullptr;
+ }
+
+ // Create the image view
+
+ VkImageViewCreateInfo viewInfo = {};
+ viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewInfo.image = textureResource->m_image;
+ viewInfo.viewType = imageViewType;
+ viewInfo.format = format;
+ viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ viewInfo.subresourceRange.baseMipLevel = 0;
+ viewInfo.subresourceRange.levelCount = 1;
+ viewInfo.subresourceRange.baseArrayLayer = 0;
+ viewInfo.subresourceRange.layerCount = 1;
+
+ viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+ viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+ viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+ viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+
+ SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateImageView(m_device, &viewInfo, nullptr, &dstDetail.m_srv));
+
+ break;
+ }
+ case BindingType::CombinedTextureSampler:
+ {
+ assert(!"not implemented");
+ return nullptr;
+ }
+ }
+ }
+
+ return bindingState.detach();;
+}
+#endif
+
+static VkDescriptorType translateDescriptorType(DescriptorSlotType type)
+{
+ switch(type)
+ {
+ default:
+ return VK_DESCRIPTOR_TYPE_MAX_ENUM;
+
+#define CASE(SRC, DST) \
+ case DescriptorSlotType::SRC: return VK_DESCRIPTOR_TYPE_##DST
+
+ CASE(Sampler, SAMPLER);
+ CASE(CombinedImageSampler, COMBINED_IMAGE_SAMPLER);
+ CASE(SampledImage, SAMPLED_IMAGE);
+ CASE(StorageImage, STORAGE_IMAGE);
+ CASE(UniformTexelBuffer, UNIFORM_TEXEL_BUFFER);
+ CASE(StorageTexelBuffer, STORAGE_TEXEL_BUFFER);
+ CASE(UniformBuffer, UNIFORM_BUFFER);
+ CASE(StorageBuffer, STORAGE_BUFFER);
+ CASE(DynamicUniformBuffer, UNIFORM_BUFFER_DYNAMIC);
+ CASE(DynamicStorageBuffer, STORAGE_BUFFER_DYNAMIC);
+ CASE(InputAttachment, INPUT_ATTACHMENT);
+
+#undef CASE
+ }
+}
+
+Result VKRenderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout)
+{
+ RefPtr<DescriptorSetLayoutImpl> descriptorSetLayoutImpl = new DescriptorSetLayoutImpl(m_api);
+
+ Slang::List<VkDescriptorSetLayoutBinding> dstBindings;
+
+ uint32_t descriptorCountForTypes[VK_DESCRIPTOR_TYPE_RANGE_SIZE] = { 0, };
+
+ UInt rangeCount = desc.slotRangeCount;
+ for(UInt rr = 0; rr < rangeCount; ++rr)
+ {
+ auto& srcRange = desc.slotRanges[rr];
+
+ VkDescriptorType dstDescriptorType = translateDescriptorType(srcRange.type);
+
+ VkDescriptorSetLayoutBinding dstBinding;
+ dstBinding.binding = uint32_t(rr);
+ dstBinding.descriptorType = dstDescriptorType;
+ dstBinding.descriptorCount = uint32_t(srcRange.count);
+ dstBinding.stageFlags = VK_SHADER_STAGE_ALL;
+ dstBinding.pImmutableSamplers = nullptr;
+
+ descriptorCountForTypes[dstDescriptorType] += uint32_t(srcRange.count);
+
+ dstBindings.add(dstBinding);
+
+ DescriptorSetLayoutImpl::RangeInfo rangeInfo;
+ rangeInfo.descriptorType = dstDescriptorType;
+ descriptorSetLayoutImpl->m_ranges.add(rangeInfo);
+ }
+
+ VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
+ descriptorSetLayoutInfo.bindingCount = uint32_t(dstBindings.getCount());
+ descriptorSetLayoutInfo.pBindings = dstBindings.getBuffer();
+
+ VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
+ SLANG_VK_CHECK(m_api.vkCreateDescriptorSetLayout(m_device, &descriptorSetLayoutInfo, nullptr, &descriptorSetLayout));
+
+ // Create a pool while we are at it, to allocate descriptor sets of this type.
+
+ VkDescriptorPoolSize poolSizes[VK_DESCRIPTOR_TYPE_RANGE_SIZE];
+ uint32_t poolSizeCount = 0;
+ for (int ii = 0; ii < SLANG_COUNT_OF(descriptorCountForTypes); ++ii)
+ {
+ auto descriptorCount = descriptorCountForTypes[ii];
+ if (descriptorCount > 0)
+ {
+ poolSizes[poolSizeCount].type = VkDescriptorType(ii);
+ poolSizes[poolSizeCount].descriptorCount = descriptorCount;
+ poolSizeCount++;
+ }
+ }
+
+ VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
+ descriptorPoolInfo.maxSets = 128; // TODO: actually pick a size.
+ descriptorPoolInfo.poolSizeCount = poolSizeCount;
+ descriptorPoolInfo.pPoolSizes = &poolSizes[0];
+
+ VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
+ SLANG_VK_CHECK(m_api.vkCreateDescriptorPool(m_device, &descriptorPoolInfo, nullptr, &descriptorPool));
+
+ descriptorSetLayoutImpl->m_descriptorSetLayout = descriptorSetLayout;
+ descriptorSetLayoutImpl->m_descriptorPool = descriptorPool;
+
+ *outLayout = descriptorSetLayoutImpl.detach();
+ return SLANG_OK;
+}
+
+Result VKRenderer::createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout)
+{
+ UInt descriptorSetCount = desc.descriptorSetCount;
+
+ VkDescriptorSetLayout descriptorSetLayouts[kMaxDescriptorSets];
+ for(UInt ii = 0; ii < descriptorSetCount; ++ii)
+ {
+ descriptorSetLayouts[ii] = ((DescriptorSetLayoutImpl*) desc.descriptorSets[ii].layout)->m_descriptorSetLayout;
+ }
+
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
+ pipelineLayoutInfo.setLayoutCount = uint32_t(desc.descriptorSetCount);
+ pipelineLayoutInfo.pSetLayouts = &descriptorSetLayouts[0];
+
+ VkPipelineLayout pipelineLayout;
+ SLANG_VK_CHECK(m_api.vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &pipelineLayout));
+
+ RefPtr<PipelineLayoutImpl> pipelineLayoutImpl = new PipelineLayoutImpl(m_api);
+ pipelineLayoutImpl->m_pipelineLayout = pipelineLayout;
+ pipelineLayoutImpl->m_descriptorSetCount = descriptorSetCount;
+
+ *outLayout = pipelineLayoutImpl.detach();
+ return SLANG_OK;
+}
+
+Result VKRenderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet)
+{
+ auto layoutImpl = (DescriptorSetLayoutImpl*)layout;
+
+ VkDescriptorSetAllocateInfo descriptorSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
+ descriptorSetAllocInfo.descriptorPool = layoutImpl->m_descriptorPool;
+ descriptorSetAllocInfo.descriptorSetCount = 1;
+ descriptorSetAllocInfo.pSetLayouts = &layoutImpl->m_descriptorSetLayout;
+
+ VkDescriptorSet descriptorSet;
+ SLANG_VK_CHECK(m_api.vkAllocateDescriptorSets(m_device, &descriptorSetAllocInfo, &descriptorSet));
+
+ RefPtr<DescriptorSetImpl> descriptorSetImpl = new DescriptorSetImpl(this);
+ descriptorSetImpl->m_layout = layoutImpl;
+ descriptorSetImpl->m_descriptorSet = descriptorSet;
+ *outDescriptorSet = descriptorSetImpl.detach();
+ return SLANG_OK;
+}
+
+/* static */VKRenderer::DescriptorSetImpl::Binding::Type VKRenderer::DescriptorSetImpl::_getBindingType(RefObject* ptr)
+{
+ typedef Binding::Type Type;
+
+ if (ptr)
+ {
+ if (dynamic_cast<ResourceView*>(ptr))
+ {
+ return Type::ResourceView;
+ }
+ else if (dynamic_cast<BufferResource*>(ptr))
+ {
+ return Type::BufferResource;
+ }
+ else if (dynamic_cast<SamplerState*>(ptr))
+ {
+ return Type::SamplerState;
+ }
+ }
+ return Type::Unknown;
+}
+
+void VKRenderer::DescriptorSetImpl::_setBinding(Binding::Type type, UInt range, UInt index, RefObject* ptr)
+{
+ SLANG_ASSERT(ptr == nullptr || _getBindingType(ptr) == type);
+
+ const Index numBindings = m_bindings.getCount();
+ for (Index i = 0; i < numBindings; ++i)
+ {
+ Binding& binding = m_bindings[i];
+
+ if (binding.type == type && binding.range == uint32_t(range) && binding.index == uint32_t(index))
+ {
+ if (ptr)
+ {
+ binding.obj = ptr;
+ }
+ else
+ {
+ m_bindings.removeAt(i);
+ }
+
+ return;
+ }
+ }
+
+ // If an entry is not found, and we have a pointer, create an entry
+ if (ptr)
+ {
+ Binding binding;
+ binding.type = type;
+ binding.range = uint32_t(range);
+ binding.index = uint32_t(index);
+ binding.obj = ptr;
+
+ m_bindings.add(binding);
+ }
+}
+
+void VKRenderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer)
+{
+ auto bufferImpl = (BufferResourceImpl*)buffer;
+
+ VkDescriptorBufferInfo bufferInfo = {};
+ bufferInfo.buffer = bufferImpl->m_buffer.m_buffer;
+ bufferInfo.offset = 0;
+ bufferInfo.range = bufferImpl->getDesc().sizeInBytes;
+
+ VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
+ writeInfo.dstSet = m_descriptorSet;
+ writeInfo.dstBinding = uint32_t(range);
+ writeInfo.dstArrayElement = uint32_t(index);
+ writeInfo.descriptorCount = 1;
+ writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType;
+ writeInfo.pBufferInfo = &bufferInfo;
+
+ m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr);
+
+ _setBinding(Binding::Type::BufferResource, range, index, buffer);
+}
+
+void VKRenderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view)
+{
+ auto viewImpl = (ResourceViewImpl*)view;
+ switch (viewImpl->m_type)
+ {
+ case ResourceViewImpl::ViewType::Texture:
+ {
+ auto textureViewImpl = (TextureResourceViewImpl*)viewImpl;
+ VkDescriptorImageInfo imageInfo = {};
+ imageInfo.imageView = textureViewImpl->m_view;
+ imageInfo.imageLayout = textureViewImpl->m_layout;
+ // imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+ VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
+ writeInfo.dstSet = m_descriptorSet;
+ writeInfo.dstBinding = uint32_t(range);
+ writeInfo.dstArrayElement = uint32_t(index);
+ writeInfo.descriptorCount = 1;
+ writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType;
+ writeInfo.pImageInfo = &imageInfo;
+
+ m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr);
+ }
+ break;
+
+ case ResourceViewImpl::ViewType::TexelBuffer:
+ {
+ auto bufferViewImpl = (TexelBufferResourceViewImpl*)viewImpl;
+
+ VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
+ writeInfo.dstSet = m_descriptorSet;
+ writeInfo.dstBinding = uint32_t(range);
+ writeInfo.dstArrayElement = uint32_t(index);
+ writeInfo.descriptorCount = 1;
+ writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType;
+ writeInfo.pTexelBufferView = &bufferViewImpl->m_view;
+
+ m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr);
+ }
+ break;
+
+ case ResourceViewImpl::ViewType::PlainBuffer:
+ {
+ auto bufferViewImpl = (PlainBufferResourceViewImpl*) viewImpl;
+
+ VkDescriptorBufferInfo bufferInfo = {};
+ bufferInfo.buffer = bufferViewImpl->m_buffer->m_buffer.m_buffer;
+ bufferInfo.offset = bufferViewImpl->offset;
+ bufferInfo.range = bufferViewImpl->size;
+
+ VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
+ writeInfo.dstSet = m_descriptorSet;
+ writeInfo.dstBinding = uint32_t(range);
+ writeInfo.dstArrayElement = uint32_t(index);
+ writeInfo.descriptorCount = 1;
+ writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType;
+ writeInfo.pBufferInfo = &bufferInfo;
+
+ m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr);
+ }
+ break;
+
+ }
+
+ _setBinding(Binding::Type::ResourceView, range, index, view);
+}
+
+void VKRenderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* sampler)
+{
+
+ _setBinding(Binding::Type::SamplerState, range, index, sampler);
+}
+
+void VKRenderer::DescriptorSetImpl::setCombinedTextureSampler(
+ UInt range,
+ UInt index,
+ ResourceView* textureView,
+ SamplerState* sampler)
+{
+
+ _setBinding(Binding::Type::SamplerState, range, index, sampler);
+ _setBinding(Binding::Type::ResourceView, range, index, textureView);
+}
+
+void VKRenderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet)
+{
+ // Ideally this should eventually be as simple as:
+ //
+ // m_api.vkCmdBindDescriptorSets(
+ // commandBuffer,
+ // translatePipelineBindPoint(pipelineType),
+ // layout->m_pipelineLayout,
+ // index,
+ // 1,
+ // ((DescriptorSetImpl*) descriptorSet)->m_descriptorSet,
+ // 0,
+ // nullptr);
+ //
+ // For now we are lazily flushing state right before drawing, so
+ // we will hang onto the parameters that were passed in and then
+ // use them later.
+ //
+
+ auto descriptorSetImpl = (DescriptorSetImpl*)descriptorSet;
+ m_currentDescriptorSetImpls[index] = descriptorSetImpl;
+ m_currentDescriptorSets[index] = descriptorSetImpl->m_descriptorSet;
+}
+
+Result VKRenderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram)
+{
+ RefPtr<ShaderProgramImpl> impl = new ShaderProgramImpl(desc.pipelineType);
+ if( desc.pipelineType == PipelineType::Compute)
+ {
+ auto computeKernel = desc.findKernel(StageType::Compute);
+ impl->m_compute = compileEntryPoint(*computeKernel, VK_SHADER_STAGE_COMPUTE_BIT, impl->m_buffers[0]);
+ }
+ else
+ {
+ auto vertexKernel = desc.findKernel(StageType::Vertex);
+ auto fragmentKernel = desc.findKernel(StageType::Fragment);
+
+ impl->m_vertex = compileEntryPoint(*vertexKernel, VK_SHADER_STAGE_VERTEX_BIT, impl->m_buffers[0]);
+ impl->m_fragment = compileEntryPoint(*fragmentKernel, VK_SHADER_STAGE_FRAGMENT_BIT, impl->m_buffers[1]);
+ }
+ *outProgram = impl.detach();
+ return SLANG_OK;
+}
+
+Result VKRenderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState)
+{
+ VkPipelineCache pipelineCache = VK_NULL_HANDLE;
+
+ auto programImpl = (ShaderProgramImpl*) desc.program;
+ auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout;
+ auto inputLayoutImpl = (InputLayoutImpl*) desc.inputLayout;
+
+ const int width = int(desc.framebufferWidth);
+ const int height = int(desc.framebufferHeight);
+
+ // Shader Stages
+ //
+ // Currently only handles vertex/fragment.
+
+ static const uint32_t kMaxShaderStages = 2;
+ VkPipelineShaderStageCreateInfo shaderStages[kMaxShaderStages];
+
+ uint32_t shaderStageCount = 0;
+ shaderStages[shaderStageCount++] = programImpl->m_vertex;
+ shaderStages[shaderStageCount++] = programImpl->m_fragment;
+
+ // VertexBuffer/s
+ // Currently only handles one
+
+ VkPipelineVertexInputStateCreateInfo vertexInputInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
+ vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertexInputInfo.vertexBindingDescriptionCount = 0;
+ vertexInputInfo.vertexAttributeDescriptionCount = 0;
+
+ VkVertexInputBindingDescription vertexInputBindingDescription;
+
+ if (inputLayoutImpl)
+ {
+ vertexInputBindingDescription.binding = 0;
+ vertexInputBindingDescription.stride = inputLayoutImpl->m_vertexSize;
+ vertexInputBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+
+ const auto& srcAttributeDescs = inputLayoutImpl->m_vertexDescs;
+
+ vertexInputInfo.vertexBindingDescriptionCount = 1;
+ vertexInputInfo.pVertexBindingDescriptions = &vertexInputBindingDescription;
+
+ vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(srcAttributeDescs.getCount());
+ vertexInputInfo.pVertexAttributeDescriptions = srcAttributeDescs.getBuffer();
+ }
+
+ VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
+ inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ inputAssembly.primitiveRestartEnable = VK_FALSE;
+
+ VkViewport viewport = {};
+ viewport.x = 0.0f;
+ viewport.y = 0.0f;
+ viewport.width = (float)width;
+ viewport.height = (float)height;
+ viewport.minDepth = 0.0f;
+ viewport.maxDepth = 1.0f;
+
+ VkRect2D scissor = {};
+ scissor.offset = { 0, 0 };
+ scissor.extent = { uint32_t(width), uint32_t(height) };
+
+ VkPipelineViewportStateCreateInfo viewportState = {};
+ viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewportState.viewportCount = 1;
+ viewportState.pViewports = &viewport;
+ viewportState.scissorCount = 1;
+ viewportState.pScissors = &scissor;
+
+ VkPipelineRasterizationStateCreateInfo rasterizer = {};
+ rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rasterizer.depthClampEnable = VK_FALSE;
+ rasterizer.rasterizerDiscardEnable = VK_FALSE;
+ rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
+ rasterizer.lineWidth = 1.0f;
+ rasterizer.cullMode = VK_CULL_MODE_NONE;
+ rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
+ rasterizer.depthBiasEnable = VK_FALSE;
+
+ VkPipelineMultisampleStateCreateInfo multisampling = {};
+ multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ multisampling.sampleShadingEnable = VK_FALSE;
+ multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+
+ VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
+ colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ colorBlendAttachment.blendEnable = VK_FALSE;
+
+ VkPipelineColorBlendStateCreateInfo colorBlending = {};
+ colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ colorBlending.logicOpEnable = VK_FALSE;
+ colorBlending.logicOp = VK_LOGIC_OP_COPY;
+ colorBlending.attachmentCount = 1;
+ colorBlending.pAttachments = &colorBlendAttachment;
+ colorBlending.blendConstants[0] = 0.0f;
+ colorBlending.blendConstants[1] = 0.0f;
+ colorBlending.blendConstants[2] = 0.0f;
+ colorBlending.blendConstants[3] = 0.0f;
+
+ VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
+
+ pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+ pipelineInfo.stageCount = 2;
+ pipelineInfo.pStages = shaderStages;
+ pipelineInfo.pVertexInputState = &vertexInputInfo;
+ pipelineInfo.pInputAssemblyState = &inputAssembly;
+ pipelineInfo.pViewportState = &viewportState;
+ pipelineInfo.pRasterizationState = &rasterizer;
+ pipelineInfo.pMultisampleState = &multisampling;
+ pipelineInfo.pColorBlendState = &colorBlending;
+ pipelineInfo.layout = pipelineLayoutImpl->m_pipelineLayout;
+ pipelineInfo.renderPass = m_renderPass;
+ pipelineInfo.subpass = 0;
+ pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
+
+ VkPipeline pipeline = VK_NULL_HANDLE;
+ SLANG_VK_CHECK(m_api.vkCreateGraphicsPipelines(m_device, pipelineCache, 1, &pipelineInfo, nullptr, &pipeline));
+
+ RefPtr<PipelineStateImpl> pipelineStateImpl = new PipelineStateImpl(m_api);
+ pipelineStateImpl->m_pipeline = pipeline;
+ pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl;
+ pipelineStateImpl->m_shaderProgram = programImpl;
+ *outState = pipelineStateImpl.detach();
+ return SLANG_OK;
+}
+
+Result VKRenderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState)
+{
+ VkPipelineCache pipelineCache = VK_NULL_HANDLE;
+
+ auto programImpl = (ShaderProgramImpl*) desc.program;
+ auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout;
+
+ VkComputePipelineCreateInfo computePipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
+ computePipelineInfo.stage = programImpl->m_compute;
+ computePipelineInfo.layout = pipelineLayoutImpl->m_pipelineLayout;
+
+ VkPipeline pipeline = VK_NULL_HANDLE;
+ SLANG_VK_CHECK(m_api.vkCreateComputePipelines(m_device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline));
+
+ RefPtr<PipelineStateImpl> pipelineStateImpl = new PipelineStateImpl(m_api);
+ pipelineStateImpl->m_pipeline = pipeline;
+ pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl;
+ pipelineStateImpl->m_shaderProgram = programImpl;
+ *outState = pipelineStateImpl.detach();
+ return SLANG_OK;
+}
+
+
+#if 0
+ else if (m_currentProgram->m_pipelineType == PipelineType::Graphics)
+ {
+ // Create the graphics pipeline
+
+ const int width = m_swapChain.getWidth();
+ const int height = m_swapChain.getHeight();
+
+
+
+
+
+ //
+
+
+ }
+ else
+ {
+ assert(!"Unhandled program type");
+ return SLANG_FAIL;
+ }
+
+ pipelineOut = pipeline;
+ return SLANG_OK;
+
+
+#endif
+
+} // renderer_test