diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2018-05-29 16:48:04 -0400 |
|---|---|---|
| committer | Tim Foley <tfoleyNV@users.noreply.github.com> | 2018-05-29 13:48:04 -0700 |
| commit | 8b67c7b3fc163156a02a40430f7038ab2f199924 (patch) | |
| tree | 3444b632f787b6655c71385333074e22c4f5e5c0 /tools/render-test/render-vk.cpp | |
| parent | e7a83323bfc4dd698ef491375a37c65c83915951 (diff) | |
Feature/vulkan texture (#579)
* First pass at support for textures in vulkan.
* Binding state has first pass support for VkImageView VkSampler.
* Split out _calcImageViewType
* Fix bug in debug build around constant buffer being added but not part of the binding description for the test.
* Offset recalculated for vk texture construction just store the texture size for each mip level.
* When outputing a vector type with a size of 1 in GLSL, it needs to be output as the underlying type. For example vector<float,1> should be output as float in GLSL.
* Vulkan render-test produces right output for the test
tests/compute/textureSamplingTest.slang -slang -gcompute -o tests/compute/textureSamplingTest.slang.actual.txt -vk
* Small improvement around xml encoding a string.
* More generalized test synthesis.
* Fix image usage flags for Vulkan.
* Improvements to what gets synthesized vulkan tests.
* Do transition on all mip levels.
* Fixing problems appearing from vulkan debug layer.
* Disable Vulkan synthesized tests for now.
Diffstat (limited to 'tools/render-test/render-vk.cpp')
| -rw-r--r-- | tools/render-test/render-vk.cpp | 764 |
1 files changed, 685 insertions, 79 deletions
diff --git a/tools/render-test/render-vk.cpp b/tools/render-test/render-vk.cpp index cc011a297..ba517be68 100644 --- a/tools/render-test/render-vk.cpp +++ b/tools/render-test/render-vk.cpp @@ -11,6 +11,8 @@ #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/ @@ -122,6 +124,40 @@ public: 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(Type type, const Desc& desc, Usage initialUsage, const VulkanApi* api) : + Parent(type, 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 ShaderProgramImpl: public ShaderProgram { public: @@ -141,9 +177,9 @@ public: struct BindingDetail { - VkImageView m_srv; - VkBufferView m_uav; - VkSampler m_samplerState; + VkImageView m_srv = VK_NULL_HANDLE; + VkBufferView m_uav = VK_NULL_HANDLE; + VkSampler m_sampler = VK_NULL_HANDLE; int m_binding = 0; }; @@ -152,13 +188,32 @@ public: public: typedef BindingState Parent; - BindingStateImpl(const Desc& desc, VKRenderer* renderer): + BindingStateImpl(const Desc& desc, const VulkanApi* api): Parent(desc), - m_renderer(renderer) + 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); + } + } + } - VKRenderer* m_renderer; + const VulkanApi* m_api; List<BindingDetail> m_bindingDetails; }; @@ -227,6 +282,7 @@ public: Slang::Result _beginPass(); void _endPass(); + void _transitionImageLayout(VkImage image, VkFormat format, const TextureResource::Desc& desc, VkImageLayout oldLayout, VkImageLayout newLayout); VkDebugReportCallbackEXT m_debugReportCallback; @@ -348,12 +404,21 @@ Slang::Result VKRenderer::_createPipeline(RefPtr<Pipeline>& pipelineOut) const int numBindings = int(srcBindings.Count()); + int numBuffers = 0; + int numImages = 0; + + int numDescriptorByType[VK_DESCRIPTOR_TYPE_RANGE_SIZE] = { 0, }; + Slang::List<VkDescriptorSetLayoutBinding> dstBindings; for (int i = 0; i < numBindings; ++i) { const auto& srcDetail = srcDetails[i]; const auto& srcBinding = srcBindings[i]; + VkDescriptorSetLayoutBinding dstBinding = {}; + dstBinding.binding = srcDetail.m_binding; + dstBinding.descriptorCount = 1; + switch (srcBinding.bindingType) { case BindingType::Buffer: @@ -363,65 +428,122 @@ Slang::Result VKRenderer::_createPipeline(RefPtr<Pipeline>& pipelineOut) if (bufferResourceDesc.bindFlags & Resource::BindFlag::UnorderedAccess) { - VkDescriptorSetLayoutBinding dstBinding = {}; - dstBinding.binding = srcDetail.m_binding; - dstBinding.descriptorCount = 1; dstBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; dstBinding.stageFlags = VK_SHADER_STAGE_ALL; - dstBindings.Add(dstBinding); + + numDescriptorByType[dstBinding.descriptorType] ++; + numBuffers++; } else if (bufferResourceDesc.bindFlags & Resource::BindFlag::ConstantBuffer) { - VkDescriptorSetLayoutBinding dstBinding = {}; - dstBinding.binding = srcDetail.m_binding; - dstBinding.descriptorCount = 1; dstBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; dstBinding.stageFlags = VK_SHADER_STAGE_ALL; - dstBindings.Add(dstBinding); + + numDescriptorByType[dstBinding.descriptorType] ++; + numBuffers++; } + break; + } + case BindingType::Texture: + { + dstBinding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + dstBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + dstBindings.Add(dstBinding); + numDescriptorByType[dstBinding.descriptorType] ++; + numImages++; break; } - default: - // TODO: handle the other cases + case BindingType::Sampler: + { + dstBinding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + dstBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + dstBindings.Add(dstBinding); + + numDescriptorByType[dstBinding.descriptorType] ++; + numImages++; break; - } - } + } - VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; - descriptorSetLayoutInfo.bindingCount = uint32_t(dstBindings.Count()); - descriptorSetLayoutInfo.pBindings = dstBindings.Buffer(); + case BindingType::CombinedTextureSampler: + { + dstBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + dstBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + dstBindings.Add(dstBinding); - SLANG_VK_CHECK(m_api.vkCreateDescriptorSetLayout(m_device, &descriptorSetLayoutInfo, nullptr, &pipeline->m_descriptorSetLayout)); + numDescriptorByType[dstBinding.descriptorType] ++; + numImages++; + break; + } + default: + { + assert(!"Unhandled type"); + return SLANG_FAIL; + } + } + } // 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 }, +#if 0 + VkDescriptorPoolSize poolSizes[] = + { + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 128 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 128 }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 128 }, }; +#endif - VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; - descriptorPoolInfo.maxSets = 128; // TODO: actually pick a size - descriptorPoolInfo.poolSizeCount = SLANG_COUNT_OF(poolSizes); - descriptorPoolInfo.pPoolSizes = poolSizes; + List<VkDescriptorPoolSize> poolSizes; + for (int i = 0; i < SLANG_COUNT_OF(numDescriptorByType); ++i) + { + int numDescriptors = numDescriptorByType[i]; + if (numDescriptors > 0) + { + const VkDescriptorPoolSize poolSize = { VkDescriptorType(i), uint32_t(numDescriptors) }; + poolSizes.Add(poolSize); + } + } + VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; + + descriptorPoolInfo.maxSets = 128; // TODO: actually pick a size. + descriptorPoolInfo.poolSizeCount = uint32_t(poolSizes.Count()); + descriptorPoolInfo.pPoolSizes = poolSizes.Buffer(); - SLANG_VK_CHECK(m_api.vkCreateDescriptorPool(m_device, &descriptorPoolInfo, nullptr, &pipeline->m_descriptorPool)); + SLANG_VK_CHECK(m_api.vkCreateDescriptorPool(m_device, &descriptorPoolInfo, nullptr, &pipeline->m_descriptorPool)); + } - // Create a descriptor set based on our layout + // Create the layout + { + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + descriptorSetLayoutInfo.bindingCount = uint32_t(dstBindings.Count()); + descriptorSetLayoutInfo.pBindings = dstBindings.Buffer(); - VkDescriptorSetAllocateInfo descriptorSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; - descriptorSetAllocInfo.descriptorPool = pipeline->m_descriptorPool; - descriptorSetAllocInfo.descriptorSetCount = 1; - descriptorSetAllocInfo.pSetLayouts = &pipeline->m_descriptorSetLayout; + SLANG_VK_CHECK(m_api.vkCreateDescriptorSetLayout(m_device, &descriptorSetLayoutInfo, nullptr, &pipeline->m_descriptorSetLayout)); + } + + // Create a descriptor set based on our layout + { + VkDescriptorSetAllocateInfo descriptorSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; + descriptorSetAllocInfo.descriptorPool = pipeline->m_descriptorPool; + descriptorSetAllocInfo.descriptorSetCount = 1; + descriptorSetAllocInfo.pSetLayouts = &pipeline->m_descriptorSetLayout; - SLANG_VK_CHECK(m_api.vkAllocateDescriptorSets(m_device, &descriptorSetAllocInfo, &pipeline->m_descriptorSet)); + SLANG_VK_CHECK(m_api.vkAllocateDescriptorSets(m_device, &descriptorSetAllocInfo, &pipeline->m_descriptorSet)); + } // Fill in the descriptor set, using our binding information + List<VkDescriptorImageInfo> imageInfos; + List<VkDescriptorBufferInfo> bufferInfos; + List<VkWriteDescriptorSet> writes; + + // Make sure there is enough space... + imageInfos.Reserve(numImages); + bufferInfos.Reserve(numBuffers); + int elementIndex = 0; for (int i = 0; i < numBindings; ++i) @@ -429,21 +551,29 @@ Slang::Result VKRenderer::_createPipeline(RefPtr<Pipeline>& pipelineOut) const auto& srcDetail = srcDetails[i]; const auto& srcBinding = srcBindings[i]; + VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + writeInfo.descriptorCount = 1; + writeInfo.dstSet = pipeline->m_descriptorSet; + writeInfo.dstBinding = srcDetail.m_binding; + writeInfo.dstArrayElement = 0; + switch (srcBinding.bindingType) { case BindingType::Buffer: { + assert(srcBinding.resource && srcBinding.resource->isBuffer()); BufferResourceImpl* bufferResource = static_cast<BufferResourceImpl*>(srcBinding.resource.Ptr()); const BufferResource::Desc& bufferResourceDesc = bufferResource->getDesc(); - VkDescriptorBufferInfo bufferInfo; - bufferInfo.buffer = bufferResource->m_buffer.m_buffer; - bufferInfo.offset = 0; - bufferInfo.range = bufferResourceDesc.sizeInBytes; + { + VkDescriptorBufferInfo bufferInfo; + bufferInfo.buffer = bufferResource->m_buffer.m_buffer; + bufferInfo.offset = 0; + bufferInfo.range = bufferResourceDesc.sizeInBytes; + + bufferInfos.Add(bufferInfo); + } - VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; - writeInfo.descriptorCount = 1; - writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; if (bufferResource->m_initialUsage == Resource::Usage::UnorderedAccess) { @@ -454,23 +584,62 @@ Slang::Result VKRenderer::_createPipeline(RefPtr<Pipeline>& pipelineOut) writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; } - writeInfo.dstSet = pipeline->m_descriptorSet; - writeInfo.dstBinding = srcDetail.m_binding; - writeInfo.dstArrayElement = 0; - writeInfo.pBufferInfo = &bufferInfo; + writeInfo.pBufferInfo = &bufferInfos.Last(); + + writes.Add(writeInfo); + break; + } + case BindingType::Texture: + { + assert(srcBinding.resource && srcBinding.resource->isTexture()); + + TextureResourceImpl* textureResource = static_cast<TextureResourceImpl*>(srcBinding.resource.Ptr()); + const TextureResource::Desc& textureResourceDesc = textureResource->getDesc(); + + { + VkDescriptorImageInfo imageInfo = {}; + imageInfo.imageView = srcDetail.m_srv; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfos.Add(imageInfo); + } + + writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + writeInfo.pImageInfo = &imageInfos.Last(); - m_api.vkUpdateDescriptorSets(m_device, 1, &writeInfo, 0, nullptr); - + writes.Add(writeInfo); break; } - default: + case BindingType::Sampler: { - // handle other cases + { + VkDescriptorImageInfo imageInfo = {}; + imageInfo.sampler = srcDetail.m_sampler; + //imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfos.Add(imageInfo); + } + + writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + writeInfo.pImageInfo = &imageInfos.Last(); + + writes.Add(writeInfo); break; } + default: + { + assert(!"Binding not currently handled"); + return SLANG_FAIL; + } } } + assert(imageInfos.Count() == numImages); + assert(bufferInfos.Count() == numBuffers); + + // Write into the descriptor set + { + m_api.vkUpdateDescriptorSets(m_device, uint32_t(writes.Count()), writes.Buffer(), 0, nullptr); + } + // Create a pipeline layout based on our descriptor set layout(s) VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; @@ -723,8 +892,15 @@ VkBool32 VKRenderer::handleDebugMessage(VkDebugReportFlagsEXT flags, VkDebugRepo if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) severity = "error"; - char buffer[1024]; + // 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.SetSize(bufferSize); + char* buffer = bufferArray.Buffer(); + sprintf_s(buffer, + bufferSize, "%s: %s %d: %s\n", pLayerPrefix, severity, @@ -1020,12 +1196,7 @@ ShaderCompiler* VKRenderer::getShaderCompiler() return this; } -TextureResource* VKRenderer::createTextureResource(Resource::Type type, Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData) -{ - return nullptr; -} - -static VkBufferUsageFlagBits _calcUsageFlagBit(Resource::BindFlag::Enum bind) +static VkBufferUsageFlagBits _calcBufferUsageFlags(Resource::BindFlag::Enum bind) { typedef Resource::BindFlag BindFlag; @@ -1041,48 +1212,363 @@ static VkBufferUsageFlagBits _calcUsageFlagBit(Resource::BindFlag::Enum bind) assert(!"Not supported yet"); return VkBufferUsageFlagBits(0); } - case BindFlag::UnorderedAccess: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + 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 _calcUsageFlagBit(int bindFlags) +static VkBufferUsageFlagBits _calcBufferUsageFlags(int bindFlags) { int dstFlags = 0; while (bindFlags) { int lsb = bindFlags & -bindFlags; - dstFlags |= _calcUsageFlagBit(Resource::BindFlag::Enum(lsb)); + dstFlags |= _calcBufferUsageFlags(Resource::BindFlag::Enum(lsb)); bindFlags &= ~lsb; } return VkBufferUsageFlagBits(dstFlags); } -BufferResource* VKRenderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData) +static VkBufferUsageFlags _calcBufferUsageFlags(int bindFlags, int cpuAccessFlags, const void* initData) { - BufferResource::Desc desc(descIn); - if (desc.bindFlags == 0) + VkBufferUsageFlags usage = _calcBufferUsageFlags(bindFlags); + + if (cpuAccessFlags & Resource::AccessFlag::Read) { - desc.bindFlags = Resource::s_requiredBinding[int(initialUsage)]; + // 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; } - const size_t bufferSize = desc.sizeInBytes; + return usage; +} - VkBufferUsageFlags usage = _calcUsageFlagBit(desc.bindFlags); - VkMemoryPropertyFlags reqMemoryProperties = 0; +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 (desc.cpuAccessFlags & Resource::AccessFlag::Read) + if (cpuAccessFlags & Resource::AccessFlag::Read) { // If it can be read from, set this - usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; } - if ((desc.cpuAccessFlags & Resource::AccessFlag::Write) || initData) + if ((cpuAccessFlags & Resource::AccessFlag::Write) || initData) { - usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + 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); +} + +TextureResource* VKRenderer::createTextureResource(Resource::Type type, Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData) +{ + TextureResource::Desc desc(descIn); + desc.setDefaults(type, initialUsage); + + const VkFormat format = VulkanUtil::getVkFormat(desc.format); + if (format == VK_FORMAT_UNDEFINED) + { + assert(!"Unhandled image format"); + return nullptr; + } + + const int arraySize = desc.calcEffectiveArraySize(type); + + RefPtr<TextureResourceImpl> texture(new TextureResourceImpl(type, desc, initialUsage, &m_api)); + + // Create the image + { + VkImageCreateInfo imageInfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; + + switch (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 nullptr; + } + } + + 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_NULL_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_NULL_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_NULL_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.Count() == 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 (int j = 0; j < int(mipSizes.Count()); ++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 (int j = 0; j < int(mipSizes.Count()); ++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 = 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, ®ion); + + // 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(); + } + + return texture.detach(); +} + +BufferResource* VKRenderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData) +{ + 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: @@ -1347,9 +1833,33 @@ void VKRenderer::dispatchCompute(int x, int y, int z) 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; +} + + BindingState* VKRenderer::createBindingState(const BindingState::Desc& bindingStateDesc) { - RefPtr<BindingStateImpl> bindingState(new BindingStateImpl(bindingStateDesc, this)); + RefPtr<BindingStateImpl> bindingState(new BindingStateImpl(bindingStateDesc, &m_api)); const auto& srcBindings = bindingStateDesc.m_bindings; const int numBindings = int(srcBindings.Count()); @@ -1369,20 +1879,116 @@ BindingState* VKRenderer::createBindingState(const BindingState::Desc& bindingSt { case BindingType::Buffer: { - assert(srcBinding.resource && srcBinding.resource->isBuffer()); - //BufferResourceImpl* bufferResource = static_cast<BufferResourceImpl*>(srcBinding.resource.Ptr()); - //const BufferResource::Desc& bufferResourceDesc = bufferResource->getDesc(); + 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. - // VkBufferView uav // VkImageView srv + + break; } - case BindingType::Texture: 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; } } } |
