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)