Wrote test for sparse image binding with testing actual content - function BaseImage::TestContent. It uses vkCopyBufferToImage and then a compute shader to read back pixels of the image.
diff --git a/bin/SparseBindingTest.comp.spv b/bin/SparseBindingTest.comp.spv
new file mode 100644
index 0000000..d56e0a9
--- /dev/null
+++ b/bin/SparseBindingTest.comp.spv
Binary files differ
diff --git a/src/Shaders/CompileShaders.bat b/src/Shaders/CompileShaders.bat
index b0f4a65..5d9d815 100644
--- a/src/Shaders/CompileShaders.bat
+++ b/src/Shaders/CompileShaders.bat
@@ -1,3 +1,4 @@
%VULKAN_SDK%/Bin32/glslangValidator.exe -V -o ../../bin/Shader.vert.spv Shader.vert
%VULKAN_SDK%/Bin32/glslangValidator.exe -V -o ../../bin/Shader.frag.spv Shader.frag
+%VULKAN_SDK%/Bin32/glslangValidator.exe -V -o ../../bin/SparseBindingTest.comp.spv SparseBindingTest.comp
pause
diff --git a/src/Shaders/SparseBindingTest.comp b/src/Shaders/SparseBindingTest.comp
new file mode 100644
index 0000000..21c41ab
--- /dev/null
+++ b/src/Shaders/SparseBindingTest.comp
@@ -0,0 +1,44 @@
+//
+// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
+
+layout(binding=0) uniform sampler2D img;
+layout(binding=1) buffer buf
+{
+ uint bufValues[];
+};
+
+void main()
+{
+ ivec2 xy = ivec2(bufValues[gl_GlobalInvocationID.x * 3],
+ bufValues[gl_GlobalInvocationID.x * 3 + 1]);
+ vec4 color = texture(img, xy);
+ bufValues[gl_GlobalInvocationID.x * 3 + 2] =
+ uint(color.r * 255.0) << 24 |
+ uint(color.g * 255.0) << 16 |
+ uint(color.b * 255.0) << 8 |
+ uint(color.a * 255.0);
+}
diff --git a/src/SparseBindingTest.cpp b/src/SparseBindingTest.cpp
index 661399e..b1fc0cb 100644
--- a/src/SparseBindingTest.cpp
+++ b/src/SparseBindingTest.cpp
@@ -12,8 +12,12 @@
extern bool g_SparseBindingEnabled;
extern VkQueue g_hSparseBindingQueue;
extern VkFence g_ImmediateFence;
+extern VkCommandBuffer g_hTemporaryCommandBuffer;
+void BeginSingleTimeCommands();
+void EndSingleTimeCommands();
void SaveAllocatorStatsToFile(const wchar_t* filePath);
+void LoadShader(std::vector<char>& out, const char* fileName);
////////////////////////////////////////////////////////////////////////////////
// Class definitions
@@ -24,10 +28,17 @@
virtual void Init(RandomNumberGenerator& rand) = 0;
virtual ~BaseImage();
+ const VkImageCreateInfo& GetCreateInfo() const { return m_CreateInfo; }
+
+ void TestContent(RandomNumberGenerator& rand);
+
protected:
+ VkImageCreateInfo m_CreateInfo = {};
VkImage m_Image = VK_NULL_HANDLE;
- void FillImageCreateInfo(VkImageCreateInfo& outInfo, RandomNumberGenerator& rand);
+ void FillImageCreateInfo(RandomNumberGenerator& rand);
+ void UploadContent();
+ void ValidateContent(RandomNumberGenerator& rand);
};
class TraditionalImage : public BaseImage
@@ -61,25 +72,324 @@
}
}
-void BaseImage::FillImageCreateInfo(VkImageCreateInfo& outInfo, RandomNumberGenerator& rand)
+void BaseImage::TestContent(RandomNumberGenerator& rand)
+{
+ printf("Validating content of %u x %u texture...\n",
+ m_CreateInfo.extent.width, m_CreateInfo.extent.height);
+ UploadContent();
+ ValidateContent(rand);
+}
+
+void BaseImage::FillImageCreateInfo(RandomNumberGenerator& rand)
{
constexpr uint32_t imageSizeMin = 8;
constexpr uint32_t imageSizeMax = 2048;
- ZeroMemory(&outInfo, sizeof(outInfo));
- outInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
- outInfo.imageType = VK_IMAGE_TYPE_2D;
- outInfo.extent.width = rand.Generate() % (imageSizeMax - imageSizeMin) + imageSizeMin;
- outInfo.extent.height = rand.Generate() % (imageSizeMax - imageSizeMin) + imageSizeMin;
- outInfo.extent.depth = 1;
- outInfo.mipLevels = 1; // TODO ?
- outInfo.arrayLayers = 1;
- outInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
- outInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
- outInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- outInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
- outInfo.samples = VK_SAMPLE_COUNT_1_BIT;
- outInfo.flags = 0;
+ ZeroMemory(&m_CreateInfo, sizeof(m_CreateInfo));
+ m_CreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ m_CreateInfo.imageType = VK_IMAGE_TYPE_2D;
+ m_CreateInfo.extent.width = rand.Generate() % (imageSizeMax - imageSizeMin) + imageSizeMin;
+ m_CreateInfo.extent.height = rand.Generate() % (imageSizeMax - imageSizeMin) + imageSizeMin;
+ m_CreateInfo.extent.depth = 1;
+ m_CreateInfo.mipLevels = 1; // TODO ?
+ m_CreateInfo.arrayLayers = 1;
+ m_CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
+ m_CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+ m_CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ m_CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+ m_CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+ m_CreateInfo.flags = 0;
+}
+
+void BaseImage::UploadContent()
+{
+ VkBufferCreateInfo srcBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+ srcBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+ srcBufCreateInfo.size = 4 * m_CreateInfo.extent.width * m_CreateInfo.extent.height;
+
+ VmaAllocationCreateInfo srcBufAllocCreateInfo = {};
+ srcBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
+ srcBufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
+
+ VkBuffer srcBuf = nullptr;
+ VmaAllocation srcBufAlloc = nullptr;
+ VmaAllocationInfo srcAllocInfo = {};
+ TEST( vmaCreateBuffer(g_hAllocator, &srcBufCreateInfo, &srcBufAllocCreateInfo, &srcBuf, &srcBufAlloc, &srcAllocInfo) == VK_SUCCESS );
+
+ // Fill texels with: r = x % 255, g = u % 255, b = 13, a = 25
+ uint32_t* srcBufPtr = (uint32_t*)srcAllocInfo.pMappedData;
+ for(uint32_t y = 0, sizeY = m_CreateInfo.extent.height; y < sizeY; ++y)
+ {
+ for(uint32_t x = 0, sizeX = m_CreateInfo.extent.width; x < sizeX; ++x, ++srcBufPtr)
+ {
+ const uint8_t r = (uint8_t)x;
+ const uint8_t g = (uint8_t)y;
+ const uint8_t b = 13;
+ const uint8_t a = 25;
+ *srcBufPtr = (uint32_t)r << 24 | (uint32_t)g << 16 |
+ (uint32_t)b << 8 | (uint32_t)a;
+ }
+ }
+
+ BeginSingleTimeCommands();
+
+ // Barrier undefined to transfer dst.
+ {
+ VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
+ barrier.srcAccessMask = 0;
+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.image = m_Image;
+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.baseMipLevel = 0;
+ barrier.subresourceRange.layerCount = 1;
+ barrier.subresourceRange.levelCount = 1;
+
+ vkCmdPipelineBarrier(g_hTemporaryCommandBuffer,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // srcStageMask
+ VK_PIPELINE_STAGE_TRANSFER_BIT, // dstStageMask
+ 0, // dependencyFlags
+ 0, nullptr, // memoryBarriers
+ 0, nullptr, // bufferMemoryBarriers
+ 1, &barrier); // imageMemoryBarriers
+ }
+
+ // CopyBufferToImage
+ {
+ VkBufferImageCopy region = {};
+ region.bufferOffset = 0;
+ region.bufferRowLength = 0; // Zeros mean tightly packed.
+ region.bufferImageHeight = 0; // Zeros mean tightly packed.
+ region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.imageSubresource.mipLevel = 0;
+ region.imageSubresource.baseArrayLayer = 0;
+ region.imageSubresource.layerCount = 1;
+ region.imageOffset = { 0, 0, 0 };
+ region.imageExtent = m_CreateInfo.extent;
+ vkCmdCopyBufferToImage(g_hTemporaryCommandBuffer, srcBuf, m_Image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
+ }
+
+ // Barrier transfer dst to fragment shader read only.
+ {
+ VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+ barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.image = m_Image;
+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.baseMipLevel = 0;
+ barrier.subresourceRange.layerCount = 1;
+ barrier.subresourceRange.levelCount = 1;
+
+ vkCmdPipelineBarrier(g_hTemporaryCommandBuffer,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, // srcStageMask
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, // dstStageMask
+ 0, // dependencyFlags
+ 0, nullptr, // memoryBarriers
+ 0, nullptr, // bufferMemoryBarriers
+ 1, &barrier); // imageMemoryBarriers
+ }
+
+ EndSingleTimeCommands();
+
+ vmaDestroyBuffer(g_hAllocator, srcBuf, srcBufAlloc);
+}
+
+void BaseImage::ValidateContent(RandomNumberGenerator& rand)
+{
+ /*
+ dstBuf has following layout:
+ For each of texels to be sampled, [0..valueCount):
+ struct {
+ in uint32_t pixelX;
+ in uint32_t pixelY;
+ out uint32_t pixelColor;
+ }
+ */
+
+ const uint32_t valueCount = 32;
+
+ VkBufferCreateInfo dstBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+ dstBufCreateInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
+ dstBufCreateInfo.size = valueCount * sizeof(uint32_t) * 3;
+
+ VmaAllocationCreateInfo dstBufAllocCreateInfo = {};
+ dstBufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
+ dstBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
+
+ VkBuffer dstBuf = nullptr;
+ VmaAllocation dstBufAlloc = nullptr;
+ VmaAllocationInfo dstBufAllocInfo = {};
+ TEST( vmaCreateBuffer(g_hAllocator, &dstBufCreateInfo, &dstBufAllocCreateInfo, &dstBuf, &dstBufAlloc, &dstBufAllocInfo) == VK_SUCCESS );
+
+ // Fill dstBuf input data.
+ {
+ uint32_t* dstBufContent = (uint32_t*)dstBufAllocInfo.pMappedData;
+ for(uint32_t i = 0; i < valueCount; ++i)
+ {
+ const uint32_t x = rand.Generate() % m_CreateInfo.extent.width;
+ const uint32_t y = rand.Generate() % m_CreateInfo.extent.height;
+ dstBufContent[i * 3 ] = x;
+ dstBufContent[i * 3 + 1] = y;
+ dstBufContent[i * 3 + 2] = 0;
+ }
+ }
+
+ VkSamplerCreateInfo samplerCreateInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
+ samplerCreateInfo.magFilter = VK_FILTER_NEAREST;
+ samplerCreateInfo.minFilter = VK_FILTER_NEAREST;
+ samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
+ samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerCreateInfo.unnormalizedCoordinates = VK_TRUE;
+
+ VkSampler sampler = nullptr;
+ TEST( vkCreateSampler( g_hDevice, &samplerCreateInfo, nullptr, &sampler) == VK_SUCCESS );
+
+ VkDescriptorSetLayoutBinding bindings[2] = {};
+ bindings[0].binding = 0;
+ bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ bindings[0].descriptorCount = 1;
+ bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+ bindings[0].pImmutableSamplers = &sampler;
+ bindings[1].binding = 1;
+ bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ bindings[1].descriptorCount = 1;
+ bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+
+ VkDescriptorSetLayoutCreateInfo descSetLayoutCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
+ descSetLayoutCreateInfo.bindingCount = 2;
+ descSetLayoutCreateInfo.pBindings = bindings;
+
+ VkDescriptorSetLayout descSetLayout = nullptr;
+ TEST( vkCreateDescriptorSetLayout(g_hDevice, &descSetLayoutCreateInfo, nullptr, &descSetLayout) == VK_SUCCESS );
+
+ VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
+ pipelineLayoutCreateInfo.setLayoutCount = 1;
+ pipelineLayoutCreateInfo.pSetLayouts = &descSetLayout;
+
+ VkPipelineLayout pipelineLayout = nullptr;
+ TEST( vkCreatePipelineLayout(g_hDevice, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout) == VK_SUCCESS );
+
+ std::vector<char> shaderCode;
+ LoadShader(shaderCode, "SparseBindingTest.comp.spv");
+
+ VkShaderModuleCreateInfo shaderModuleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
+ shaderModuleCreateInfo.codeSize = shaderCode.size();
+ shaderModuleCreateInfo.pCode = (const uint32_t*)shaderCode.data();
+
+ VkShaderModule shaderModule = nullptr;
+ TEST( vkCreateShaderModule(g_hDevice, &shaderModuleCreateInfo, nullptr, &shaderModule) == VK_SUCCESS );
+
+ VkComputePipelineCreateInfo pipelineCreateInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
+ pipelineCreateInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ pipelineCreateInfo.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
+ pipelineCreateInfo.stage.module = shaderModule;
+ pipelineCreateInfo.stage.pName = "main";
+ pipelineCreateInfo.layout = pipelineLayout;
+
+ VkPipeline pipeline = nullptr;
+ TEST( vkCreateComputePipelines(g_hDevice, nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline) == VK_SUCCESS );
+
+ VkDescriptorPoolSize poolSizes[2] = {};
+ poolSizes[0].type = bindings[0].descriptorType;
+ poolSizes[0].descriptorCount = bindings[0].descriptorCount;
+ poolSizes[1].type = bindings[1].descriptorType;
+ poolSizes[1].descriptorCount = bindings[1].descriptorCount;
+
+ VkDescriptorPoolCreateInfo descPoolCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
+ descPoolCreateInfo.maxSets = 1;
+ descPoolCreateInfo.poolSizeCount = 2;
+ descPoolCreateInfo.pPoolSizes = poolSizes;
+
+ VkDescriptorPool descPool = nullptr;
+ TEST( vkCreateDescriptorPool(g_hDevice, &descPoolCreateInfo, nullptr, &descPool) == VK_SUCCESS );
+
+ VkDescriptorSetAllocateInfo descSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
+ descSetAllocInfo.descriptorPool = descPool;
+ descSetAllocInfo.descriptorSetCount = 1;
+ descSetAllocInfo.pSetLayouts = &descSetLayout;
+
+ VkDescriptorSet descSet = nullptr;
+ TEST( vkAllocateDescriptorSets(g_hDevice, &descSetAllocInfo, &descSet) == VK_SUCCESS );
+
+ VkImageViewCreateInfo imageViewCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
+ imageViewCreateInfo.image = m_Image;
+ imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ imageViewCreateInfo.format = m_CreateInfo.format;
+ imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ imageViewCreateInfo.subresourceRange.layerCount = 1;
+ imageViewCreateInfo.subresourceRange.levelCount = 1;
+
+ VkImageView imageView = nullptr;
+ TEST( vkCreateImageView(g_hDevice, &imageViewCreateInfo, nullptr, &imageView) == VK_SUCCESS );
+
+ VkDescriptorImageInfo descImageInfo = {};
+ descImageInfo.imageView = imageView;
+ descImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+ VkDescriptorBufferInfo descBufferInfo = {};
+ descBufferInfo.buffer = dstBuf;
+ descBufferInfo.offset = 0;
+ descBufferInfo.range = VK_WHOLE_SIZE;
+
+ VkWriteDescriptorSet descWrites[2] = {};
+ descWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descWrites[0].dstSet = descSet;
+ descWrites[0].dstBinding = bindings[0].binding;
+ descWrites[0].dstArrayElement = 0;
+ descWrites[0].descriptorCount = 1;
+ descWrites[0].descriptorType = bindings[0].descriptorType;
+ descWrites[0].pImageInfo = &descImageInfo;
+ descWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descWrites[1].dstSet = descSet;
+ descWrites[1].dstBinding = bindings[1].binding;
+ descWrites[1].dstArrayElement = 0;
+ descWrites[1].descriptorCount = 1;
+ descWrites[1].descriptorType = bindings[1].descriptorType;
+ descWrites[1].pBufferInfo = &descBufferInfo;
+ vkUpdateDescriptorSets(g_hDevice, 2, descWrites, 0, nullptr);
+
+ BeginSingleTimeCommands();
+ vkCmdBindPipeline(g_hTemporaryCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
+ vkCmdBindDescriptorSets(g_hTemporaryCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, &descSet, 0, nullptr);
+ vkCmdDispatch(g_hTemporaryCommandBuffer, valueCount, 1, 1);
+ EndSingleTimeCommands();
+
+ // Validate dstBuf output data.
+ {
+ const uint32_t* dstBufContent = (const uint32_t*)dstBufAllocInfo.pMappedData;
+ for(uint32_t i = 0; i < valueCount; ++i)
+ {
+ const uint32_t x = dstBufContent[i * 3 ];
+ const uint32_t y = dstBufContent[i * 3 + 1];
+ const uint32_t color = dstBufContent[i * 3 + 2];
+ const uint8_t a = (uint8_t)(color >> 24);
+ const uint8_t b = (uint8_t)(color >> 16);
+ const uint8_t g = (uint8_t)(color >> 8);
+ const uint8_t r = (uint8_t)color;
+ TEST(r == (uint8_t)x && g == (uint8_t)y && b == 13 && a == 25);
+ }
+ }
+
+ vkDestroyImageView(g_hDevice, imageView, nullptr);
+ vkDestroyDescriptorPool(g_hDevice, descPool, nullptr);
+ vmaDestroyBuffer(g_hAllocator, dstBuf, dstBufAlloc);
+ vkDestroyPipeline(g_hDevice, pipeline, nullptr);
+ vkDestroyShaderModule(g_hDevice, shaderModule, nullptr);
+ vkDestroyPipelineLayout(g_hDevice, pipelineLayout, nullptr);
+ vkDestroyDescriptorSetLayout(g_hDevice, descSetLayout, nullptr);
+ vkDestroySampler(g_hDevice, sampler, nullptr);
}
////////////////////////////////////////////////////////////////////////////////
@@ -87,15 +397,14 @@
void TraditionalImage::Init(RandomNumberGenerator& rand)
{
- VkImageCreateInfo imageCreateInfo;
- FillImageCreateInfo(imageCreateInfo, rand);
+ FillImageCreateInfo(rand);
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
// Default BEST_FIT is clearly better.
//allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT;
- ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &imageCreateInfo, &allocCreateInfo,
+ ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &m_CreateInfo, &allocCreateInfo,
&m_Image, &m_Allocation, nullptr) );
}
@@ -115,10 +424,9 @@
assert(g_SparseBindingEnabled && g_hSparseBindingQueue);
// Create image.
- VkImageCreateInfo imageCreateInfo;
- FillImageCreateInfo(imageCreateInfo, rand);
- imageCreateInfo.flags |= VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
- ERR_GUARD_VULKAN( vkCreateImage(g_hDevice, &imageCreateInfo, nullptr, &m_Image) );
+ FillImageCreateInfo(rand);
+ m_CreateInfo.flags |= VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
+ ERR_GUARD_VULKAN( vkCreateImage(g_hDevice, &m_CreateInfo, nullptr, &m_Image) );
// Get memory requirements.
VkMemoryRequirements imageMemReq;
@@ -126,6 +434,7 @@
// This is just to silence validation layer warning.
// But it doesn't help. Looks like a bug in Vulkan validation layers.
+ // See: https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/364
uint32_t sparseMemReqCount = 0;
vkGetImageSparseMemoryRequirements(g_hDevice, m_Image, &sparseMemReqCount, nullptr);
TEST(sparseMemReqCount <= 8);
@@ -224,6 +533,21 @@
SaveAllocatorStatsToFile(L"SparseBindingTest.json");
+ // Choose biggest image. Test uploading and sampling.
+ BaseImage* biggestImage = nullptr;
+ for(size_t i = 0, count = images.size(); i < count; ++i)
+ {
+ if(!biggestImage ||
+ images[i].image->GetCreateInfo().extent.width * images[i].image->GetCreateInfo().extent.height >
+ biggestImage->GetCreateInfo().extent.width * biggestImage->GetCreateInfo().extent.height)
+ {
+ biggestImage = images[i].image.get();
+ }
+ }
+ assert(biggestImage);
+
+ biggestImage->TestContent(rand);
+
// Free remaining images.
images.clear();
}
diff --git a/src/VulkanSample.cpp b/src/VulkanSample.cpp
index 0e5cae6..ed79925 100644
--- a/src/VulkanSample.cpp
+++ b/src/VulkanSample.cpp
@@ -149,7 +149,7 @@
ERR_GUARD_VULKAN( vkQueueWaitIdle(g_hGraphicsQueue) );
}
-static void LoadShader(std::vector<char>& out, const char* fileName)
+void LoadShader(std::vector<char>& out, const char* fileName)
{
std::ifstream file(std::string(SHADER_PATH1) + fileName, std::ios::ate | std::ios::binary);
if(file.is_open() == false)
@@ -1222,8 +1222,9 @@
{
if(queueFamilies[i].queueCount > 0)
{
+ const uint32_t flagsForGraphicsQueue = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT;
if((g_GraphicsQueueFamilyIndex != 0) &&
- ((queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0))
+ ((queueFamilies[i].queueFlags & flagsForGraphicsQueue) == flagsForGraphicsQueue))
{
g_GraphicsQueueFamilyIndex = i;
}
@@ -1785,6 +1786,23 @@
printf("ERROR: %s\n", ex.what());
}
break;
+ case 'S':
+ try
+ {
+ if(g_SparseBindingEnabled)
+ {
+ TestSparseBinding();
+ }
+ else
+ {
+ printf("Sparse binding not supported.\n");
+ }
+ }
+ catch(const std::exception& ex)
+ {
+ printf("ERROR: %s\n", ex.what());
+ }
+ break;
}
return 0;