// render-vk.cpp #include "render-vk.h" #include "options.h" #include "render.h" #ifdef _WIN32 #define VK_USE_PLATFORM_WIN32_KHR 1 #endif #define VK_NO_PROTOTYPES #include #define ENABLE_VALIDATION_LAYER 1 #ifdef _MSC_VER #include #pragma warning(disable: 4996) #if (_MSC_VER < 1900) #define snprintf sprintf_s #endif #endif #define FOREACH_GLOBAL_PROC(M) \ M(vkCreateInstance) \ /* */ #define FOREACH_INSTANCE_PROC(M) \ M(vkCreateDevice) \ M(vkCreateDebugReportCallbackEXT) \ M(vkDestroyDebugReportCallbackEXT) \ M(vkDebugReportMessageEXT) \ M(vkEnumeratePhysicalDevices) \ M(vkGetPhysicalDeviceProperties) \ M(vkGetPhysicalDeviceFeatures) \ M(vkGetPhysicalDeviceMemoryProperties) \ M(vkGetPhysicalDeviceQueueFamilyProperties) \ M(vkGetDeviceProcAddr) \ /* */ #define FOREACH_DEVICE_PROC(M) \ M(vkCreateDescriptorPool) \ M(vkCreateCommandPool) \ M(vkGetDeviceQueue) \ M(vkAllocateCommandBuffers) \ M(vkBeginCommandBuffer) \ M(vkEndCommandBuffer) \ M(vkQueueSubmit) \ M(vkQueueWaitIdle) \ M(vkFreeCommandBuffers) \ M(vkCreateBuffer) \ M(vkGetBufferMemoryRequirements) \ M(vkAllocateMemory) \ M(vkBindBufferMemory) \ M(vkMapMemory) \ M(vkUnmapMemory) \ M(vkCmdCopyBuffer) \ M(vkDestroyBuffer) \ M(vkFreeMemory) \ M(vkCreateDescriptorSetLayout) \ M(vkAllocateDescriptorSets) \ M(vkUpdateDescriptorSets) \ M(vkCreatePipelineLayout) \ M(vkCreateComputePipelines) \ M(vkCmdBindPipeline) \ M(vkCmdBindDescriptorSets) \ M(vkCmdDispatch) \ M(vkDestroyPipeline) \ M(vkCreateShaderModule) \ /* */ namespace renderer_test { class VKRenderer : public Renderer, public ShaderCompiler { public: VkInstance instance; VkPhysicalDevice physicalDevice; VkPhysicalDeviceProperties deviceProperties; VkPhysicalDeviceFeatures deviceFeatures; VkPhysicalDeviceMemoryProperties deviceMemoryProperties; VkPhysicalDeviceFeatures enabledFeatures; VkDevice device; VkQueue queue; VkCommandPool commandPool; VkSubmitInfo submitInfo; VkDebugReportCallbackEXT debugReportCallback; #define DECLARE_PROC(NAME) PFN_##NAME NAME; DECLARE_PROC(vkGetInstanceProcAddr); FOREACH_GLOBAL_PROC(DECLARE_PROC) FOREACH_INSTANCE_PROC(DECLARE_PROC) FOREACH_DEVICE_PROC(DECLARE_PROC) #undef DECLARE_PROC // Renderer interface void checkResult(VkResult result) { assert(result == VK_SUCCESS); } VkBool32 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"; char buffer[1024]; sprintf_s(buffer, "%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 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); } virtual void initialize(void* inWindowHandle) override { char const* dynamicLibraryName = "vulkan-1.dll"; HMODULE vulkan = LoadLibraryA(dynamicLibraryName); if(!vulkan) { fprintf(stderr, "error: failed load '%s'\n", dynamicLibraryName); exit(1); } vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) GetProcAddress(vulkan, "vkGetInstanceProcAddr"); if(!vkGetInstanceProcAddr) { fprintf(stderr, "error: failed load symbol 'vkGetInstanceProcAddr'\n"); exit(1); } 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, #ifdef _WIN32 VK_KHR_WIN32_SURFACE_EXTENSION_NAME, #else #endif #if ENABLE_VALIDATION_LAYER VK_EXT_DEBUG_REPORT_EXTENSION_NAME, #endif }; VkInstanceCreateInfo instanceCreateInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; instanceCreateInfo.pApplicationInfo = &applicationInfo; instanceCreateInfo.enabledExtensionCount = sizeof(instanceExtensions) / sizeof(instanceExtensions[0]); instanceCreateInfo.ppEnabledExtensionNames = &instanceExtensions[0]; #if ENABLE_VALIDATION_LAYER uint32_t layerCount = 1; const char *layerNames[] = { "VK_LAYER_LUNARG_standard_validation" }; instanceCreateInfo.enabledLayerCount = layerCount; instanceCreateInfo.ppEnabledLayerNames = layerNames; #endif instance = 0; #define LOAD_INSTANCE_PROC(NAME) NAME = (PFN_##NAME) vkGetInstanceProcAddr(instance, #NAME); FOREACH_GLOBAL_PROC(LOAD_INSTANCE_PROC); checkResult(vkCreateInstance( &instanceCreateInfo, nullptr, &instance)); FOREACH_INSTANCE_PROC(LOAD_INSTANCE_PROC); #undef LOAD_INSTANCE_PROC #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; checkResult(vkCreateDebugReportCallbackEXT( instance, &debugCreateInfo, nullptr, &debugReportCallback)); #endif uint32_t physicalDeviceCount = 0; checkResult(vkEnumeratePhysicalDevices( instance, &physicalDeviceCount, nullptr)); VkPhysicalDevice* physicalDevices = (VkPhysicalDevice*)alloca( physicalDeviceCount * sizeof(VkPhysicalDevice)); checkResult(vkEnumeratePhysicalDevices( instance, &physicalDeviceCount, physicalDevices)); uint32_t selectedDeviceIndex = 0; // TODO: allow override of selected device physicalDevice = physicalDevices[selectedDeviceIndex]; vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties( physicalDevice, &queueFamilyCount, nullptr); VkQueueFamilyProperties* queueFamilies = (VkQueueFamilyProperties*)alloca( queueFamilyCount * sizeof(VkQueueFamilyProperties)); vkGetPhysicalDeviceQueueFamilyProperties( physicalDevice, &queueFamilyCount, queueFamilies); // Find a queue that can service our needs VkQueueFlags reqQueueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; uint32_t queueFamilyIndex = uint32_t(-1); for (uint32_t qq = 0; qq < queueFamilyCount; ++qq) { if ((queueFamilies[qq].queueFlags & reqQueueFlags) == reqQueueFlags) { queueFamilyIndex = qq; break; } } assert(queueFamilyIndex < queueFamilyCount); float queuePriority = 0.0f; VkDeviceQueueCreateInfo queueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; queueCreateInfo.queueFamilyIndex = queueFamilyIndex; queueCreateInfo.queueCount = 1; queueCreateInfo.pQueuePriorities = &queuePriority; char const* const deviceExtensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, }; VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; deviceCreateInfo.queueCreateInfoCount = 1; deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; deviceCreateInfo.pEnabledFeatures = &enabledFeatures; deviceCreateInfo.enabledExtensionCount = sizeof(deviceExtensions) / sizeof(deviceExtensions[0]); deviceCreateInfo.ppEnabledExtensionNames = &deviceExtensions[0]; checkResult(vkCreateDevice( physicalDevice, &deviceCreateInfo, nullptr, &device)); #define LOAD_DEVICE_PROC(NAME) NAME = (PFN_##NAME) vkGetDeviceProcAddr(device, #NAME); FOREACH_DEVICE_PROC(LOAD_DEVICE_PROC) #undef LOAD_DEVICE_PROC // Create a command pool VkCommandPoolCreateInfo commandPoolCreateInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex; commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; checkResult(vkCreateCommandPool( device, &commandPoolCreateInfo, nullptr, &commandPool)); vkGetDeviceQueue( device, queueFamilyIndex, 0, &queue); // set up swap chain // create command buffers // depth/stencil? // render pass? // pipeline cache // frame buffer // create semaphores for sync } float clearColor[4]; virtual void setClearColor(float const* color) override { for(int ii = 0; ii < 4; ++ii) clearColor[ii] = color[ii]; } virtual void clearFrame() override { } virtual void presentFrame() override { } virtual void captureScreenShot(char const* outputPath) override { } virtual ShaderCompiler* getShaderCompiler() override { return this; } VkCommandBuffer getCommandBuffer() { VkCommandBufferAllocateInfo info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; info.commandPool = commandPool; info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; info.commandBufferCount = 1; VkCommandBuffer commandBuffer; checkResult(vkAllocateCommandBuffers( device, &info, &commandBuffer)); return commandBuffer; } VkCommandBuffer beginCommandBuffer() { VkCommandBuffer commandBuffer = getCommandBuffer(); VkCommandBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; checkResult(vkBeginCommandBuffer(commandBuffer, &beginInfo)); return commandBuffer; } void flushCommandBuffer(VkCommandBuffer commandBuffer) { checkResult(vkEndCommandBuffer(commandBuffer)); VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &commandBuffer; checkResult(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); checkResult(vkQueueWaitIdle(queue)); vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); } struct BufferImpl { VkBuffer buffer; VkDeviceMemory memory; }; BufferImpl createBufferImpl( size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties, void const* initData = nullptr) { if( initData ) { // TODO: what if we are allocating it as CPU-writable anyway? usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; } VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; bufferCreateInfo.size = bufferSize; bufferCreateInfo.usage = usage; VkBuffer buffer; checkResult(vkCreateBuffer( device, &bufferCreateInfo, nullptr, &buffer)); VkMemoryRequirements memoryReqs = {}; vkGetBufferMemoryRequirements(device, buffer, &memoryReqs); uint32_t memoryTypeIndex = getMemoryTypeIndex( memoryReqs.memoryTypeBits, reqMemoryProperties); VkMemoryPropertyFlags actualMemoryProperites = deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags; VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; allocateInfo.allocationSize = memoryReqs.size; allocateInfo.memoryTypeIndex = memoryTypeIndex; VkDeviceMemory memory; checkResult(vkAllocateMemory( device, &allocateInfo, nullptr, &memory)); checkResult(vkBindBufferMemory( device, buffer, memory, 0)); if( initData ) { // TODO: only create staging buffer if the memory type // used for the buffer doesn't let us fill things in // directly. BufferImpl staging = createBufferImpl( bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); // Copy into staging buffer void* mappedData = nullptr; checkResult(vkMapMemory(device, staging.memory, 0, bufferSize, 0, &mappedData)); memcpy(mappedData, initData, bufferSize); vkUnmapMemory(device, staging.memory); // Copy from staging buffer to real buffer VkCommandBuffer commandBuffer = beginCommandBuffer(); VkBufferCopy copyInfo = {}; copyInfo.size = bufferSize; vkCmdCopyBuffer( commandBuffer, staging.buffer, buffer, 1, ©Info); flushCommandBuffer(commandBuffer); // Now destroy the staging buffer vkDestroyBuffer(device, staging.buffer, nullptr); vkFreeMemory(device, staging.memory, nullptr); } BufferImpl impl; impl.buffer = buffer; impl.memory = memory; return impl; } virtual Buffer* createBuffer(BufferDesc const& desc) override { size_t bufferSize = desc.size; VkBufferUsageFlags usage = 0; VkMemoryPropertyFlags reqMemoryProperties = 0; switch( desc.flavor ) { case BufferFlavor::Constant: usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; reqMemoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; break; case BufferFlavor::Vertex: usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; reqMemoryProperties = 0; break; } BufferImpl bufferImpl = createBufferImpl( bufferSize, usage, reqMemoryProperties, desc.initData); BufferImpl* bufferPtr = new BufferImpl(); *bufferPtr = bufferImpl; return (Buffer*) bufferPtr; } struct InputLayoutImpl { }; virtual InputLayout* createInputLayout(InputElementDesc const* inputElements, UInt inputElementCount) override { InputLayoutImpl* impl = new InputLayoutImpl(); // TODO: actually initialize things return (InputLayout*) impl; } virtual void* map(Buffer* buffer, MapFlavor flavor) override { return nullptr; } virtual void unmap(Buffer* buffer) override { } virtual void setInputLayout(InputLayout* inputLayout) override { } virtual void setPrimitiveTopology(PrimitiveTopology topology) override { } virtual void setVertexBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* strides, UInt const* offsets) override { } struct ShaderProgramImpl { VkPipelineShaderStageCreateInfo compute; VkPipelineShaderStageCreateInfo vertex; VkPipelineShaderStageCreateInfo fragment; }; ShaderProgramImpl* currentProgram = nullptr; virtual void setShaderProgram(ShaderProgram* program) override { currentProgram = (ShaderProgramImpl*) program; } virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) override { } virtual void draw(UInt vertexCount, UInt startVertex = 0) override { } struct BindingImpl { ShaderInputType type; InputBufferType bufferType; // Only valid if `type` is `Buffer` VkImageView srv; VkBufferView uav; VkBuffer buffer; VkSampler samplerState; int binding = 0; bool isOutput = false; int bufferLength = 0; }; struct BindingStateImpl { Slang::List bindings; int numRenderTargets; }; uint32_t getMemoryTypeIndex( uint32_t inTypeBits, VkMemoryPropertyFlags properties) { uint32_t typeBits = inTypeBits; uint32_t typeIndex = 0; while( typeBits ) { if((deviceMemoryProperties.memoryTypes[typeIndex].propertyFlags & properties) == properties) { return typeIndex; } typeIndex++; typeBits >>= 1; } assert(!"failed to find a usable memory type"); return uint32_t(-1); } void createInputBuffer( ShaderInputLayoutEntry const& entry, InputBufferDesc const& bufferDesc, Slang::List const& bufferData, VkBuffer &bufferOut, VkBufferView &uavOut, VkImageView &srvOut) { size_t bufferSize = bufferData.Count() * sizeof(unsigned int); void const* initData = bufferData.Buffer(); VkBufferUsageFlags usage = 0; VkMemoryPropertyFlags reqMemoryProperties = 0; switch( bufferDesc.type ) { case InputBufferType::ConstantBuffer: usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; reqMemoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; break; case InputBufferType::StorageBuffer: usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; reqMemoryProperties = 0; break; } // If we are going to read back from the buffer, be sure to request // the required access. if(entry.isOutput) { usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; } BufferImpl bufferImpl = createBufferImpl( bufferSize, usage, reqMemoryProperties, initData); // TODO: need to hang onto the `memory` field so // that we can release it when we are done. bufferOut = bufferImpl.buffer; // Fill in any views needed switch( bufferDesc.type ) { case InputBufferType::ConstantBuffer: break; case InputBufferType::StorageBuffer: { } break; } } void createInputTexture( InputTextureDesc const& inputDesc, VkImageView& viewOut) { TextureData texData; generateTextureData(texData, inputDesc); assert(!"unimplemented"); } void createInputSampler( InputSamplerDesc const& inputDesc, VkSampler& stateOut) { assert(!"unimplemented"); } virtual BindingState* createBindingState(const ShaderInputLayout & layout) { BindingStateImpl* bindingState = new BindingStateImpl(); bindingState->numRenderTargets = layout.numRenderTargets; for (auto & entry : layout.entries) { BindingImpl binding; binding.type = entry.type; binding.binding = entry.hlslBinding; binding.isOutput = entry.isOutput; switch (entry.type) { case ShaderInputType::Buffer: { createInputBuffer(entry, entry.bufferDesc, entry.bufferData, binding.buffer, binding.uav, binding.srv); binding.bufferLength = (int)(entry.bufferData.Count() * sizeof(unsigned int)); binding.bufferType = entry.bufferDesc.type; } break; case ShaderInputType::Texture: { createInputTexture(entry.textureDesc, binding.srv); } break; case ShaderInputType::Sampler: { createInputSampler(entry.samplerDesc, binding.samplerState); } break; case ShaderInputType::CombinedTextureSampler: { throw "not implemented"; } break; } bindingState->bindings.Add(binding); } return (BindingState*) bindingState; } BindingStateImpl* currentBindingState = nullptr; virtual void setBindingState(BindingState * state) { currentBindingState = (BindingStateImpl*) state; } virtual void serializeOutput(BindingState* s, const char * fileName) { auto state = (BindingStateImpl*) s; FILE * f = fopen(fileName, "wb"); int id = 0; for (auto& bb: state->bindings) { if (bb.isOutput) { if (bb.buffer) { // create staging buffer size_t bufferSize = bb.bufferLength; BufferImpl staging = createBufferImpl( 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 = beginCommandBuffer(); VkBufferCopy copyInfo = {}; copyInfo.size = bufferSize; vkCmdCopyBuffer( commandBuffer, bb.buffer, staging.buffer, 1, ©Info); flushCommandBuffer(commandBuffer); // Write out the data from the buffer void* mappedData = nullptr; checkResult(vkMapMemory(device, staging.memory, 0, bufferSize, 0, &mappedData)); auto ptr = (unsigned int *) mappedData; for (auto i = 0u; i < bufferSize / sizeof(unsigned int); i++) fprintf(f, "%X\n", ptr[i]); vkUnmapMemory(device, staging.memory); // Now destroy the staging buffer vkDestroyBuffer(device, staging.buffer, nullptr); vkFreeMemory(device, staging.memory, nullptr); } else { printf("invalid output type at %d.\n", id); } } id++; } fclose(f); } virtual void dispatchCompute(int x, int y, int z) override { // HACK: create a new pipeline for every call // First create a pipeline layout based on what is bound Slang::List bindings; for( auto bb : currentBindingState->bindings ) { switch( bb.type ) { case ShaderInputType::Buffer: { switch(bb.bufferType) { case InputBufferType::StorageBuffer: { VkDescriptorSetLayoutBinding binding = {}; binding.binding = bb.binding; binding.descriptorCount = 1; binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; binding.stageFlags = VK_SHADER_STAGE_ALL; bindings.Add(binding); } break; default: // handle other cases break; } } break; default: // TODO: handle the other cases break; } } VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; descriptorSetLayoutInfo.bindingCount = uint32_t(bindings.Count()); descriptorSetLayoutInfo.pBindings = bindings.Buffer(); VkDescriptorSetLayout descriptorSetLayout = 0; checkResult(vkCreateDescriptorSetLayout( device, &descriptorSetLayoutInfo, nullptr, &descriptorSetLayout)); // Create a descriptor pool for allocating sets VkDescriptorPoolSize poolSizes[] = { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 128 }, { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 128 }, { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 128 }, }; VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; descriptorPoolInfo.maxSets = 128; // TODO: actually pick a size descriptorPoolInfo.poolSizeCount = sizeof(poolSizes) / sizeof(poolSizes[0]); descriptorPoolInfo.pPoolSizes = poolSizes; VkDescriptorPool descriptorPool; checkResult(vkCreateDescriptorPool( device, &descriptorPoolInfo, nullptr, &descriptorPool)); // Create a descriptor set based on our layout VkDescriptorSetAllocateInfo descriptorSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; descriptorSetAllocInfo.descriptorPool = descriptorPool; descriptorSetAllocInfo.descriptorSetCount = 1; descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayout; VkDescriptorSet descriptorSet; checkResult(vkAllocateDescriptorSets( device, &descriptorSetAllocInfo, &descriptorSet)); // Fill in the descritpor set, using our binding information for( auto bb : currentBindingState->bindings ) { switch( bb.type ) { case ShaderInputType::Buffer: { switch(bb.bufferType) { case InputBufferType::StorageBuffer: { VkDescriptorBufferInfo bufferInfo; bufferInfo.buffer = bb.buffer; bufferInfo.offset = 0; bufferInfo.range = bb.bufferLength; VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; writeInfo.descriptorCount = 1; writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; writeInfo.dstSet = descriptorSet; writeInfo.dstBinding = bb.binding; writeInfo.dstArrayElement = 0; writeInfo.pBufferInfo = &bufferInfo; vkUpdateDescriptorSets( device, 1, &writeInfo, 0, nullptr); } break; default: // handle other cases break; } } break; default: // TODO: handle the other cases break; } } // Create a pipeline layout based on our descriptor set layout(s) VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; pipelineLayoutInfo.setLayoutCount = 1; pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; VkPipelineLayout pipelineLayout = 0; checkResult(vkCreatePipelineLayout( device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); // Then create a pipeline to use that layout VkComputePipelineCreateInfo computePipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; computePipelineInfo.stage = currentProgram->compute; computePipelineInfo.layout = pipelineLayout; VkPipelineCache pipelineCache = 0; VkPipeline pipeline; checkResult(vkCreateComputePipelines( device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline)); // Also create descriptor sets based on the given pipeline layout VkCommandBuffer commandBuffer = beginCommandBuffer(); vkCmdBindPipeline( commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); vkCmdBindDescriptorSets( commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); vkCmdDispatch(commandBuffer, x, y, z); flushCommandBuffer(commandBuffer); vkDestroyPipeline(device, pipeline, nullptr); // TODO: need to free up the other resources too... } // ShaderCompiler interface VkPipelineShaderStageCreateInfo compileEntryPoint( ShaderCompileRequest::EntryPoint const& entryPointRequest, VkShaderStageFlagBits stage) { char const* dataBegin = entryPointRequest.source.dataBegin; char const* dataEnd = entryPointRequest.source.dataEnd; // 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; char* codeBegin = (char*) malloc(codeSize); memcpy(codeBegin, dataBegin, codeSize); VkShaderModuleCreateInfo moduleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; moduleCreateInfo.pCode = (uint32_t*) codeBegin; moduleCreateInfo.codeSize = codeSize; VkShaderModule module; checkResult(vkCreateShaderModule( 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; } virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) override { ShaderProgramImpl* impl = new ShaderProgramImpl(); if( request.computeShader.name ) { impl->compute = compileEntryPoint( request.computeShader, VK_SHADER_STAGE_COMPUTE_BIT); } else { impl->vertex = compileEntryPoint( request.vertexShader, VK_SHADER_STAGE_VERTEX_BIT); impl->fragment = compileEntryPoint( request.fragmentShader, VK_SHADER_STAGE_FRAGMENT_BIT); } return (ShaderProgram*) impl; } }; Renderer* createVKRenderer() { return new VKRenderer(); } } // renderer_test