diff --git a/util/test/demos/CMakeLists.txt b/util/test/demos/CMakeLists.txt index f3ecee864..c8fdae5cc 100644 --- a/util/test/demos/CMakeLists.txt +++ b/util/test/demos/CMakeLists.txt @@ -31,6 +31,7 @@ set(VULKAN_SRC vk/vk_empty_capture.cpp vk/vk_ext_buffer_address.cpp vk/vk_extended_dyn_state.cpp + vk/vk_graphics_pipeline.cpp vk/vk_image_layouts.cpp vk/vk_imageless_framebuffer.cpp vk/vk_indirect.cpp diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index 47c4bcc07..c6c18b5c1 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -291,6 +291,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index 201746acc..1d78cd5a4 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -637,6 +637,9 @@ Vulkan\demos + + Vulkan\demos + D3D12\demos diff --git a/util/test/demos/vk/vk_graphics_pipeline.cpp b/util/test/demos/vk/vk_graphics_pipeline.cpp new file mode 100644 index 000000000..e526b5559 --- /dev/null +++ b/util/test/demos/vk/vk_graphics_pipeline.cpp @@ -0,0 +1,469 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019-2022 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_Graphics_Pipeline, VulkanGraphicsTest) +{ + static constexpr const char *Description = + "Tests the use of graphics pipelines and makes sure different features handle them."; + + const std::string vertex = R"EOSHADER( + +#version 420 core + +struct v2f +{ + vec4 pos; + vec4 col; + vec4 uv; +}; + +layout(location = 0) in vec3 Position; +layout(location = 1) in vec4 Color; +layout(location = 2) in vec2 UV; + +layout(location = 0) out v2f vertOut; + +layout(set = 0, binding = 0) uniform ubo +{ + vec2 offset; + vec2 pad; + vec4 scale; +}; + +void main() +{ + vertOut.pos = vec4(Position.xyz*vec3(1,-1,1), 1); + gl_Position = vertOut.pos; + vertOut.col = Color; +#if 0 + vertOut.col = Color.yxzw; +#endif + vertOut.uv = vec4(UV.xy + vec2(100.0f, 100.0f) + offset.xy, 0, 1) * scale; +} + +)EOSHADER"; + + 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(location = 1, index = 0) out vec4 Color1; + +layout(set = 2, binding = 0) uniform sampler2D smiley[16]; + +layout(constant_id = 1) const int spec_canary = 0; + +layout(push_constant) uniform PushData +{ + uint idx; +} push; + +void main() +{ + if(spec_canary != 1337) { Color = vec4(0.2, 0.0, 0.2, 1.0); return; } + + Color = vertIn.col * 0.5f + 0.5f * texture(smiley[push.idx], vec2(0.4f, 0.6f)); + Color1 = vec4(1.0 - vertIn.col.x, 1.0 - vertIn.col.y, 1.0 - vertIn.col.z, 1.0); +} + +)EOSHADER"; + + void Prepare(int argc, char **argv) + { + devExts.push_back(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME); + devExts.push_back(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME); + + VulkanGraphicsTest::Prepare(argc, argv); + + static VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT graphlibFeats = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT, + }; + + if(hasExt(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME)) + { + graphlibFeats.graphicsPipelineLibrary = VK_TRUE; + graphlibFeats.pNext = (void *)devInfoNext; + devInfoNext = &graphlibFeats; + } + } + + int main() + { + // initialise, create window, create context, etc + if(!Init()) + return 3; + + VkDescriptorSetLayout vsetlayout = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo({ + { + 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, + }, + })); + VkDescriptorSetLayout fsetlayout = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo({ + { + 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 16, VK_SHADER_STAGE_FRAGMENT_BIT, + }, + })); + + VkPipelineLayoutCreateFlags layoutFlags = 0; + + if(hasExt(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME)) + layoutFlags = VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT; + + VkPipelineLayout vlayout = createPipelineLayout(vkh::PipelineLayoutCreateInfo( + {vsetlayout, VK_NULL_HANDLE, VK_NULL_HANDLE}, + {vkh::PushConstantRange(VK_SHADER_STAGE_FRAGMENT_BIT, 0, 4)}, layoutFlags)); + VkPipelineLayout flayout = createPipelineLayout(vkh::PipelineLayoutCreateInfo( + {VK_NULL_HANDLE, VK_NULL_HANDLE, fsetlayout}, + {vkh::PushConstantRange(VK_SHADER_STAGE_FRAGMENT_BIT, 0, 4)}, layoutFlags)); + VkPipelineLayout fulllayout = createPipelineLayout(vkh::PipelineLayoutCreateInfo( + {vsetlayout, VK_NULL_HANDLE, fsetlayout}, + {vkh::PushConstantRange(VK_SHADER_STAGE_FRAGMENT_BIT, 0, 4)}, layoutFlags)); + + vkh::RenderPassCreator renderPassCreateInfo; + + renderPassCreateInfo.attachments.push_back(vkh::AttachmentDescription( + mainWindow->format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, + VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE)); + renderPassCreateInfo.attachments.push_back(vkh::AttachmentDescription( + VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, + VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE)); + renderPassCreateInfo.attachments.push_back(vkh::AttachmentDescription( + VK_FORMAT_D32_SFLOAT_S8_UINT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, + VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_DONT_CARE)); + + renderPassCreateInfo.addSubpass({VkAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL}), + VkAttachmentReference({1, VK_IMAGE_LAYOUT_GENERAL})}, + 2, VK_IMAGE_LAYOUT_GENERAL); + + VkRenderPass renderPass = createRenderPass(renderPassCreateInfo); + + VkGraphicsPipelineLibraryCreateInfoEXT libInfo = {}; + libInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT; + + vkh::GraphicsPipelineCreateInfo pipeCreateInfo; + + 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.flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR; + pipeCreateInfo.pNext = &libInfo; + + VkPipeline libList[4] = {}; + + libInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT; + VkGraphicsPipelineCreateInfo *info = pipeCreateInfo; + info->pTessellationState = NULL; + info->pViewportState = NULL; + info->pRasterizationState = NULL; + info->pMultisampleState = NULL; + info->pDepthStencilState = NULL; + info->pColorBlendState = NULL; + libList[0] = createGraphicsPipeline(info); + + pipeCreateInfo.vertexInputState.vertexBindingDescriptions = {}; + pipeCreateInfo.vertexInputState.vertexAttributeDescriptions = {}; + + std::vector spirv = ::CompileShaderToSpv( + vertex, SPIRVTarget::vulkan12, ShaderLang::glsl, ShaderStage::vert, "main", {}); + + VkShaderModuleCreateInfo vertShad = vkh::ShaderModuleCreateInfo(spirv); + + pipeCreateInfo.stages = { + vkh::PipelineShaderStageCreateInfo(VK_NULL_HANDLE, VK_SHADER_STAGE_VERTEX_BIT), + }; + pipeCreateInfo.stages[0].pNext = &vertShad; + + pipeCreateInfo.layout = vlayout; + pipeCreateInfo.renderPass = renderPass; + + libInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT; + info = pipeCreateInfo; + info->pTessellationState = NULL; + info->pViewportState = NULL; + info->pRasterizationState = NULL; + info->pMultisampleState = NULL; + libList[1] = createGraphicsPipeline(info); + + spirv = ::CompileShaderToSpv(pixel, SPIRVTarget::vulkan12, ShaderLang::glsl, ShaderStage::frag, + "main", {}); + + VkShaderModuleCreateInfo fragShad = vkh::ShaderModuleCreateInfo(spirv); + + pipeCreateInfo.stages = { + vkh::PipelineShaderStageCreateInfo(VK_NULL_HANDLE, VK_SHADER_STAGE_FRAGMENT_BIT), + }; + pipeCreateInfo.stages[0].pNext = &fragShad; + + VkSpecializationMapEntry specmap[1] = { + {1, 0 * sizeof(uint32_t), sizeof(uint32_t)}, + }; + + uint32_t specvals[1] = {1337}; + + VkSpecializationInfo spec = {}; + spec.mapEntryCount = ARRAY_COUNT(specmap); + spec.pMapEntries = specmap; + spec.dataSize = sizeof(specvals); + spec.pData = specvals; + + pipeCreateInfo.stages[0].pSpecializationInfo = &spec; + + pipeCreateInfo.layout = flayout; + + libInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT; + info = pipeCreateInfo; + info->pVertexInputState = NULL; + info->pInputAssemblyState = NULL; + info->pTessellationState = NULL; + info->pViewportState = NULL; + info->pDepthStencilState = NULL; + info->pColorBlendState = NULL; + libList[2] = createGraphicsPipeline(info); + + pipeCreateInfo.stages = {}; + pipeCreateInfo.layout = VK_NULL_HANDLE; + + pipeCreateInfo.colorBlendState.attachments.push_back({ + // blendEnable + VK_FALSE, + // color* + VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, VK_BLEND_OP_ADD, + // alpha* + VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, VK_BLEND_OP_ADD, + // colorWriteMask + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | + VK_COLOR_COMPONENT_A_BIT, + }); + + libInfo.pNext = NULL; + libInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT; + info = pipeCreateInfo; + info->pVertexInputState = NULL; + info->pInputAssemblyState = NULL; + info->pTessellationState = NULL; + info->pViewportState = NULL; + info->pRasterizationState = NULL; + libList[3] = createGraphicsPipeline(info); + + VkPipelineLibraryCreateInfoKHR libs = {}; + libs.sType = VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR; + libs.libraryCount = 4; + libs.pLibraries = libList; + + VkGraphicsPipelineCreateInfo linkedPipeInfo = {}; + linkedPipeInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + linkedPipeInfo.pNext = &libs; + + VkPipeline pipe = createGraphicsPipeline(&linkedPipeInfo); + + AllocatedImage offimg( + this, vkh::ImageCreateInfo(screenWidth, screenHeight, 0, VK_FORMAT_R32G32B32A32_SFLOAT, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + + VkImageView offview = createImageView(vkh::ImageViewCreateInfo( + offimg.image, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R32G32B32A32_SFLOAT)); + + AllocatedImage depthimg( + this, vkh::ImageCreateInfo(screenWidth, screenHeight, 0, VK_FORMAT_D32_SFLOAT_S8_UINT, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + + VkImageView dsvview = createImageView(vkh::ImageViewCreateInfo( + depthimg.image, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_D32_SFLOAT_S8_UINT, {}, + vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))); + + // create framebuffers using swapchain images and DS image + std::vector fbs; + fbs.resize(mainWindow->GetCount()); + + for(size_t i = 0; i < mainWindow->GetCount(); i++) + fbs[i] = createFramebuffer(vkh::FramebufferCreateInfo( + renderPass, {mainWindow->GetView(i), offview, dsvview}, mainWindow->scissor.extent)); + + Vec4f cbufferdata[2] = { + Vec4f(-100.0f, -100.0f, 0.0f, 0.0f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), + }; + + AllocatedBuffer cb( + this, vkh::BufferCreateInfo(sizeof(cbufferdata), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + cb.upload(cbufferdata); + + VkDescriptorSet vdescset = allocateDescriptorSet(vsetlayout); + + vkh::updateDescriptorSets( + device, { + vkh::WriteDescriptorSet(vdescset, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.buffer, 0)}), + }); + + 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({0, 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, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE)); + + VkDescriptorSet fdescset = allocateDescriptorSet(fsetlayout); + + uint32_t idx = 13; + + vkh::updateDescriptorSets( + device, { + vkh::WriteDescriptorSet( + fdescset, 0, idx, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + { + vkh::DescriptorImageInfo( + smileyview, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, smileysampler), + }), + }); + + AllocatedImage badimg( + this, vkh::ImageCreateInfo(4, 4, 0, VK_FORMAT_R32G32B32A32_SFLOAT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + + setName(badimg.image, "Black Tex"); + + VkImageView badimgview = createImageView(vkh::ImageViewCreateInfo( + badimg.image, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R32G32B32A32_SFLOAT)); + + for(uint32_t i = 0; i < 16; i++) + { + if(i == idx) + continue; + + vkh::updateDescriptorSets( + device, + { + vkh::WriteDescriptorSet( + fdescset, 0, i, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + { + vkh::DescriptorImageInfo(badimgview, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + smileysampler), + }), + }); + } + + { + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + vkh::cmdPipelineBarrier( + cmd, { + vkh::ImageMemoryBarrier(0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, badimg.image), + }); + + vkEndCommandBuffer(cmd); + + Submit(99, 99, {cmd}); + } + + AllocatedBuffer vb( + this, vkh::BufferCreateInfo(sizeof(DefaultTri), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + vb.upload(DefaultTri); + + while(Running()) + { + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + vkCmdBeginRenderPass(cmd, + vkh::RenderPassBeginInfo( + renderPass, fbs[mainWindow->imgIndex], mainWindow->scissor, + { + vkh::ClearValue(0.2f, 0.2f, 0.2f, 1.0f), + vkh::ClearValue(0.0f, 0.0f, 0.2f, 1.0f), vkh::ClearValue(1.0f, 0), + }), + VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); + vkCmdPushConstants(cmd, fulllayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, 4, &idx); + vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0}); + vkCmdSetViewport(cmd, 0, 1, &mainWindow->viewport); + vkCmdSetScissor(cmd, 0, 1, &mainWindow->scissor); + vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, fulllayout, 0, {vdescset}, {}); + vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, fulllayout, 2, {fdescset}, {}); + 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_helpers.cpp b/util/test/demos/vk/vk_helpers.cpp index 6bb4e53f3..c242ff5d1 100644 --- a/util/test/demos/vk/vk_helpers.cpp +++ b/util/test/demos/vk/vk_helpers.cpp @@ -244,6 +244,16 @@ const GraphicsPipelineCreateInfo &GraphicsPipelineCreateInfo::operator=( void GraphicsPipelineCreateInfo::bake() { + pVertexInputState = &vertexInputState; + pInputAssemblyState = &inputAssemblyState; + pTessellationState = &tessellationState; + pViewportState = &viewportState; + pRasterizationState = &rasterizationState; + pMultisampleState = &multisampleState; + pDepthStencilState = &depthStencilState; + pColorBlendState = &colorBlendState; + pDynamicState = &dynamicState; + stageCount = (uint32_t)stages.size(); pStages = stages.data(); diff --git a/util/test/demos/vk/vk_helpers.h b/util/test/demos/vk/vk_helpers.h index 6b428cb75..b6705a2fd 100644 --- a/util/test/demos/vk/vk_helpers.h +++ b/util/test/demos/vk/vk_helpers.h @@ -720,11 +720,12 @@ struct PushConstantRange : public VkPushConstantRange struct PipelineLayoutCreateInfo : public VkPipelineLayoutCreateInfo { PipelineLayoutCreateInfo(const std::vector &setLayouts = {}, - const std::vector &pushConstantRanges = {}) + const std::vector &pushConstantRanges = {}, + VkPipelineLayoutCreateFlags flags = 0) { sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pNext = NULL; - this->flags = 0; + this->flags = flags; this->setLayoutCount = (uint32_t)setLayouts.size(); this->pSetLayouts = setLayouts.data(); this->pushConstantRangeCount = (uint32_t)pushConstantRanges.size(); @@ -1369,6 +1370,12 @@ struct GraphicsPipelineCreateInfo : private VkGraphicsPipelineCreateInfo return (const VkGraphicsPipelineCreateInfo *)this; } + operator VkGraphicsPipelineCreateInfo *() + { + bake(); + return (VkGraphicsPipelineCreateInfo *)this; + } + private: void bake(); }; diff --git a/util/test/tests/Vulkan/VK_Graphics_Pipeline.py b/util/test/tests/Vulkan/VK_Graphics_Pipeline.py new file mode 100644 index 000000000..96f9a9b0e --- /dev/null +++ b/util/test/tests/Vulkan/VK_Graphics_Pipeline.py @@ -0,0 +1,207 @@ +import renderdoc as rd +import rdtest + + +class VK_Graphics_Pipeline(rdtest.TestCase): + demos_test_name = 'VK_Graphics_Pipeline' + + def check_capture(self): + last_action = self.get_last_action() + + self.controller.SetFrameEvent(last_action.eventId, True) + + tri_col = [0.408, 0.863, 0.182, 1.0] + + self.check_triangle(out=last_action.copyDestination, fore=tri_col) + + self.check_export(self.capture_filename) + + action = self.find_action("Draw") + + self.controller.SetFrameEvent(action.eventId, False) + + postvs_data = self.get_postvs(action, rd.MeshDataStage.VSOut, 0, action.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) + + pipe = self.controller.GetPipelineState() + + # Do a minimal reflection test. This doesn't exhaustively test all reflection data, just to make sure that + # the linked pipelines work + vsrefl = pipe.GetShaderReflection(rd.ShaderStage.Vertex) + fsrefl = pipe.GetShaderReflection(rd.ShaderStage.Fragment) + + self.check(len(vsrefl.inputSignature) == 3) + self.check(vsrefl.inputSignature[0].varName == "Position") + self.check(vsrefl.inputSignature[1].varName == "Color") + self.check(vsrefl.inputSignature[2].varName == "UV") + + self.check(len(fsrefl.readOnlyResources) == 1) + self.check(fsrefl.readOnlyResources[0].name == "smiley") + + vkpipe = self.controller.GetVulkanPipelineState() + + binding = vkpipe.graphics.descriptorSets[2].bindings[0] + + if binding.dynamicallyUsedCount != 1: + raise rdtest.TestFailureException("Bind 0 doesn't have the right used count {}" + .format(binding.dynamicallyUsedCount)) + + if not binding.binds[13].dynamicallyUsed: + raise rdtest.TestFailureException("Graphics bind 0[13] isn't dynamically used") + + trace = self.controller.DebugVertex(0, 0, 0, 0) + + if trace.debugger is None: + raise rdtest.TestFailureException("No vertex debug result") + + cycles, variables = self.process_trace(trace) + + outputs = 0 + + for var in trace.sourceVars: + var: rd.SourceVariableMapping + if var.variables[0].type == rd.DebugVariableType.Variable and var.signatureIndex >= 0: + name = var.name + + if name not in postvs_data[0].keys(): + raise rdtest.TestFailureException("Don't have expected output for {}".format(name)) + + expect = postvs_data[0][name] + value = self.evaluate_source_var(var, variables) + + if len(expect) != value.columns: + raise rdtest.TestFailureException( + "Vertex output {} has different size ({} values) to expectation ({} values)" + .format(name, action.eventId, value.columns, len(expect))) + + compType = rd.VarTypeCompType(value.type) + if compType == rd.CompType.UInt: + debugged = list(value.value.u32v[0:value.columns]) + elif compType == rd.CompType.SInt: + debugged = list(value.value.s32v[0:value.columns]) + else: + debugged = list(value.value.f32v[0:value.columns]) + + is_eq, diff_amt = rdtest.value_compare_diff(expect, debugged, eps=5.0E-06) + if not is_eq: + rdtest.log.error( + "Debugged vertex output value {}: {} difference. {} doesn't exactly match postvs output {}".format( + name, action.eventId, diff_amt, debugged, expect)) + + outputs = outputs + 1 + + rdtest.log.success('Successfully debugged vertex in {} cycles, {}/{} outputs match' + .format(cycles, outputs, len(vsrefl.outputSignature))) + + self.controller.FreeTrace(trace) + + history = self.controller.PixelHistory(pipe.GetOutputTargets()[0].resourceId, 200, 150, rd.Subresource(0, 0, 0), + rd.CompType.Typeless) + + # should be a clear then a draw + self.check(len(history) == 2) + + self.check(self.find_action('', history[0].eventId).flags & rd.ActionFlags.BeginPass) + + self.check(self.find_action('', history[1].eventId).eventId == action.eventId) + self.check(history[1].Passed()) + + if not rdtest.value_compare(history[1].shaderOut.col.floatValue, tri_col, eps=1.0/256.0): + raise rdtest.TestFailureException( + "History for drawcall output is wrong: {}".format(history[1].shaderOut.col.floatValue)) + + trace = self.controller.DebugPixel(200, 150, 0, 0) + + if trace.debugger is None: + raise rdtest.TestFailureException("No pixel debug result") + + cycles, variables = self.process_trace(trace) + + output_sourcevar = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0) + + if output_sourcevar is None: + raise rdtest.TestFailureException("Couldn't get colour output value") + + debugged = self.evaluate_source_var(output_sourcevar, variables) + + self.controller.FreeTrace(trace) + + debuggedValue = list(debugged.value.f32v[0:4]) + + is_eq, diff_amt = rdtest.value_compare_diff(history[1].shaderOut.col.floatValue, debuggedValue, eps=5.0E-06) + if not is_eq: + rdtest.log.error( + "Debugged pixel value {}: {} difference. {} doesn't exactly match history shader output {}".format( + debugged.name, diff_amt, debuggedValue, history[1].shaderOut.col.floatValue)) + + rdtest.log.success('Successfully debugged pixel in {} cycles, result matches'.format(cycles)) + + out = self.controller.CreateOutput(rd.CreateHeadlessWindowingData(100, 100), rd.ReplayOutputType.Texture) + + tex = rd.TextureDisplay() + tex.resourceId = pipe.GetOutputTargets()[0].resourceId + + tex.overlay = rd.DebugOverlay.TriangleSizeDraw + out.SetTextureDisplay(tex) + + out.Display() + + overlay_id = out.GetDebugOverlayTexID() + + self.check_pixel_value(overlay_id, 200, 150, [14992.0, 14992.0, 14992.0, 1.0]) + + rdtest.log.success("Triangle size overlay gave correct output") + + out.Shutdown() + + source = vsrefl.debugInfo.files[0].contents.replace('#if 0', '#if 1') + + newShader = self.controller.BuildTargetShader(vsrefl.entryPoint, + rd.ShaderEncoding.GLSL, + bytes(source, 'UTF-8'), + rd.ShaderCompileFlags(), + rd.ShaderStage.Vertex) + + if len(newShader[1]) != 0: + raise rdtest.TestFailureException("Failed to compile edited shader: {}".format(newShader[1])) + + self.controller.ReplaceResource(vsrefl.resourceId, newShader[0]) + + # Refresh the replay if it didn't happen already + self.controller.SetFrameEvent(last_action.eventId, True) + + tri_col2 = [0.906, 0.361, 0.182, 1.0] + self.check_triangle(out=last_action.copyDestination, fore=tri_col2) + + rdtest.log.success("Edited shader had the right triangle output") + + self.controller.RemoveReplacement(vsrefl.resourceId) + self.controller.FreeTargetResource(newShader[0])