diff --git a/util/test/demos/CMakeLists.txt b/util/test/demos/CMakeLists.txt index 828031cc9..e13d71ef1 100644 --- a/util/test/demos/CMakeLists.txt +++ b/util/test/demos/CMakeLists.txt @@ -11,6 +11,7 @@ set(VULKAN_SRC vk/vk_buffer_truncation.cpp vk/vk_cbuffer_zoo.cpp vk/vk_custom_border_color.cpp + vk/vk_dedicated_allocation.cpp vk/vk_descriptor_index.cpp vk/vk_descriptor_reuse.cpp vk/vk_descriptor_variable_count.cpp diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index 3c208c497..f66f1d3d8 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -275,6 +275,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index cd3013832..4924dccf9 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -586,6 +586,9 @@ D3D11\demos + + Vulkan\demos + diff --git a/util/test/demos/vk/vk_dedicated_allocation.cpp b/util/test/demos/vk/vk_dedicated_allocation.cpp new file mode 100644 index 000000000..7619dfad1 --- /dev/null +++ b/util/test/demos/vk/vk_dedicated_allocation.cpp @@ -0,0 +1,184 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Baldur Karlsson + * + * 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. + ******************************************************************************/ + +#include "vk_test.h" + +RD_TEST(VK_Dedicated_Allocation, VulkanGraphicsTest) +{ + static constexpr const char *Description = + "Uses dedicated memory allocations for an image and a buffer, to ensure they work and the " + "contents are preserved."; + + const std::string pixel = R"EOSHADER( +#version 460 core + +#define v2f v2f_block \ +{ \ + vec4 pos; \ + vec4 col; \ + vec4 uv; \ +} + +layout(location = 0) in v2f vertIn; + +layout(location = 0, index = 0) out vec4 Color; + +layout(binding = 0) uniform sampler2D smiley; + +void main() +{ + Color = texture(smiley, vertIn.uv.xy * 2.0f); +} + +)EOSHADER"; + + void Prepare(int argc, char **argv) + { + devExts.push_back(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME); + devExts.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); + + VulkanGraphicsTest::Prepare(argc, argv); + + if(!Avail.empty()) + return; + + // no physical device features + } + + int main() + { + vmaDedicated = true; + + // initialise, create window, create context, etc + if(!Init()) + return 3; + + VkDescriptorSetLayout setlayout = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo({ + { + 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, + }, + })); + + VkPipelineLayout layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo({setlayout})); + + vkh::GraphicsPipelineCreateInfo pipeCreateInfo; + + pipeCreateInfo.layout = layout; + pipeCreateInfo.renderPass = mainWindow->rp; + + pipeCreateInfo.vertexInputState.vertexBindingDescriptions = {vkh::vertexBind(0, DefaultA2V)}; + pipeCreateInfo.vertexInputState.vertexAttributeDescriptions = { + vkh::vertexAttr(0, 0, DefaultA2V, pos), vkh::vertexAttr(1, 0, DefaultA2V, col), + vkh::vertexAttr(2, 0, DefaultA2V, uv), + }; + + pipeCreateInfo.stages = { + CompileShaderModule(VKDefaultVertex, ShaderLang::glsl, ShaderStage::vert, "main"), + CompileShaderModule(pixel, ShaderLang::glsl, ShaderStage::frag, "main"), + }; + + VkPipeline pipe = createGraphicsPipeline(pipeCreateInfo); + + AllocatedBuffer vb( + this, vkh::BufferCreateInfo(sizeof(DefaultTri), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo( + {VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + vb.upload(DefaultTri); + + Texture rgba8; + LoadXPM(SmileyTexture, rgba8); + + AllocatedImage smiley( + this, vkh::ImageCreateInfo(rgba8.width, rgba8.height, 0, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT), + VmaAllocationCreateInfo( + {VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_GPU_ONLY})); + + VkImageView smileyview = createImageView( + vkh::ImageViewCreateInfo(smiley.image, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R8G8B8A8_UNORM)); + + AllocatedBuffer uploadBuf(this, vkh::BufferCreateInfo(rgba8.data.size() * sizeof(uint32_t), + VK_BUFFER_USAGE_TRANSFER_SRC_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + uploadBuf.upload(rgba8.data.data(), rgba8.data.size() * sizeof(uint32_t)); + + uploadBufferToImage(smiley.image, {rgba8.width, rgba8.height, 1}, uploadBuf.buffer, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + VkSampler smileysampler = createSampler(vkh::SamplerCreateInfo( + VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_LINEAR)); + + VkDescriptorSet descset = allocateDescriptorSet(setlayout); + + vkh::updateDescriptorSets( + device, { + vkh::WriteDescriptorSet( + descset, 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + {vkh::DescriptorImageInfo( + smileyview, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, smileysampler)}), + }); + + while(Running()) + { + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + VkImage swapimg = + StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + vkCmdClearColorImage(cmd, swapimg, VK_IMAGE_LAYOUT_GENERAL, + vkh::ClearColorValue(0.2f, 0.2f, 0.2f, 1.0f), 1, + vkh::ImageSubresourceRange()); + + vkCmdBeginRenderPass( + cmd, vkh::RenderPassBeginInfo(mainWindow->rp, mainWindow->GetFB(), mainWindow->scissor), + VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); + vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, {descset}, {}); + vkCmdSetViewport(cmd, 0, 1, &mainWindow->viewport); + vkCmdSetScissor(cmd, 0, 1, &mainWindow->scissor); + vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0}); + vkCmdDraw(cmd, 3, 1, 0, 0); + + vkCmdEndRenderPass(cmd); + + FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + vkEndCommandBuffer(cmd); + + Submit(0, 1, {cmd}); + + Present(); + } + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/demos/vk/vk_test.cpp b/util/test/demos/vk/vk_test.cpp index 5cbbe59d1..8fb4b68b7 100644 --- a/util/test/demos/vk/vk_test.cpp +++ b/util/test/demos/vk/vk_test.cpp @@ -650,6 +650,8 @@ bool VulkanGraphicsTest::Init() allocInfo.device = device; allocInfo.frameInUseCount = 4; allocInfo.pVulkanFunctions = &funcs; + if(hasExt(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME) && vmaDedicated) + allocInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; vmaCreateAllocator(&allocInfo, &allocator); @@ -1417,7 +1419,8 @@ void VulkanGraphicsTest::getPhysProperties2(void *nextStruct) bool VulkanGraphicsTest::hasExt(const char *ext) { - return std::find(devExts.begin(), devExts.end(), ext) != devExts.end(); + return std::find_if(devExts.begin(), devExts.end(), + [ext](const char *a) { return !strcmp(a, ext); }) != devExts.end(); } template <> diff --git a/util/test/demos/vk/vk_test.h b/util/test/demos/vk/vk_test.h index 08ef44755..c9e0b3b7a 100644 --- a/util/test/demos/vk/vk_test.h +++ b/util/test/demos/vk/vk_test.h @@ -287,6 +287,7 @@ struct VulkanGraphicsTest : public GraphicsTest VulkanWindow *mainWindow = NULL; // VMA + bool vmaDedicated = false; VmaAllocator allocator = VK_NULL_HANDLE; private: diff --git a/util/test/tests/Vulkan/VK_Dedicated_Allocation.py b/util/test/tests/Vulkan/VK_Dedicated_Allocation.py new file mode 100644 index 000000000..9793abf06 --- /dev/null +++ b/util/test/tests/Vulkan/VK_Dedicated_Allocation.py @@ -0,0 +1,55 @@ +import renderdoc as rd +import rdtest + + +class VK_Dedicated_Allocation(rdtest.TestCase): + demos_test_name = 'VK_Dedicated_Allocation' + + def check_capture(self): + draw = self.find_draw("Draw") + + self.controller.SetFrameEvent(draw.eventId, True) + + postvs_data = self.get_postvs(draw, rd.MeshDataStage.VSOut, 0, draw.numIndices) + + postvs_ref = { + 0: { + 'vtx': 0, + 'idx': 0, + 'gl_PerVertex_var.gl_Position': [-0.5, 0.5, 0.0, 1.0], + 'vertOut.pos': [-0.5, 0.5, 0.0, 1.0], + 'vertOut.col': [0.0, 1.0, 0.0, 1.0], + 'vertOut.uv': [0.0, 0.0, 0.0, 1.0], + }, + 1: { + 'vtx': 1, + 'idx': 1, + 'gl_PerVertex_var.gl_Position': [0.0, -0.5, 0.0, 1.0], + 'vertOut.pos': [0.0, -0.5, 0.0, 1.0], + 'vertOut.col': [0.0, 1.0, 0.0, 1.0], + 'vertOut.uv': [0.0, 1.0, 0.0, 1.0], + }, + 2: { + 'vtx': 2, + 'idx': 2, + 'gl_PerVertex_var.gl_Position': [0.5, 0.5, 0.0, 1.0], + 'vertOut.pos': [0.5, 0.5, 0.0, 1.0], + 'vertOut.col': [0.0, 1.0, 0.0, 1.0], + 'vertOut.uv': [1.0, 0.0, 0.0, 1.0], + }, + } + + self.check_mesh_data(postvs_ref, postvs_data) + + rdtest.log.success('Mesh data is as expected') + + pipe: rd.PipeState = self.controller.GetPipelineState() + + self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, 155, 195, [1.0, 0.0, 0.09, 1.0], eps=1.0/255.0) + self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, 190, 195, [0.0, 1.0, 0.09, 1.0], eps=1.0/255.0) + self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, 255, 195, [1.0, 0.0, 0.09, 1.0], eps=1.0/255.0) + self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, 230, 150, [0.723, 1.0, 1.0, 1.0], eps=1.0/255.0) + + self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, 190, 80, [0.2, 0.2, 0.2, 1.0], eps=1.0/255.0) + self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, 200, 80, [0.723, 1.0, 1.0, 1.0], eps=1.0/255.0) + self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, 210, 80, [0.2, 0.2, 0.2, 1.0], eps=1.0/255.0)