From cce4b79f32ffaea4d74de12bb978e406155a5fdf Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 27 Dec 2025 16:53:48 +0800 Subject: [PATCH 1/9] Making the use of push constants a bit more sophisticated: at a minimum, read colors from push constants instead of getting them from the VS. Also pass structs to a function. --- .../ConvertUBOToPushConstantsTestVk.cpp | 861 ++++++++++++++++++ 1 file changed, 861 insertions(+) create mode 100644 Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp diff --git a/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp b/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp new file mode 100644 index 0000000000..92fed9efc1 --- /dev/null +++ b/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp @@ -0,0 +1,861 @@ +/* + * Copyright 2019-2025 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "Vulkan/TestingEnvironmentVk.hpp" +#include "Vulkan/TestingSwapChainVk.hpp" + +#include "DeviceContextVk.h" +#include "RenderDeviceVk.h" +#include "TextureVk.h" + + +#include "GLSLangUtils.hpp" +#include "DXCompiler.hpp" +#include "SPIRVTools.hpp" + +#include "volk.h" + +#include "gtest/gtest.h" + +namespace Diligent +{ + +VkFormat TexFormatToVkFormat(TEXTURE_FORMAT TexFmt); + +namespace Testing +{ + +// Forward declaration of reference renderer +void RenderDrawCommandReferenceVk(ISwapChain* pSwapChain, const float* pClearColor); + +namespace +{ + +class VkConvertUBOToPushConstantsTest : public ::testing::Test +{ +public: + static std::unique_ptr DXCompiler; + +protected: + static void SetUpTestSuite() + { + GLSLangUtils::InitializeGlslang(); + + DXCompiler = CreateDXCompiler(DXCompilerTarget::Vulkan, 0, nullptr); + } + + static void TearDownTestSuite() + { + GLSLangUtils::FinalizeGlslang(); + + DXCompiler.reset(); + } +}; + +std::unique_ptr VkConvertUBOToPushConstantsTest::DXCompiler; + +// GLSL Vertex Shader - procedural two triangles, outputs vertex index for color lookup +const std::string GLSL_ProceduralTriangleVS = R"( +#version 450 core + +layout(location = 0) out flat int out_VertexIndex; + +void main() +{ + vec4 Pos[6]; + Pos[0] = vec4(-1.0, -0.5, 0.0, 1.0); + Pos[1] = vec4(-0.5, +0.5, 0.0, 1.0); + Pos[2] = vec4( 0.0, -0.5, 0.0, 1.0); + + Pos[3] = vec4(+0.0, -0.5, 0.0, 1.0); + Pos[4] = vec4(+0.5, +0.5, 0.0, 1.0); + Pos[5] = vec4(+1.0, -0.5, 0.0, 1.0); + + gl_Position = Pos[gl_VertexIndex]; + out_VertexIndex = gl_VertexIndex; +} +)"; + +// GLSL Fragment Shader with UBO - will be patched to push constants +// Tests: +// 1. Nested structs for multiple access chains and storage class propagation +// 2. Reading colors from push constants instead of VS input +// 3. Passing struct to function (tests function inlining before conversion) +const std::string GLSL_FragmentShaderWithUBO = R"( +#version 450 core + +// Deeply nested structs to test multiple access chains and storage class propagation +struct Level3Data +{ + vec4 Factor; +}; + +struct Level2Data +{ + Level3Data Inner; +}; + +struct Level1Data +{ + Level2Data Nested; +}; + +// Colors stored in push constants - tests reading colors from push constants +struct ColorData +{ + vec3 Colors[6]; +}; + +// UBO named "CB1" with instance name "cb" - allows testing both name matching paths +layout(set = 0, binding = 0) uniform CB1 +{ + Level1Data Data; + ColorData VertexColors; +} cb; + +layout(location = 0) in flat int in_VertexIndex; +layout(location = 0) out vec4 out_Color; + +// Helper function that takes UBO struct as parameter +// This tests OpFunctionCall handling - requires function inlining before conversion +vec4 GetNestedFactor(Level1Data data) +{ + return data.Nested.Inner.Factor; +} + +// Another helper that accesses colors from the UBO +vec3 GetVertexColor(ColorData colors, int index) +{ + return colors.Colors[index]; +} + +void main() +{ + // Access deeply nested member through function call + // This generates OpFunctionCall which requires inlining before storage class propagation + vec4 factor = GetNestedFactor(cb.Data); + + // Get color from push constants using vertex index + vec3 color = GetVertexColor(cb.VertexColors, in_VertexIndex); + + out_Color = vec4(color, 1.0) * factor; +} +)"; + +// Push constant data structure matching the UBO layout +// Must match the layout of CB1 in the shader: +// - Level1Data Data (containing nested Factor vec4) +// - ColorData VertexColors (containing Colors[6] vec3 array) +struct PushConstantData +{ + float Factor[4]; // vec4 Factor in Level1Data.Nested.Inner + float Colors[6][4]; // vec3 Colors[6] with padding (std140 layout: vec3 padded to vec4) +}; + +// HLSL Vertex Shader - procedural two triangles, outputs vertex index for color lookup +const std::string HLSL_ProceduralTriangleVS = R"( +struct PSInput +{ + float4 Pos : SV_POSITION; + uint VertexIndex : TEXCOORD0; +}; + +PSInput main(uint VertexId : SV_VertexID) +{ + float4 Pos[6]; + Pos[0] = float4(-1.0, -0.5, 0.0, 1.0); + Pos[1] = float4(-0.5, +0.5, 0.0, 1.0); + Pos[2] = float4( 0.0, -0.5, 0.0, 1.0); + + Pos[3] = float4(+0.0, -0.5, 0.0, 1.0); + Pos[4] = float4(+0.5, +0.5, 0.0, 1.0); + Pos[5] = float4(+1.0, -0.5, 0.0, 1.0); + + PSInput Out; + Out.Pos = Pos[VertexId]; + Out.VertexIndex = VertexId; + return Out; +} +)"; + +// HLSL Fragment Shader with constant buffer - will be patched to push constants +// Tests: +// 1. Deeply nested structs for multiple access chains +// 2. Reading colors from constant buffer instead of VS input +// 3. Passing struct to function (tests function inlining before conversion) +const std::string HLSL_FragmentShaderWithCB = R"( +// Deeply nested structs to test multiple access chains +struct Level3Data +{ + float4 Factor; +}; + +struct Level2Data +{ + Level3Data Inner; +}; + +struct Level1Data +{ + Level2Data Nested; +}; + +// Colors stored in constant buffer - tests reading colors from push constants +struct ColorData +{ + float3 Colors[6]; +}; + +// Constant buffer named "CB1" +cbuffer CB1 : register(b0) +{ + Level1Data Data; + ColorData VertexColors; +}; + +struct PSInput +{ + float4 Pos : SV_POSITION; + uint VertexIndex : TEXCOORD0; +}; + +// Helper function that takes CB struct as parameter +// This tests OpFunctionCall handling - requires function inlining before conversion +float4 GetNestedFactor(Level1Data data) +{ + return data.Nested.Inner.Factor; +} + +// Another helper that accesses colors from the CB +float3 GetVertexColor(ColorData colors, uint index) +{ + return colors.Colors[index]; +} + +float4 main(PSInput In) : SV_Target +{ + // Access deeply nested member through function call + // This generates OpFunctionCall which requires inlining before storage class propagation + float4 factor = GetNestedFactor(Data); + + // Get color from constant buffer using vertex index + float3 color = GetVertexColor(VertexColors, In.VertexIndex); + + return float4(color, 1.0) * factor; +} +)"; + +// Helper to create VkShaderModule from SPIR-V bytecode +VkShaderModule CreateVkShaderModuleFromSPIRV(VkDevice vkDevice, const std::vector& SPIRV) +{ + VkShaderModuleCreateInfo ShaderModuleCI = {}; + ShaderModuleCI.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + ShaderModuleCI.pNext = nullptr; + ShaderModuleCI.flags = 0; + ShaderModuleCI.codeSize = SPIRV.size() * sizeof(uint32_t); + ShaderModuleCI.pCode = SPIRV.data(); + + VkShaderModule vkShaderModule = VK_NULL_HANDLE; + VkResult res = vkCreateShaderModule(vkDevice, &ShaderModuleCI, nullptr, &vkShaderModule); + VERIFY_EXPR(res == VK_SUCCESS); + (void)res; + + return vkShaderModule; +} + +std::vector LoadSPIRVFromHLSL(const std::string& ShaderSource, + SHADER_TYPE ShaderType, + SHADER_COMPILER Compiler = SHADER_COMPILER_DEFAULT) +{ + ShaderCreateInfo ShaderCI; + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_HLSL; + ShaderCI.Source = ShaderSource.data(); + ShaderCI.SourceLength = ShaderSource.size(); + ShaderCI.Desc = {"SPIRV test shader", ShaderType}; + ShaderCI.EntryPoint = "main"; + + std::vector SPIRV; + + if (Compiler == SHADER_COMPILER_DXC) + { + if (!VkConvertUBOToPushConstantsTest::DXCompiler || !VkConvertUBOToPushConstantsTest::DXCompiler->IsLoaded()) + { + UNEXPECTED("Test should be skipped if DXCompiler is not available"); + return {}; + } + + RefCntAutoPtr pCompilerOutput; + VkConvertUBOToPushConstantsTest::DXCompiler->Compile(ShaderCI, ShaderVersion{6, 0}, nullptr, nullptr, &SPIRV, &pCompilerOutput); + + if (pCompilerOutput && pCompilerOutput->GetSize() > 0) + { + const char* CompilerOutput = static_cast(pCompilerOutput->GetConstDataPtr()); + if (*CompilerOutput != 0) + LOG_INFO_MESSAGE("DXC compiler output:\n", CompilerOutput); + } + } + else + { + SPIRV = GLSLangUtils::HLSLtoSPIRV(ShaderCI, GLSLangUtils::SpirvVersion::Vk100, nullptr, nullptr); + } + + return SPIRV; +} + +std::vector LoadSPIRVFromGLSL(const std::string& ShaderSource, SHADER_TYPE ShaderType = SHADER_TYPE_PIXEL) +{ + GLSLangUtils::GLSLtoSPIRVAttribs Attribs; + Attribs.ShaderType = ShaderType; + Attribs.ShaderSource = ShaderSource.data(); + Attribs.SourceCodeLen = static_cast(ShaderSource.size()); + Attribs.Version = GLSLangUtils::SpirvVersion::Vk100; + Attribs.AssignBindings = true; + + return GLSLangUtils::GLSLtoSPIRV(Attribs); +} + +void CompileSPIRV(const std::string& ShaderSource, + const std::string& ShaderIdentifier, + SHADER_COMPILER Compiler, + SHADER_TYPE ShaderType, + SHADER_SOURCE_LANGUAGE SourceLanguage, + std::vector& SPIRV) +{ + if (Compiler == SHADER_COMPILER_DXC) + { + VERIFY(SourceLanguage == SHADER_SOURCE_LANGUAGE_HLSL, "DXC only supports HLSL"); + if (!VkConvertUBOToPushConstantsTest::DXCompiler || !VkConvertUBOToPushConstantsTest::DXCompiler->IsLoaded()) + { + GTEST_SKIP() << "DXC compiler is not available"; + } + } + + SPIRV = (SourceLanguage == SHADER_SOURCE_LANGUAGE_GLSL) ? + LoadSPIRVFromGLSL(ShaderSource, ShaderType) : + LoadSPIRVFromHLSL(ShaderSource, ShaderType, Compiler); + ASSERT_FALSE(SPIRV.empty()) << "Failed to compile shader " << ShaderIdentifier; +} + +// Renderer that uses patched push constants shader +class PatchedPushConstantsRenderer +{ +public: + PatchedPushConstantsRenderer(TestingSwapChainVk* pSwapChain, + const std::vector& VS_SPIRV, + const std::vector& FS_SPIRV, + uint32_t PushConstantSize, + VkShaderStageFlags PushConstantStages = VK_SHADER_STAGE_FRAGMENT_BIT) + { + m_pSwapChain = pSwapChain; + + auto* pEnv = TestingEnvironmentVk::GetInstance(); + m_vkDevice = pEnv->GetVkDevice(); + + const auto& SCDesc = pSwapChain->GetDesc(); + + CreateRenderPass(); + + // Create shader modules from SPIR-V + m_vkVSModule = CreateVkShaderModuleFromSPIRV(m_vkDevice, VS_SPIRV); + VERIFY_EXPR(m_vkVSModule != VK_NULL_HANDLE); + + m_vkFSModule = CreateVkShaderModuleFromSPIRV(m_vkDevice, FS_SPIRV); + VERIFY_EXPR(m_vkFSModule != VK_NULL_HANDLE); + + // Pipeline layout with push constants (no descriptor sets) + VkPushConstantRange PushConstantRange = {}; + PushConstantRange.stageFlags = PushConstantStages; + PushConstantRange.offset = 0; + PushConstantRange.size = PushConstantSize; + + VkPipelineLayoutCreateInfo PipelineLayoutCI = {}; + PipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + PipelineLayoutCI.setLayoutCount = 0; + PipelineLayoutCI.pSetLayouts = nullptr; + PipelineLayoutCI.pushConstantRangeCount = 1; + PipelineLayoutCI.pPushConstantRanges = &PushConstantRange; + + VkResult res = vkCreatePipelineLayout(m_vkDevice, &PipelineLayoutCI, nullptr, &m_vkLayout); + VERIFY_EXPR(res == VK_SUCCESS); + (void)res; + + // Create graphics pipeline + VkGraphicsPipelineCreateInfo PipelineCI = {}; + PipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + + VkPipelineShaderStageCreateInfo ShaderStages[2] = {}; + ShaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + ShaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + ShaderStages[0].module = m_vkVSModule; + ShaderStages[0].pName = "main"; + + ShaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + ShaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + ShaderStages[1].module = m_vkFSModule; + ShaderStages[1].pName = "main"; + + PipelineCI.pStages = ShaderStages; + PipelineCI.stageCount = _countof(ShaderStages); + PipelineCI.layout = m_vkLayout; + + VkPipelineVertexInputStateCreateInfo VertexInputStateCI = {}; + VertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + PipelineCI.pVertexInputState = &VertexInputStateCI; + + VkPipelineInputAssemblyStateCreateInfo InputAssemblyCI = {}; + InputAssemblyCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + InputAssemblyCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + InputAssemblyCI.primitiveRestartEnable = VK_FALSE; + PipelineCI.pInputAssemblyState = &InputAssemblyCI; + + VkPipelineTessellationStateCreateInfo TessStateCI = {}; + TessStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; + PipelineCI.pTessellationState = &TessStateCI; + + VkPipelineViewportStateCreateInfo ViewPortStateCI = {}; + ViewPortStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + ViewPortStateCI.viewportCount = 1; + + VkViewport Viewport = {}; + Viewport.y = static_cast(SCDesc.Height); + Viewport.width = static_cast(SCDesc.Width); + Viewport.height = -static_cast(SCDesc.Height); + Viewport.maxDepth = 1; + ViewPortStateCI.pViewports = &Viewport; + + ViewPortStateCI.scissorCount = 1; + VkRect2D ScissorRect = {}; + ScissorRect.extent.width = SCDesc.Width; + ScissorRect.extent.height = SCDesc.Height; + ViewPortStateCI.pScissors = &ScissorRect; + PipelineCI.pViewportState = &ViewPortStateCI; + + VkPipelineRasterizationStateCreateInfo RasterizerStateCI = {}; + RasterizerStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + RasterizerStateCI.polygonMode = VK_POLYGON_MODE_FILL; + RasterizerStateCI.cullMode = VK_CULL_MODE_NONE; + RasterizerStateCI.lineWidth = 1; + PipelineCI.pRasterizationState = &RasterizerStateCI; + + // Multisample state (24) + VkPipelineMultisampleStateCreateInfo MSStateCI = {}; + + MSStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + MSStateCI.pNext = nullptr; + MSStateCI.flags = 0; // reserved for future use + // If subpass uses color and/or depth/stencil attachments, then the rasterizationSamples member of + // pMultisampleState must be the same as the sample count for those subpass attachments + MSStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + MSStateCI.sampleShadingEnable = VK_FALSE; + MSStateCI.minSampleShading = 0; // a minimum fraction of sample shading if sampleShadingEnable is set to VK_TRUE. + uint32_t SampleMask[] = {0xFFFFFFFF, 0}; // Vulkan spec allows up to 64 samples + MSStateCI.pSampleMask = SampleMask; // an array of static coverage information that is ANDed with + // the coverage information generated during rasterization (25.3) + MSStateCI.alphaToCoverageEnable = VK_FALSE; // whether a temporary coverage value is generated based on + // the alpha component of the fragment's first color output + MSStateCI.alphaToOneEnable = VK_FALSE; // whether the alpha component of the fragment's first color output is replaced with one + PipelineCI.pMultisampleState = &MSStateCI; + + VkPipelineDepthStencilStateCreateInfo DepthStencilStateCI = {}; + DepthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + PipelineCI.pDepthStencilState = &DepthStencilStateCI; + + VkPipelineColorBlendStateCreateInfo BlendStateCI = {}; + VkPipelineColorBlendAttachmentState Attachment = {}; + Attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + BlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + BlendStateCI.pAttachments = &Attachment; + BlendStateCI.attachmentCount = 1; + PipelineCI.pColorBlendState = &BlendStateCI; + + VkPipelineDynamicStateCreateInfo DynamicStateCI = {}; + DynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + PipelineCI.pDynamicState = &DynamicStateCI; + + PipelineCI.renderPass = m_vkRenderPass; + PipelineCI.subpass = 0; + PipelineCI.basePipelineHandle = VK_NULL_HANDLE; + PipelineCI.basePipelineIndex = 0; + + res = vkCreateGraphicsPipelines(m_vkDevice, VK_NULL_HANDLE, 1, &PipelineCI, nullptr, &m_vkPipeline); + VERIFY_EXPR(res == VK_SUCCESS); + VERIFY_EXPR(m_vkPipeline != VK_NULL_HANDLE); + + m_PushConstantStages = PushConstantStages; + + CreateFramebuffer(); + } + + void CreateRenderPass() + { + VkFormat ColorFormat = TexFormatToVkFormat(m_pSwapChain->GetCurrentBackBufferRTV()->GetDesc().Format); + VkFormat DepthFormat = TexFormatToVkFormat(m_pSwapChain->GetDepthBufferDSV()->GetDesc().Format); + + std::array Attachments; + std::array AttachmentReferences; + + VkSubpassDescription Subpass; + + VkRenderPassCreateInfo RenderPassCI = + TestingEnvironmentVk::GetRenderPassCreateInfo(1, &ColorFormat, DepthFormat, 1, + VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_LOAD_OP_CLEAR, + Attachments, AttachmentReferences, Subpass); + VkResult res = vkCreateRenderPass(m_vkDevice, &RenderPassCI, nullptr, &m_vkRenderPass); + VERIFY_EXPR(res >= 0); + (void)res; + } + + void CreateFramebuffer() + { + // Use Diligent Engine managed images (different from TestingSwapChainVk's internal images). + // The test compares rendering to Diligent Engine images against TestingSwapChainVk's internal images. + m_vkRenderTargetImage = (VkImage)m_pSwapChain->GetCurrentBackBufferRTV()->GetTexture()->GetNativeHandle(); + m_vkDepthBufferImage = (VkImage)m_pSwapChain->GetDepthBufferDSV()->GetTexture()->GetNativeHandle(); + + VkFormat ColorFormat = TexFormatToVkFormat(m_pSwapChain->GetCurrentBackBufferRTV()->GetDesc().Format); + VkFormat DepthFormat = TexFormatToVkFormat(m_pSwapChain->GetDepthBufferDSV()->GetDesc().Format); + + { + VkImageViewCreateInfo ImageViewCI = {}; + + ImageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + ImageViewCI.pNext = nullptr; + ImageViewCI.flags = 0; // reserved for future use. + ImageViewCI.image = m_vkRenderTargetImage; + ImageViewCI.format = ColorFormat; + ImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; + ImageViewCI.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + ImageViewCI.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + ImageViewCI.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + ImageViewCI.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + ImageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + ImageViewCI.subresourceRange.levelCount = 1; + ImageViewCI.subresourceRange.layerCount = 1; + + VkResult res = vkCreateImageView(m_vkDevice, &ImageViewCI, nullptr, &m_vkRenderTargetView); + VERIFY_EXPR(res >= 0); + + ImageViewCI.image = m_vkDepthBufferImage; + ImageViewCI.format = DepthFormat; + ImageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + + res = vkCreateImageView(m_vkDevice, &ImageViewCI, nullptr, &m_vkDepthBufferView); + VERIFY_EXPR(res >= 0); + } + + { + VkFramebufferCreateInfo FramebufferCI = {}; + + FramebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + FramebufferCI.pNext = nullptr; + FramebufferCI.flags = 0; // reserved for future use + FramebufferCI.renderPass = m_vkRenderPass; + FramebufferCI.attachmentCount = 2; + VkImageView Attachments[2] = {m_vkDepthBufferView, m_vkRenderTargetView}; + FramebufferCI.pAttachments = Attachments; + FramebufferCI.width = m_pSwapChain->GetDesc().Width; + FramebufferCI.height = m_pSwapChain->GetDesc().Height; + FramebufferCI.layers = 1; + + VkResult res = vkCreateFramebuffer(m_vkDevice, &FramebufferCI, nullptr, &m_vkFramebuffer); + VERIFY_EXPR(res >= 0); + (void)res; + } + } + + void BeginRenderPass(VkCommandBuffer vkCmdBuffer) + { + // Manually transition Diligent Engine managed images to the required layouts. + // We cannot use TestingSwapChainVk::TransitionRenderTarget/TransitionDepthBuffer + // because they operate on TestingSwapChainVk's internal images, not the Diligent Engine images. + VkImageMemoryBarrier ImageBarriers[2] = {}; + + // Render target barrier: UNDEFINED -> COLOR_ATTACHMENT_OPTIMAL + ImageBarriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + ImageBarriers[0].srcAccessMask = 0; + ImageBarriers[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + ImageBarriers[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + ImageBarriers[0].newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + ImageBarriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + ImageBarriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + ImageBarriers[0].image = m_vkRenderTargetImage; + ImageBarriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + ImageBarriers[0].subresourceRange.baseMipLevel = 0; + ImageBarriers[0].subresourceRange.levelCount = 1; + ImageBarriers[0].subresourceRange.baseArrayLayer = 0; + ImageBarriers[0].subresourceRange.layerCount = 1; + + // Depth buffer barrier: UNDEFINED -> DEPTH_STENCIL_ATTACHMENT_OPTIMAL + ImageBarriers[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + ImageBarriers[1].srcAccessMask = 0; + ImageBarriers[1].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + ImageBarriers[1].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + ImageBarriers[1].newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + ImageBarriers[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + ImageBarriers[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + ImageBarriers[1].image = m_vkDepthBufferImage; + ImageBarriers[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + ImageBarriers[1].subresourceRange.baseMipLevel = 0; + ImageBarriers[1].subresourceRange.levelCount = 1; + ImageBarriers[1].subresourceRange.baseArrayLayer = 0; + ImageBarriers[1].subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier(vkCmdBuffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + 0, + 0, nullptr, + 0, nullptr, + 2, ImageBarriers); + + VkRenderPassBeginInfo BeginInfo = {}; + + BeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + BeginInfo.renderPass = m_vkRenderPass; + BeginInfo.framebuffer = m_vkFramebuffer; + BeginInfo.renderArea.extent = VkExtent2D{m_pSwapChain->GetDesc().Width, m_pSwapChain->GetDesc().Height}; + + VkClearValue ClearValues[2] = {}; + + ClearValues[0].depthStencil.depth = 1; + ClearValues[1].color.float32[0] = 0; + ClearValues[1].color.float32[1] = 0; + ClearValues[1].color.float32[2] = 0; + ClearValues[1].color.float32[3] = 0; + + BeginInfo.clearValueCount = 2; + BeginInfo.pClearValues = ClearValues; + + vkCmdBeginRenderPass(vkCmdBuffer, &BeginInfo, VK_SUBPASS_CONTENTS_INLINE); + } + + void Draw(VkCommandBuffer vkCmdBuffer, const void* pPushConstantData, uint32_t PushConstantSize) + { + vkCmdBindPipeline(vkCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_vkPipeline); + vkCmdPushConstants(vkCmdBuffer, m_vkLayout, m_PushConstantStages, 0, PushConstantSize, pPushConstantData); + vkCmdDraw(vkCmdBuffer, 6, 1, 0, 0); + } + + void EndRenderPass(VkCommandBuffer vkCmdBuffer) + { + vkCmdEndRenderPass(vkCmdBuffer); + } + + ~PatchedPushConstantsRenderer() + { + vkDestroyPipeline(m_vkDevice, m_vkPipeline, nullptr); + vkDestroyPipelineLayout(m_vkDevice, m_vkLayout, nullptr); + vkDestroyShaderModule(m_vkDevice, m_vkVSModule, nullptr); + vkDestroyShaderModule(m_vkDevice, m_vkFSModule, nullptr); + vkDestroyRenderPass(m_vkDevice, m_vkRenderPass, nullptr); + vkDestroyFramebuffer(m_vkDevice, m_vkFramebuffer, nullptr); + vkDestroyImageView(m_vkDevice, m_vkDepthBufferView, nullptr); + vkDestroyImageView(m_vkDevice, m_vkRenderTargetView, nullptr); + } + +private: + TestingSwapChainVk* m_pSwapChain = nullptr; + VkDevice m_vkDevice = VK_NULL_HANDLE; + VkShaderModule m_vkVSModule = VK_NULL_HANDLE; + VkShaderModule m_vkFSModule = VK_NULL_HANDLE; + VkPipeline m_vkPipeline = VK_NULL_HANDLE; + VkPipelineLayout m_vkLayout = VK_NULL_HANDLE; + VkRenderPass m_vkRenderPass = VK_NULL_HANDLE; + VkFramebuffer m_vkFramebuffer = VK_NULL_HANDLE; + VkImage m_vkRenderTargetImage = VK_NULL_HANDLE; // Diligent Engine managed render target + VkImage m_vkDepthBufferImage = VK_NULL_HANDLE; // Diligent Engine managed depth buffer + VkImageView m_vkRenderTargetView = VK_NULL_HANDLE; + VkImageView m_vkDepthBufferView = VK_NULL_HANDLE; + VkShaderStageFlags m_PushConstantStages = 0; +}; + +// Test helper that runs the full test flow +void RunConvertUBOToPushConstantsTest(SHADER_COMPILER Compiler, SHADER_SOURCE_LANGUAGE SourceLanguage, const std::string& BlockName) +{ + auto* pEnv = TestingEnvironmentVk::GetInstance(); + if (!pEnv) + { + GTEST_SKIP() << "Vulkan environment not available"; + return; + } + + auto* pDevice = pEnv->GetDevice(); + if (pDevice->GetDeviceInfo().Type != RENDER_DEVICE_TYPE_VULKAN) + { + GTEST_SKIP() << "This test requires Vulkan device"; + return; + } + + if (Compiler == SHADER_COMPILER_DXC) + { + if (!VkConvertUBOToPushConstantsTest::DXCompiler || + !VkConvertUBOToPushConstantsTest::DXCompiler->IsLoaded()) + { + GTEST_SKIP() << "Skipped because DXCompiler not available"; + return; + } + } + + auto* pContext = pEnv->GetDeviceContext(); + auto* pSwapChain = pEnv->GetSwapChain(); + + RefCntAutoPtr pTestingSwapChain{pSwapChain, IID_TestingSwapChain}; + + auto* pTestingSwapChainVk = ClassPtrCast(pSwapChain); + + // Step 1: Render reference using existing ReferenceTriangleRenderer + pContext->Flush(); + pContext->InvalidateState(); + + const float ClearColor[] = {0.0f, 0.0f, 0.0f, 0.0f}; + RenderDrawCommandReferenceVk(pSwapChain, ClearColor); + + // Take snapshot of reference image + pTestingSwapChain->TakeSnapshot(); + + // Step 2: Compile shaders to SPIR-V + std::vector VS_SPIRV, FS_SPIRV; + + if (SourceLanguage == SHADER_SOURCE_LANGUAGE_HLSL) + { + CompileSPIRV(HLSL_ProceduralTriangleVS, "HLSL_ProceduralTriangleVS", Compiler, SHADER_TYPE_VERTEX, SHADER_SOURCE_LANGUAGE_HLSL, VS_SPIRV); + CompileSPIRV(HLSL_FragmentShaderWithCB, "HLSL_FragmentShaderWithCB", Compiler, SHADER_TYPE_PIXEL, SHADER_SOURCE_LANGUAGE_HLSL, FS_SPIRV); + } + else + { + CompileSPIRV(GLSL_ProceduralTriangleVS, "GLSL_ProceduralTriangleVS", Compiler, SHADER_TYPE_VERTEX, SHADER_SOURCE_LANGUAGE_GLSL, VS_SPIRV); + CompileSPIRV(GLSL_FragmentShaderWithUBO, "GLSL_FragmentShaderWithUBO", Compiler, SHADER_TYPE_PIXEL, SHADER_SOURCE_LANGUAGE_GLSL, FS_SPIRV); + } + + ASSERT_FALSE(VS_SPIRV.empty()) << "Failed to compile vertex shader"; + ASSERT_FALSE(FS_SPIRV.empty()) << "Failed to compile fragment shader"; + + // Step 3: Patch fragment shader to use push constants + std::vector FS_SPIRV_Patched = ConvertUBOToPushConstants(FS_SPIRV, BlockName); + ASSERT_FALSE(FS_SPIRV_Patched.empty()) << "Failed to patch UBO to push constants with BlockName: " << BlockName; + + if (SourceLanguage == SHADER_SOURCE_LANGUAGE_HLSL) + { + // SPIR-V bytecode generated from HLSL must be legalized to + // turn it into a valid vulkan SPIR-V shader. + SPIRV_OPTIMIZATION_FLAGS OptimizationFlags = SPIRV_OPTIMIZATION_FLAG_LEGALIZATION | SPIRV_OPTIMIZATION_FLAG_STRIP_REFLECTION; + VS_SPIRV = OptimizeSPIRV(VS_SPIRV, OptimizationFlags); + FS_SPIRV_Patched = OptimizeSPIRV(FS_SPIRV_Patched, OptimizationFlags); + } + + // Step 4: Render with push constants + { + PatchedPushConstantsRenderer Renderer{ + pTestingSwapChainVk, + VS_SPIRV, + FS_SPIRV_Patched, + sizeof(PushConstantData), + VK_SHADER_STAGE_FRAGMENT_BIT}; + + VkCommandBuffer vkCmdBuffer = pEnv->AllocateCommandBuffer(); + + Renderer.BeginRenderPass(vkCmdBuffer); + + // Set push constant data + // Factor = (1,1,1,1) to make output identical to reference + // Colors match the reference triangle colors (RGB for each vertex) + PushConstantData PushData = {}; + PushData.Factor[0] = 1.0f; + PushData.Factor[1] = 1.0f; + PushData.Factor[2] = 1.0f; + PushData.Factor[3] = 1.0f; + + // Vertex colors matching the reference (std140 layout: vec3 padded to 16 bytes) + // Triangle 1: Red, Green, Blue + PushData.Colors[0][0] = 1.0f; PushData.Colors[0][1] = 0.0f; PushData.Colors[0][2] = 0.0f; // Red + PushData.Colors[1][0] = 0.0f; PushData.Colors[1][1] = 1.0f; PushData.Colors[1][2] = 0.0f; // Green + PushData.Colors[2][0] = 0.0f; PushData.Colors[2][1] = 0.0f; PushData.Colors[2][2] = 1.0f; // Blue + // Triangle 2: Red, Green, Blue + PushData.Colors[3][0] = 1.0f; PushData.Colors[3][1] = 0.0f; PushData.Colors[3][2] = 0.0f; // Red + PushData.Colors[4][0] = 0.0f; PushData.Colors[4][1] = 1.0f; PushData.Colors[4][2] = 0.0f; // Green + PushData.Colors[5][0] = 0.0f; PushData.Colors[5][1] = 0.0f; PushData.Colors[5][2] = 1.0f; // Blue + + Renderer.Draw(vkCmdBuffer, &PushData, sizeof(PushData)); + + Renderer.EndRenderPass(vkCmdBuffer); + + vkEndCommandBuffer(vkCmdBuffer); + + pEnv->SubmitCommandBuffer(vkCmdBuffer, true); + } + + // Sync Diligent Engine's internal layout tracking with the actual image layouts. + // After our native Vulkan rendering, the images are in COLOR_ATTACHMENT_OPTIMAL + // and DEPTH_STENCIL_ATTACHMENT_OPTIMAL layouts, but Diligent Engine doesn't know this. + // We need to update the tracked layouts so that CompareWithSnapshot() can correctly + // transition the images for the copy operation. + { + RefCntAutoPtr pRenderTargetVk{pTestingSwapChainVk->GetCurrentBackBufferRTV()->GetTexture(), IID_TextureVk}; + RefCntAutoPtr pDepthBufferVk{pTestingSwapChainVk->GetDepthBufferDSV()->GetTexture(), IID_TextureVk}; + if (pRenderTargetVk) + pRenderTargetVk->SetLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + if (pDepthBufferVk) + pDepthBufferVk->SetLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + } + + // Step 5: Comparison native draw image with ref snapshot + pTestingSwapChainVk->Present(); +} + +} // namespace + +// Test patching UBO using struct type name "CB1" +TEST_F(VkConvertUBOToPushConstantsTest, PatchByStructTypeName_GLSLang_GLSL) +{ + RunConvertUBOToPushConstantsTest(SHADER_COMPILER_GLSLANG, SHADER_SOURCE_LANGUAGE_GLSL, "CB1"); +} + +// Test patching UBO using variable instance name "cb" +TEST_F(VkConvertUBOToPushConstantsTest, PatchByVariableName_GLSLang_GLSL) +{ + RunConvertUBOToPushConstantsTest(SHADER_COMPILER_GLSLANG, SHADER_SOURCE_LANGUAGE_GLSL, "cb"); +} + +// Test patching CB using cbuffer block name "CB1" with DXC compiler +// Note: In HLSL, cbuffer name and struct name may be the same or different. +// DXC typically generates both OpName for the struct type and the variable. + +TEST_F(VkConvertUBOToPushConstantsTest, PatchByStructTypeName_GLSLang_HLSL) +{ + RunConvertUBOToPushConstantsTest(SHADER_COMPILER_GLSLANG, SHADER_SOURCE_LANGUAGE_HLSL, "CB1"); +} + +TEST_F(VkConvertUBOToPushConstantsTest, PatchByStructTypeName_DXC_HLSL) +{ + RunConvertUBOToPushConstantsTest(SHADER_COMPILER_DXC, SHADER_SOURCE_LANGUAGE_HLSL, "CB1"); +} + + +} // namespace Testing + +} // namespace Diligent From 017a980827f3fe0e9438460d86dc48edcfa33e84 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 27 Dec 2025 17:20:47 +0800 Subject: [PATCH 2/9] fix formatting. --- .../ConvertUBOToPushConstantsTestVk.cpp | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp b/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp index 92fed9efc1..1893f497a5 100644 --- a/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp +++ b/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp @@ -171,8 +171,8 @@ void main() // - ColorData VertexColors (containing Colors[6] vec3 array) struct PushConstantData { - float Factor[4]; // vec4 Factor in Level1Data.Nested.Inner - float Colors[6][4]; // vec3 Colors[6] with padding (std140 layout: vec3 padded to vec4) + float Factor[4]; // vec4 Factor in Level1Data.Nested.Inner + float Colors[6][4]; // vec3 Colors[6] with padding (std140 layout: vec3 padded to vec4) }; // HLSL Vertex Shader - procedural two triangles, outputs vertex index for color lookup @@ -792,13 +792,25 @@ void RunConvertUBOToPushConstantsTest(SHADER_COMPILER Compiler, SHADER_SOURCE_LA // Vertex colors matching the reference (std140 layout: vec3 padded to 16 bytes) // Triangle 1: Red, Green, Blue - PushData.Colors[0][0] = 1.0f; PushData.Colors[0][1] = 0.0f; PushData.Colors[0][2] = 0.0f; // Red - PushData.Colors[1][0] = 0.0f; PushData.Colors[1][1] = 1.0f; PushData.Colors[1][2] = 0.0f; // Green - PushData.Colors[2][0] = 0.0f; PushData.Colors[2][1] = 0.0f; PushData.Colors[2][2] = 1.0f; // Blue + PushData.Colors[0][0] = 1.0f; + PushData.Colors[0][1] = 0.0f; + PushData.Colors[0][2] = 0.0f; // Red + PushData.Colors[1][0] = 0.0f; + PushData.Colors[1][1] = 1.0f; + PushData.Colors[1][2] = 0.0f; // Green + PushData.Colors[2][0] = 0.0f; + PushData.Colors[2][1] = 0.0f; + PushData.Colors[2][2] = 1.0f; // Blue // Triangle 2: Red, Green, Blue - PushData.Colors[3][0] = 1.0f; PushData.Colors[3][1] = 0.0f; PushData.Colors[3][2] = 0.0f; // Red - PushData.Colors[4][0] = 0.0f; PushData.Colors[4][1] = 1.0f; PushData.Colors[4][2] = 0.0f; // Green - PushData.Colors[5][0] = 0.0f; PushData.Colors[5][1] = 0.0f; PushData.Colors[5][2] = 1.0f; // Blue + PushData.Colors[3][0] = 1.0f; + PushData.Colors[3][1] = 0.0f; + PushData.Colors[3][2] = 0.0f; // Red + PushData.Colors[4][0] = 0.0f; + PushData.Colors[4][1] = 1.0f; + PushData.Colors[4][2] = 0.0f; // Green + PushData.Colors[5][0] = 0.0f; + PushData.Colors[5][1] = 0.0f; + PushData.Colors[5][2] = 1.0f; // Blue Renderer.Draw(vkCmdBuffer, &PushData, sizeof(PushData)); From 522fbb4bf04f5882d5e5644e13cb3416caaca57b Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 27 Dec 2025 17:35:06 +0800 Subject: [PATCH 3/9] Don't test ConvertUBOToPushConstantsTestVk when "DILIGENT_NO_GLSLANG" defined or "DILIGENT_USE_SPIRV_TOOLCHAIN" not defined. --- Tests/DiligentCoreAPITest/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Tests/DiligentCoreAPITest/CMakeLists.txt b/Tests/DiligentCoreAPITest/CMakeLists.txt index b2b1ccdd5f..1d84e1473a 100644 --- a/Tests/DiligentCoreAPITest/CMakeLists.txt +++ b/Tests/DiligentCoreAPITest/CMakeLists.txt @@ -35,6 +35,12 @@ if(NOT ARCHIVER_SUPPORTED) list(REMOVE_ITEM SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/RenderStateCacheTest.cpp) endif() +if(NOT ${DILIGENT_USE_SPIRV_TOOLCHAIN} OR ${DILIGENT_NO_GLSLANG}) + list(REMOVE_ITEM SOURCE + ${CMAKE_CURRENT_SOURCE_DIR}/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp + ) +endif() + if(D3D11_SUPPORTED) file(GLOB D3D11_SOURCE LIST_DIRECTORIES false src/D3D11/*) file(GLOB D3D11_INCLUDE LIST_DIRECTORIES false include/D3D11/*) From 8ef6ac1643e2aee6c854b6e529ecf199b12c69ef Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 27 Dec 2025 17:48:03 +0800 Subject: [PATCH 4/9] fix REMOVE_ITEM --- Tests/DiligentCoreAPITest/CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Tests/DiligentCoreAPITest/CMakeLists.txt b/Tests/DiligentCoreAPITest/CMakeLists.txt index 1d84e1473a..a0b270fd59 100644 --- a/Tests/DiligentCoreAPITest/CMakeLists.txt +++ b/Tests/DiligentCoreAPITest/CMakeLists.txt @@ -35,12 +35,6 @@ if(NOT ARCHIVER_SUPPORTED) list(REMOVE_ITEM SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/RenderStateCacheTest.cpp) endif() -if(NOT ${DILIGENT_USE_SPIRV_TOOLCHAIN} OR ${DILIGENT_NO_GLSLANG}) - list(REMOVE_ITEM SOURCE - ${CMAKE_CURRENT_SOURCE_DIR}/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp - ) -endif() - if(D3D11_SUPPORTED) file(GLOB D3D11_SOURCE LIST_DIRECTORIES false src/D3D11/*) file(GLOB D3D11_INCLUDE LIST_DIRECTORIES false include/D3D11/*) @@ -60,6 +54,12 @@ if(VULKAN_SUPPORTED) file(GLOB VK_INCLUDE LIST_DIRECTORIES false include/Vulkan/*) list(APPEND INCLUDE ${VK_INCLUDE}) list(APPEND SOURCE ${VK_SOURCE}) + + if(NOT ${DILIGENT_USE_SPIRV_TOOLCHAIN} OR ${DILIGENT_NO_GLSLANG}) + list(REMOVE_ITEM SOURCE + ${CMAKE_CURRENT_SOURCE_DIR}/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp + ) + endif() endif() if(METAL_SUPPORTED) From 7df250b5baf9b12de1dedcb4bc04cc2626c5c307 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 27 Dec 2025 18:23:51 +0800 Subject: [PATCH 5/9] Fix: ConvertUBOToPushConstantsTestVk.cpp:560:22: error: variable 'res' set but not used [-Werror,-Wunused-but-set-variable] --- .../src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp b/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp index 1893f497a5..22a122de66 100644 --- a/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp +++ b/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp @@ -559,6 +559,7 @@ class PatchedPushConstantsRenderer VkResult res = vkCreateImageView(m_vkDevice, &ImageViewCI, nullptr, &m_vkRenderTargetView); VERIFY_EXPR(res >= 0); + (void)res; ImageViewCI.image = m_vkDepthBufferImage; ImageViewCI.format = DepthFormat; @@ -566,6 +567,7 @@ class PatchedPushConstantsRenderer res = vkCreateImageView(m_vkDevice, &ImageViewCI, nullptr, &m_vkDepthBufferView); VERIFY_EXPR(res >= 0); + (void)res; } { From 5251b996a143469e59348c55c74df08d949f74ad Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 27 Dec 2025 19:59:49 +0800 Subject: [PATCH 6/9] Fix push constants layout --- .../ConvertUBOToPushConstantsTestVk.cpp | 194 ++++++++++++------ 1 file changed, 126 insertions(+), 68 deletions(-) diff --git a/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp b/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp index 22a122de66..cc1cd89082 100644 --- a/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp +++ b/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp @@ -77,11 +77,46 @@ class VkConvertUBOToPushConstantsTest : public ::testing::Test std::unique_ptr VkConvertUBOToPushConstantsTest::DXCompiler; -// GLSL Vertex Shader - procedural two triangles, outputs vertex index for color lookup +// GLSL Vertex Shader - procedural two triangles, reads colors from UBO and outputs for interpolation const std::string GLSL_ProceduralTriangleVS = R"( #version 450 core -layout(location = 0) out flat int out_VertexIndex; +// Vertex colors stored in UBO - will be patched to push constants +// Matching the same UBO structure as fragment shader +struct Level3Data +{ + vec4 Factor; +}; + +struct Level2Data +{ + Level3Data Inner; +}; + +struct Level1Data +{ + Level2Data Nested; +}; + +struct ColorData +{ + vec4 Colors[6]; +}; + +layout(set = 0, binding = 0) uniform CB1 +{ + Level1Data Data; + ColorData VertexColors; +} cb; + +layout(location = 0) out vec3 out_Color; + +// Helper function to access colors from the UBO +// This tests OpFunctionCall handling in vertex shader +vec3 GetVertexColor(ColorData colors, int index) +{ + return colors.Colors[index].rgb; +} void main() { @@ -95,15 +130,15 @@ void main() Pos[5] = vec4(+1.0, -0.5, 0.0, 1.0); gl_Position = Pos[gl_VertexIndex]; - out_VertexIndex = gl_VertexIndex; + // Output color from UBO via helper function - will be interpolated across triangle + out_Color = GetVertexColor(cb.VertexColors, gl_VertexIndex); } )"; // GLSL Fragment Shader with UBO - will be patched to push constants // Tests: // 1. Nested structs for multiple access chains and storage class propagation -// 2. Reading colors from push constants instead of VS input -// 3. Passing struct to function (tests function inlining before conversion) +// 2. Passing struct to function (tests function inlining before conversion) const std::string GLSL_FragmentShaderWithUBO = R"( #version 450 core @@ -123,10 +158,9 @@ struct Level1Data Level2Data Nested; }; -// Colors stored in push constants - tests reading colors from push constants struct ColorData { - vec3 Colors[6]; + vec4 Colors[6]; }; // UBO named "CB1" with instance name "cb" - allows testing both name matching paths @@ -136,7 +170,7 @@ layout(set = 0, binding = 0) uniform CB1 ColorData VertexColors; } cb; -layout(location = 0) in flat int in_VertexIndex; +layout(location = 0) in vec3 in_Color; layout(location = 0) out vec4 out_Color; // Helper function that takes UBO struct as parameter @@ -146,22 +180,14 @@ vec4 GetNestedFactor(Level1Data data) return data.Nested.Inner.Factor; } -// Another helper that accesses colors from the UBO -vec3 GetVertexColor(ColorData colors, int index) -{ - return colors.Colors[index]; -} - void main() { // Access deeply nested member through function call // This generates OpFunctionCall which requires inlining before storage class propagation vec4 factor = GetNestedFactor(cb.Data); - // Get color from push constants using vertex index - vec3 color = GetVertexColor(cb.VertexColors, in_VertexIndex); - - out_Color = vec4(color, 1.0) * factor; + // Use interpolated color from vertex shader + out_Color = vec4(in_Color, 1.0) * factor; } )"; @@ -169,20 +195,58 @@ void main() // Must match the layout of CB1 in the shader: // - Level1Data Data (containing nested Factor vec4) // - ColorData VertexColors (containing Colors[6] vec3 array) +// +// IMPORTANT: Do NOT use float Factor[4] - in std140, arrays have each element +// aligned to 16 bytes, so float[4] would be 64 bytes instead of 16! +// Use a struct to represent vec4 as 4 contiguous floats. struct PushConstantData { - float Factor[4]; // vec4 Factor in Level1Data.Nested.Inner - float Colors[6][4]; // vec3 Colors[6] with padding (std140 layout: vec3 padded to vec4) + float4 Factor; // vec4 Factor in Level1Data.Nested.Inner (16 bytes) + float4 Colors[6]; // vec4 Colors[6] (16 bytes * 6) }; -// HLSL Vertex Shader - procedural two triangles, outputs vertex index for color lookup +// HLSL Vertex Shader - procedural two triangles, reads colors from CB and outputs for interpolation const std::string HLSL_ProceduralTriangleVS = R"( +// Matching the same CB structure as fragment shader +struct Level3Data +{ + float4 Factor; +}; + +struct Level2Data +{ + Level3Data Inner; +}; + +struct Level1Data +{ + Level2Data Nested; +}; + +struct ColorData +{ + float4 Colors[6]; +}; + +cbuffer CB1 : register(b0) +{ + Level1Data Data; + ColorData VertexColors; +}; + struct PSInput { - float4 Pos : SV_POSITION; - uint VertexIndex : TEXCOORD0; + float4 Pos : SV_POSITION; + float3 Color : COLOR0; }; +// Helper function to access colors from the CB +// This tests OpFunctionCall handling in vertex shader +float3 GetVertexColor(ColorData colors, uint index) +{ + return colors.Colors[index].rgb; +} + PSInput main(uint VertexId : SV_VertexID) { float4 Pos[6]; @@ -195,8 +259,9 @@ PSInput main(uint VertexId : SV_VertexID) Pos[5] = float4(+1.0, -0.5, 0.0, 1.0); PSInput Out; - Out.Pos = Pos[VertexId]; - Out.VertexIndex = VertexId; + Out.Pos = Pos[VertexId]; + // Output color from CB via helper function - will be interpolated across triangle + Out.Color = GetVertexColor(VertexColors, VertexId); return Out; } )"; @@ -204,8 +269,7 @@ PSInput main(uint VertexId : SV_VertexID) // HLSL Fragment Shader with constant buffer - will be patched to push constants // Tests: // 1. Deeply nested structs for multiple access chains -// 2. Reading colors from constant buffer instead of VS input -// 3. Passing struct to function (tests function inlining before conversion) +// 2. Passing struct to function (tests function inlining before conversion) const std::string HLSL_FragmentShaderWithCB = R"( // Deeply nested structs to test multiple access chains struct Level3Data @@ -223,10 +287,9 @@ struct Level1Data Level2Data Nested; }; -// Colors stored in constant buffer - tests reading colors from push constants struct ColorData { - float3 Colors[6]; + float4 Colors[6]; }; // Constant buffer named "CB1" @@ -238,8 +301,8 @@ cbuffer CB1 : register(b0) struct PSInput { - float4 Pos : SV_POSITION; - uint VertexIndex : TEXCOORD0; + float4 Pos : SV_POSITION; + float3 Color : COLOR0; }; // Helper function that takes CB struct as parameter @@ -249,22 +312,14 @@ float4 GetNestedFactor(Level1Data data) return data.Nested.Inner.Factor; } -// Another helper that accesses colors from the CB -float3 GetVertexColor(ColorData colors, uint index) -{ - return colors.Colors[index]; -} - float4 main(PSInput In) : SV_Target { // Access deeply nested member through function call // This generates OpFunctionCall which requires inlining before storage class propagation float4 factor = GetNestedFactor(Data); - // Get color from constant buffer using vertex index - float3 color = GetVertexColor(VertexColors, In.VertexIndex); - - return float4(color, 1.0) * factor; + // Use interpolated color from vertex shader + return float4(In.Color, 1.0) * factor; } )"; @@ -757,16 +812,19 @@ void RunConvertUBOToPushConstantsTest(SHADER_COMPILER Compiler, SHADER_SOURCE_LA ASSERT_FALSE(VS_SPIRV.empty()) << "Failed to compile vertex shader"; ASSERT_FALSE(FS_SPIRV.empty()) << "Failed to compile fragment shader"; - // Step 3: Patch fragment shader to use push constants + // Step 3: Patch both vertex and fragment shaders to use push constants + // Both shaders reference the same UBO (CB1), so both need to be patched + std::vector VS_SPIRV_Patched = ConvertUBOToPushConstants(VS_SPIRV, BlockName); std::vector FS_SPIRV_Patched = ConvertUBOToPushConstants(FS_SPIRV, BlockName); - ASSERT_FALSE(FS_SPIRV_Patched.empty()) << "Failed to patch UBO to push constants with BlockName: " << BlockName; + ASSERT_FALSE(VS_SPIRV_Patched.empty()) << "Failed to patch VS UBO to push constants with BlockName: " << BlockName; + ASSERT_FALSE(FS_SPIRV_Patched.empty()) << "Failed to patch FS UBO to push constants with BlockName: " << BlockName; if (SourceLanguage == SHADER_SOURCE_LANGUAGE_HLSL) { // SPIR-V bytecode generated from HLSL must be legalized to // turn it into a valid vulkan SPIR-V shader. SPIRV_OPTIMIZATION_FLAGS OptimizationFlags = SPIRV_OPTIMIZATION_FLAG_LEGALIZATION | SPIRV_OPTIMIZATION_FLAG_STRIP_REFLECTION; - VS_SPIRV = OptimizeSPIRV(VS_SPIRV, OptimizationFlags); + VS_SPIRV_Patched = OptimizeSPIRV(VS_SPIRV_Patched, OptimizationFlags); FS_SPIRV_Patched = OptimizeSPIRV(FS_SPIRV_Patched, OptimizationFlags); } @@ -774,10 +832,10 @@ void RunConvertUBOToPushConstantsTest(SHADER_COMPILER Compiler, SHADER_SOURCE_LA { PatchedPushConstantsRenderer Renderer{ pTestingSwapChainVk, - VS_SPIRV, + VS_SPIRV_Patched, FS_SPIRV_Patched, sizeof(PushConstantData), - VK_SHADER_STAGE_FRAGMENT_BIT}; + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT}; VkCommandBuffer vkCmdBuffer = pEnv->AllocateCommandBuffer(); @@ -787,32 +845,32 @@ void RunConvertUBOToPushConstantsTest(SHADER_COMPILER Compiler, SHADER_SOURCE_LA // Factor = (1,1,1,1) to make output identical to reference // Colors match the reference triangle colors (RGB for each vertex) PushConstantData PushData = {}; - PushData.Factor[0] = 1.0f; - PushData.Factor[1] = 1.0f; - PushData.Factor[2] = 1.0f; - PushData.Factor[3] = 1.0f; + PushData.Factor.x = 1.0f; + PushData.Factor.y = 1.0f; + PushData.Factor.z = 1.0f; + PushData.Factor.w = 1.0f; // Vertex colors matching the reference (std140 layout: vec3 padded to 16 bytes) // Triangle 1: Red, Green, Blue - PushData.Colors[0][0] = 1.0f; - PushData.Colors[0][1] = 0.0f; - PushData.Colors[0][2] = 0.0f; // Red - PushData.Colors[1][0] = 0.0f; - PushData.Colors[1][1] = 1.0f; - PushData.Colors[1][2] = 0.0f; // Green - PushData.Colors[2][0] = 0.0f; - PushData.Colors[2][1] = 0.0f; - PushData.Colors[2][2] = 1.0f; // Blue + PushData.Colors[0].x = 1.0f; + PushData.Colors[0].y = 0.0f; + PushData.Colors[0].z = 0.0f; // Red + PushData.Colors[1].x = 0.0f; + PushData.Colors[1].y = 1.0f; + PushData.Colors[1].z = 0.0f; // Green + PushData.Colors[2].x = 0.0f; + PushData.Colors[2].y = 0.0f; + PushData.Colors[2].z = 1.0f; // Blue // Triangle 2: Red, Green, Blue - PushData.Colors[3][0] = 1.0f; - PushData.Colors[3][1] = 0.0f; - PushData.Colors[3][2] = 0.0f; // Red - PushData.Colors[4][0] = 0.0f; - PushData.Colors[4][1] = 1.0f; - PushData.Colors[4][2] = 0.0f; // Green - PushData.Colors[5][0] = 0.0f; - PushData.Colors[5][1] = 0.0f; - PushData.Colors[5][2] = 1.0f; // Blue + PushData.Colors[3].x = 1.0f; + PushData.Colors[3].y = 0.0f; + PushData.Colors[3].z = 0.0f; // Red + PushData.Colors[4].x = 0.0f; + PushData.Colors[4].y = 1.0f; + PushData.Colors[4].z = 0.0f; // Green + PushData.Colors[5].x = 0.0f; + PushData.Colors[5].y = 0.0f; + PushData.Colors[5].z = 1.0f; // Blue Renderer.Draw(vkCmdBuffer, &PushData, sizeof(PushData)); From fd14d112155508e9db2df1aad3cad967a93d16a9 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 27 Dec 2025 20:01:07 +0800 Subject: [PATCH 7/9] fix formatting --- .../src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp b/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp index cc1cd89082..c4c690e375 100644 --- a/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp +++ b/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp @@ -201,7 +201,7 @@ void main() // Use a struct to represent vec4 as 4 contiguous floats. struct PushConstantData { - float4 Factor; // vec4 Factor in Level1Data.Nested.Inner (16 bytes) + float4 Factor; // vec4 Factor in Level1Data.Nested.Inner (16 bytes) float4 Colors[6]; // vec4 Colors[6] (16 bytes * 6) }; From 32d24f3172af63d04e627fc76c050aba68182bb6 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 27 Dec 2025 20:24:41 +0800 Subject: [PATCH 8/9] Fix TestingEnvironment.cpp:66: Diligent Engine: ERROR: Debug assertion failed in CheckDynamicType(), file DebugUtilities.hpp, line 63: Dynamic type cast failed. Src typeid: 'N8Diligent7Testing20TestingEnvironmentGLE' Dst typeid: 'N8Diligent7Testing20TestingEnvironmentVkE' --- Tests/DiligentCoreAPITest/CMakeLists.txt | 10 +++++----- .../src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp | 12 ++++-------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Tests/DiligentCoreAPITest/CMakeLists.txt b/Tests/DiligentCoreAPITest/CMakeLists.txt index a0b270fd59..ef5c7fe764 100644 --- a/Tests/DiligentCoreAPITest/CMakeLists.txt +++ b/Tests/DiligentCoreAPITest/CMakeLists.txt @@ -54,12 +54,12 @@ if(VULKAN_SUPPORTED) file(GLOB VK_INCLUDE LIST_DIRECTORIES false include/Vulkan/*) list(APPEND INCLUDE ${VK_INCLUDE}) list(APPEND SOURCE ${VK_SOURCE}) +endif() - if(NOT ${DILIGENT_USE_SPIRV_TOOLCHAIN} OR ${DILIGENT_NO_GLSLANG}) - list(REMOVE_ITEM SOURCE - ${CMAKE_CURRENT_SOURCE_DIR}/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp - ) - endif() +if(NOT VULKAN_SUPPORTED OR NOT ${DILIGENT_USE_SPIRV_TOOLCHAIN} OR ${DILIGENT_NO_GLSLANG}) + list(REMOVE_ITEM SOURCE + ${CMAKE_CURRENT_SOURCE_DIR}/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp + ) endif() if(METAL_SUPPORTED) diff --git a/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp b/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp index c4c690e375..f24bb458ad 100644 --- a/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp +++ b/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp @@ -754,19 +754,15 @@ class PatchedPushConstantsRenderer // Test helper that runs the full test flow void RunConvertUBOToPushConstantsTest(SHADER_COMPILER Compiler, SHADER_SOURCE_LANGUAGE SourceLanguage, const std::string& BlockName) { - auto* pEnv = TestingEnvironmentVk::GetInstance(); - if (!pEnv) + // First check device type using base class to avoid ClassPtrCast assertion failure + auto* pBaseEnv = GPUTestingEnvironment::GetInstance(); + if (!pBaseEnv || pBaseEnv->GetDevice()->GetDeviceInfo().Type != RENDER_DEVICE_TYPE_VULKAN) { GTEST_SKIP() << "Vulkan environment not available"; return; } - auto* pDevice = pEnv->GetDevice(); - if (pDevice->GetDeviceInfo().Type != RENDER_DEVICE_TYPE_VULKAN) - { - GTEST_SKIP() << "This test requires Vulkan device"; - return; - } + auto* pEnv = TestingEnvironmentVk::GetInstance(); if (Compiler == SHADER_COMPILER_DXC) { From ab08dfd30c2ab4bf81dfeaedceb961c553be2fa3 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 28 Dec 2025 11:11:35 -0500 Subject: [PATCH 9/9] A few improvements - Do not create VkImages that can be obtained from Diligent - Use different background color for each test - Code cleanup --- .../ConvertUBOToPushConstantsTestVk.cpp | 390 ++++++++---------- 1 file changed, 167 insertions(+), 223 deletions(-) diff --git a/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp b/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp index f24bb458ad..88b390d6a6 100644 --- a/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp +++ b/Tests/DiligentCoreAPITest/src/Vulkan/ConvertUBOToPushConstantsTestVk.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 Diligent Graphics LLC + * Copyright 2025 Diligent Graphics LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,11 +30,12 @@ #include "DeviceContextVk.h" #include "RenderDeviceVk.h" #include "TextureVk.h" - +#include "TextureViewVk.h" #include "GLSLangUtils.hpp" #include "DXCompiler.hpp" #include "SPIRVTools.hpp" +#include "FastRand.hpp" #include "volk.h" @@ -57,11 +58,21 @@ namespace class VkConvertUBOToPushConstantsTest : public ::testing::Test { public: - static std::unique_ptr DXCompiler; + static IDXCompiler* GetDXCompiler() + { + return DXCompiler.get(); + } protected: static void SetUpTestSuite() { + GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); + if (pEnv->GetDevice()->GetDeviceInfo().Type != RENDER_DEVICE_TYPE_VULKAN) + { + // Skip all tests in this suite + GTEST_SKIP() << "This test is only for Vulkan device"; + } + GLSLangUtils::InitializeGlslang(); DXCompiler = CreateDXCompiler(DXCompilerTarget::Vulkan, 0, nullptr); @@ -69,13 +80,25 @@ class VkConvertUBOToPushConstantsTest : public ::testing::Test static void TearDownTestSuite() { + if (IsSkipped()) + { + return; + } + GLSLangUtils::FinalizeGlslang(); DXCompiler.reset(); } + + void RunConvertUBOToPushConstantsTest(SHADER_COMPILER Compiler, SHADER_SOURCE_LANGUAGE SourceLanguage, const std::string& BlockName); + +private: + static std::unique_ptr DXCompiler; + static FastRandFloat Rnd; }; std::unique_ptr VkConvertUBOToPushConstantsTest::DXCompiler; +FastRandFloat VkConvertUBOToPushConstantsTest::Rnd{0, 0.f, 1.f}; // GLSL Vertex Shader - procedural two triangles, reads colors from UBO and outputs for interpolation const std::string GLSL_ProceduralTriangleVS = R"( @@ -112,7 +135,6 @@ layout(set = 0, binding = 0) uniform CB1 layout(location = 0) out vec3 out_Color; // Helper function to access colors from the UBO -// This tests OpFunctionCall handling in vertex shader vec3 GetVertexColor(ColorData colors, int index) { return colors.Colors[index].rgb; @@ -326,12 +348,12 @@ float4 main(PSInput In) : SV_Target // Helper to create VkShaderModule from SPIR-V bytecode VkShaderModule CreateVkShaderModuleFromSPIRV(VkDevice vkDevice, const std::vector& SPIRV) { - VkShaderModuleCreateInfo ShaderModuleCI = {}; - ShaderModuleCI.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - ShaderModuleCI.pNext = nullptr; - ShaderModuleCI.flags = 0; - ShaderModuleCI.codeSize = SPIRV.size() * sizeof(uint32_t); - ShaderModuleCI.pCode = SPIRV.data(); + VkShaderModuleCreateInfo ShaderModuleCI{}; + ShaderModuleCI.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + ShaderModuleCI.pNext = nullptr; + ShaderModuleCI.flags = 0; + ShaderModuleCI.codeSize = SPIRV.size() * sizeof(uint32_t); + ShaderModuleCI.pCode = SPIRV.data(); VkShaderModule vkShaderModule = VK_NULL_HANDLE; VkResult res = vkCreateShaderModule(vkDevice, &ShaderModuleCI, nullptr, &vkShaderModule); @@ -356,14 +378,15 @@ std::vector LoadSPIRVFromHLSL(const std::string& ShaderSource, if (Compiler == SHADER_COMPILER_DXC) { - if (!VkConvertUBOToPushConstantsTest::DXCompiler || !VkConvertUBOToPushConstantsTest::DXCompiler->IsLoaded()) + IDXCompiler* DXCompiler = VkConvertUBOToPushConstantsTest::GetDXCompiler(); + if (!DXCompiler || !DXCompiler->IsLoaded()) { UNEXPECTED("Test should be skipped if DXCompiler is not available"); return {}; } RefCntAutoPtr pCompilerOutput; - VkConvertUBOToPushConstantsTest::DXCompiler->Compile(ShaderCI, ShaderVersion{6, 0}, nullptr, nullptr, &SPIRV, &pCompilerOutput); + DXCompiler->Compile(ShaderCI, ShaderVersion{6, 0}, nullptr, nullptr, &SPIRV, &pCompilerOutput); if (pCompilerOutput && pCompilerOutput->GetSize() > 0) { @@ -399,15 +422,6 @@ void CompileSPIRV(const std::string& ShaderSource, SHADER_SOURCE_LANGUAGE SourceLanguage, std::vector& SPIRV) { - if (Compiler == SHADER_COMPILER_DXC) - { - VERIFY(SourceLanguage == SHADER_SOURCE_LANGUAGE_HLSL, "DXC only supports HLSL"); - if (!VkConvertUBOToPushConstantsTest::DXCompiler || !VkConvertUBOToPushConstantsTest::DXCompiler->IsLoaded()) - { - GTEST_SKIP() << "DXC compiler is not available"; - } - } - SPIRV = (SourceLanguage == SHADER_SOURCE_LANGUAGE_GLSL) ? LoadSPIRVFromGLSL(ShaderSource, ShaderType) : LoadSPIRVFromHLSL(ShaderSource, ShaderType, Compiler); @@ -422,14 +436,12 @@ class PatchedPushConstantsRenderer const std::vector& VS_SPIRV, const std::vector& FS_SPIRV, uint32_t PushConstantSize, - VkShaderStageFlags PushConstantStages = VK_SHADER_STAGE_FRAGMENT_BIT) - { - m_pSwapChain = pSwapChain; + VkShaderStageFlags PushConstantStages = VK_SHADER_STAGE_FRAGMENT_BIT) : + m_pSwapChain{pSwapChain}, + m_vkDevice{TestingEnvironmentVk::GetInstance()->GetVkDevice()} - auto* pEnv = TestingEnvironmentVk::GetInstance(); - m_vkDevice = pEnv->GetVkDevice(); - - const auto& SCDesc = pSwapChain->GetDesc(); + { + const SwapChainDesc& SCDesc = pSwapChain->GetDesc(); CreateRenderPass(); @@ -441,31 +453,31 @@ class PatchedPushConstantsRenderer VERIFY_EXPR(m_vkFSModule != VK_NULL_HANDLE); // Pipeline layout with push constants (no descriptor sets) - VkPushConstantRange PushConstantRange = {}; - PushConstantRange.stageFlags = PushConstantStages; - PushConstantRange.offset = 0; - PushConstantRange.size = PushConstantSize; - - VkPipelineLayoutCreateInfo PipelineLayoutCI = {}; - PipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - PipelineLayoutCI.setLayoutCount = 0; - PipelineLayoutCI.pSetLayouts = nullptr; - PipelineLayoutCI.pushConstantRangeCount = 1; - PipelineLayoutCI.pPushConstantRanges = &PushConstantRange; + VkPushConstantRange PushConstantRange{}; + PushConstantRange.stageFlags = PushConstantStages; + PushConstantRange.offset = 0; + PushConstantRange.size = PushConstantSize; + + VkPipelineLayoutCreateInfo PipelineLayoutCI{}; + PipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + PipelineLayoutCI.setLayoutCount = 0; + PipelineLayoutCI.pSetLayouts = nullptr; + PipelineLayoutCI.pushConstantRangeCount = 1; + PipelineLayoutCI.pPushConstantRanges = &PushConstantRange; VkResult res = vkCreatePipelineLayout(m_vkDevice, &PipelineLayoutCI, nullptr, &m_vkLayout); VERIFY_EXPR(res == VK_SUCCESS); (void)res; // Create graphics pipeline - VkGraphicsPipelineCreateInfo PipelineCI = {}; - PipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + VkGraphicsPipelineCreateInfo PipelineCI{}; + PipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - VkPipelineShaderStageCreateInfo ShaderStages[2] = {}; - ShaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - ShaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - ShaderStages[0].module = m_vkVSModule; - ShaderStages[0].pName = "main"; + VkPipelineShaderStageCreateInfo ShaderStages[2]{}; + ShaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + ShaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + ShaderStages[0].module = m_vkVSModule; + ShaderStages[0].pName = "main"; ShaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; ShaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; @@ -476,25 +488,25 @@ class PatchedPushConstantsRenderer PipelineCI.stageCount = _countof(ShaderStages); PipelineCI.layout = m_vkLayout; - VkPipelineVertexInputStateCreateInfo VertexInputStateCI = {}; - VertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - PipelineCI.pVertexInputState = &VertexInputStateCI; + VkPipelineVertexInputStateCreateInfo VertexInputStateCI{}; + VertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + PipelineCI.pVertexInputState = &VertexInputStateCI; - VkPipelineInputAssemblyStateCreateInfo InputAssemblyCI = {}; - InputAssemblyCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - InputAssemblyCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - InputAssemblyCI.primitiveRestartEnable = VK_FALSE; - PipelineCI.pInputAssemblyState = &InputAssemblyCI; + VkPipelineInputAssemblyStateCreateInfo InputAssemblyCI{}; + InputAssemblyCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + InputAssemblyCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + InputAssemblyCI.primitiveRestartEnable = VK_FALSE; + PipelineCI.pInputAssemblyState = &InputAssemblyCI; - VkPipelineTessellationStateCreateInfo TessStateCI = {}; - TessStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; - PipelineCI.pTessellationState = &TessStateCI; + VkPipelineTessellationStateCreateInfo TessStateCI{}; + TessStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; + PipelineCI.pTessellationState = &TessStateCI; - VkPipelineViewportStateCreateInfo ViewPortStateCI = {}; - ViewPortStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - ViewPortStateCI.viewportCount = 1; + VkPipelineViewportStateCreateInfo ViewPortStateCI{}; + ViewPortStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + ViewPortStateCI.viewportCount = 1; - VkViewport Viewport = {}; + VkViewport Viewport{}; Viewport.y = static_cast(SCDesc.Height); Viewport.width = static_cast(SCDesc.Width); Viewport.height = -static_cast(SCDesc.Height); @@ -502,21 +514,21 @@ class PatchedPushConstantsRenderer ViewPortStateCI.pViewports = &Viewport; ViewPortStateCI.scissorCount = 1; - VkRect2D ScissorRect = {}; - ScissorRect.extent.width = SCDesc.Width; - ScissorRect.extent.height = SCDesc.Height; - ViewPortStateCI.pScissors = &ScissorRect; - PipelineCI.pViewportState = &ViewPortStateCI; - - VkPipelineRasterizationStateCreateInfo RasterizerStateCI = {}; - RasterizerStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - RasterizerStateCI.polygonMode = VK_POLYGON_MODE_FILL; - RasterizerStateCI.cullMode = VK_CULL_MODE_NONE; - RasterizerStateCI.lineWidth = 1; - PipelineCI.pRasterizationState = &RasterizerStateCI; - - // Multisample state (24) - VkPipelineMultisampleStateCreateInfo MSStateCI = {}; + VkRect2D ScissorRect{}; + ScissorRect.extent.width = SCDesc.Width; + ScissorRect.extent.height = SCDesc.Height; + ViewPortStateCI.pScissors = &ScissorRect; + PipelineCI.pViewportState = &ViewPortStateCI; + + VkPipelineRasterizationStateCreateInfo RasterizerStateCI{}; + RasterizerStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + RasterizerStateCI.polygonMode = VK_POLYGON_MODE_FILL; + RasterizerStateCI.cullMode = VK_CULL_MODE_NONE; + RasterizerStateCI.lineWidth = 1; + PipelineCI.pRasterizationState = &RasterizerStateCI; + + // Multisample state + VkPipelineMultisampleStateCreateInfo MSStateCI{}; MSStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; MSStateCI.pNext = nullptr; @@ -528,28 +540,28 @@ class PatchedPushConstantsRenderer MSStateCI.minSampleShading = 0; // a minimum fraction of sample shading if sampleShadingEnable is set to VK_TRUE. uint32_t SampleMask[] = {0xFFFFFFFF, 0}; // Vulkan spec allows up to 64 samples MSStateCI.pSampleMask = SampleMask; // an array of static coverage information that is ANDed with - // the coverage information generated during rasterization (25.3) + // the coverage information generated during rasterization MSStateCI.alphaToCoverageEnable = VK_FALSE; // whether a temporary coverage value is generated based on // the alpha component of the fragment's first color output MSStateCI.alphaToOneEnable = VK_FALSE; // whether the alpha component of the fragment's first color output is replaced with one PipelineCI.pMultisampleState = &MSStateCI; - VkPipelineDepthStencilStateCreateInfo DepthStencilStateCI = {}; - DepthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - PipelineCI.pDepthStencilState = &DepthStencilStateCI; + VkPipelineDepthStencilStateCreateInfo DepthStencilStateCI{}; + DepthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + PipelineCI.pDepthStencilState = &DepthStencilStateCI; - VkPipelineColorBlendStateCreateInfo BlendStateCI = {}; - VkPipelineColorBlendAttachmentState Attachment = {}; - Attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VkPipelineColorBlendStateCreateInfo BlendStateCI{}; + VkPipelineColorBlendAttachmentState Attachment{}; + Attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; BlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; BlendStateCI.pAttachments = &Attachment; BlendStateCI.attachmentCount = 1; PipelineCI.pColorBlendState = &BlendStateCI; - VkPipelineDynamicStateCreateInfo DynamicStateCI = {}; - DynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - PipelineCI.pDynamicState = &DynamicStateCI; + VkPipelineDynamicStateCreateInfo DynamicStateCI{}; + DynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + PipelineCI.pDynamicState = &DynamicStateCI; PipelineCI.renderPass = m_vkRenderPass; PipelineCI.subpass = 0; @@ -570,8 +582,8 @@ class PatchedPushConstantsRenderer VkFormat ColorFormat = TexFormatToVkFormat(m_pSwapChain->GetCurrentBackBufferRTV()->GetDesc().Format); VkFormat DepthFormat = TexFormatToVkFormat(m_pSwapChain->GetDepthBufferDSV()->GetDesc().Format); - std::array Attachments; - std::array AttachmentReferences; + std::array Attachments{}; + std::array AttachmentReferences{}; VkSubpassDescription Subpass; @@ -588,69 +600,39 @@ class PatchedPushConstantsRenderer { // Use Diligent Engine managed images (different from TestingSwapChainVk's internal images). // The test compares rendering to Diligent Engine images against TestingSwapChainVk's internal images. - m_vkRenderTargetImage = (VkImage)m_pSwapChain->GetCurrentBackBufferRTV()->GetTexture()->GetNativeHandle(); - m_vkDepthBufferImage = (VkImage)m_pSwapChain->GetDepthBufferDSV()->GetTexture()->GetNativeHandle(); - - VkFormat ColorFormat = TexFormatToVkFormat(m_pSwapChain->GetCurrentBackBufferRTV()->GetDesc().Format); - VkFormat DepthFormat = TexFormatToVkFormat(m_pSwapChain->GetDepthBufferDSV()->GetDesc().Format); - - { - VkImageViewCreateInfo ImageViewCI = {}; - - ImageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - ImageViewCI.pNext = nullptr; - ImageViewCI.flags = 0; // reserved for future use. - ImageViewCI.image = m_vkRenderTargetImage; - ImageViewCI.format = ColorFormat; - ImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; - ImageViewCI.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - ImageViewCI.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - ImageViewCI.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - ImageViewCI.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - - ImageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - ImageViewCI.subresourceRange.levelCount = 1; - ImageViewCI.subresourceRange.layerCount = 1; - - VkResult res = vkCreateImageView(m_vkDevice, &ImageViewCI, nullptr, &m_vkRenderTargetView); - VERIFY_EXPR(res >= 0); - (void)res; - - ImageViewCI.image = m_vkDepthBufferImage; - ImageViewCI.format = DepthFormat; - ImageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - - res = vkCreateImageView(m_vkDevice, &ImageViewCI, nullptr, &m_vkDepthBufferView); - VERIFY_EXPR(res >= 0); - (void)res; - } - - { - VkFramebufferCreateInfo FramebufferCI = {}; - - FramebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - FramebufferCI.pNext = nullptr; - FramebufferCI.flags = 0; // reserved for future use - FramebufferCI.renderPass = m_vkRenderPass; - FramebufferCI.attachmentCount = 2; - VkImageView Attachments[2] = {m_vkDepthBufferView, m_vkRenderTargetView}; - FramebufferCI.pAttachments = Attachments; - FramebufferCI.width = m_pSwapChain->GetDesc().Width; - FramebufferCI.height = m_pSwapChain->GetDesc().Height; - FramebufferCI.layers = 1; - - VkResult res = vkCreateFramebuffer(m_vkDevice, &FramebufferCI, nullptr, &m_vkFramebuffer); - VERIFY_EXPR(res >= 0); - (void)res; - } + RefCntAutoPtr pRTV{m_pSwapChain->GetCurrentBackBufferRTV(), IID_TextureViewVk}; + VERIFY_EXPR(pRTV); + RefCntAutoPtr pDSV{m_pSwapChain->GetDepthBufferDSV(), IID_TextureViewVk}; + VERIFY_EXPR(pDSV); + + VkFramebufferCreateInfo FramebufferCI{}; + FramebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + FramebufferCI.pNext = nullptr; + FramebufferCI.flags = 0; // reserved for future use + FramebufferCI.renderPass = m_vkRenderPass; + FramebufferCI.attachmentCount = 2; + VkImageView Attachments[2] = {pDSV->GetVulkanImageView(), pRTV->GetVulkanImageView()}; + FramebufferCI.pAttachments = Attachments; + FramebufferCI.width = m_pSwapChain->GetDesc().Width; + FramebufferCI.height = m_pSwapChain->GetDesc().Height; + FramebufferCI.layers = 1; + + VkResult res = vkCreateFramebuffer(m_vkDevice, &FramebufferCI, nullptr, &m_vkFramebuffer); + VERIFY_EXPR(res >= 0); + (void)res; } - void BeginRenderPass(VkCommandBuffer vkCmdBuffer) + void BeginRenderPass(VkCommandBuffer vkCmdBuffer, const float* ClearColor) { + RefCntAutoPtr pBackBuffer{m_pSwapChain->GetCurrentBackBufferRTV()->GetTexture(), IID_TextureVk}; + VERIFY_EXPR(pBackBuffer); + RefCntAutoPtr pDepthBuffer{m_pSwapChain->GetDepthBufferDSV()->GetTexture(), IID_TextureVk}; + VERIFY_EXPR(pDepthBuffer); + // Manually transition Diligent Engine managed images to the required layouts. // We cannot use TestingSwapChainVk::TransitionRenderTarget/TransitionDepthBuffer // because they operate on TestingSwapChainVk's internal images, not the Diligent Engine images. - VkImageMemoryBarrier ImageBarriers[2] = {}; + VkImageMemoryBarrier ImageBarriers[2]{}; // Render target barrier: UNDEFINED -> COLOR_ATTACHMENT_OPTIMAL ImageBarriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -660,7 +642,7 @@ class PatchedPushConstantsRenderer ImageBarriers[0].newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; ImageBarriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; ImageBarriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - ImageBarriers[0].image = m_vkRenderTargetImage; + ImageBarriers[0].image = pBackBuffer->GetVkImage(); ImageBarriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; ImageBarriers[0].subresourceRange.baseMipLevel = 0; ImageBarriers[0].subresourceRange.levelCount = 1; @@ -675,7 +657,7 @@ class PatchedPushConstantsRenderer ImageBarriers[1].newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; ImageBarriers[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; ImageBarriers[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - ImageBarriers[1].image = m_vkDepthBufferImage; + ImageBarriers[1].image = pDepthBuffer->GetVkImage(); ImageBarriers[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; ImageBarriers[1].subresourceRange.baseMipLevel = 0; ImageBarriers[1].subresourceRange.levelCount = 1; @@ -690,20 +672,18 @@ class PatchedPushConstantsRenderer 0, nullptr, 2, ImageBarriers); - VkRenderPassBeginInfo BeginInfo = {}; - + VkRenderPassBeginInfo BeginInfo{}; BeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; BeginInfo.renderPass = m_vkRenderPass; BeginInfo.framebuffer = m_vkFramebuffer; BeginInfo.renderArea.extent = VkExtent2D{m_pSwapChain->GetDesc().Width, m_pSwapChain->GetDesc().Height}; - VkClearValue ClearValues[2] = {}; - + VkClearValue ClearValues[2]{}; ClearValues[0].depthStencil.depth = 1; - ClearValues[1].color.float32[0] = 0; - ClearValues[1].color.float32[1] = 0; - ClearValues[1].color.float32[2] = 0; - ClearValues[1].color.float32[3] = 0; + ClearValues[1].color.float32[0] = ClearColor[0]; + ClearValues[1].color.float32[1] = ClearColor[1]; + ClearValues[1].color.float32[2] = ClearColor[2]; + ClearValues[1].color.float32[3] = ClearColor[3]; BeginInfo.clearValueCount = 2; BeginInfo.pClearValues = ClearValues; @@ -731,61 +711,45 @@ class PatchedPushConstantsRenderer vkDestroyShaderModule(m_vkDevice, m_vkFSModule, nullptr); vkDestroyRenderPass(m_vkDevice, m_vkRenderPass, nullptr); vkDestroyFramebuffer(m_vkDevice, m_vkFramebuffer, nullptr); - vkDestroyImageView(m_vkDevice, m_vkDepthBufferView, nullptr); - vkDestroyImageView(m_vkDevice, m_vkRenderTargetView, nullptr); } private: - TestingSwapChainVk* m_pSwapChain = nullptr; - VkDevice m_vkDevice = VK_NULL_HANDLE; - VkShaderModule m_vkVSModule = VK_NULL_HANDLE; - VkShaderModule m_vkFSModule = VK_NULL_HANDLE; - VkPipeline m_vkPipeline = VK_NULL_HANDLE; - VkPipelineLayout m_vkLayout = VK_NULL_HANDLE; - VkRenderPass m_vkRenderPass = VK_NULL_HANDLE; - VkFramebuffer m_vkFramebuffer = VK_NULL_HANDLE; - VkImage m_vkRenderTargetImage = VK_NULL_HANDLE; // Diligent Engine managed render target - VkImage m_vkDepthBufferImage = VK_NULL_HANDLE; // Diligent Engine managed depth buffer - VkImageView m_vkRenderTargetView = VK_NULL_HANDLE; - VkImageView m_vkDepthBufferView = VK_NULL_HANDLE; - VkShaderStageFlags m_PushConstantStages = 0; + TestingSwapChainVk* m_pSwapChain = nullptr; + VkDevice m_vkDevice = VK_NULL_HANDLE; + VkShaderModule m_vkVSModule = VK_NULL_HANDLE; + VkShaderModule m_vkFSModule = VK_NULL_HANDLE; + VkPipeline m_vkPipeline = VK_NULL_HANDLE; + VkPipelineLayout m_vkLayout = VK_NULL_HANDLE; + VkRenderPass m_vkRenderPass = VK_NULL_HANDLE; + VkFramebuffer m_vkFramebuffer = VK_NULL_HANDLE; + VkShaderStageFlags m_PushConstantStages = 0; }; // Test helper that runs the full test flow -void RunConvertUBOToPushConstantsTest(SHADER_COMPILER Compiler, SHADER_SOURCE_LANGUAGE SourceLanguage, const std::string& BlockName) +void VkConvertUBOToPushConstantsTest::RunConvertUBOToPushConstantsTest(SHADER_COMPILER Compiler, SHADER_SOURCE_LANGUAGE SourceLanguage, const std::string& BlockName) { - // First check device type using base class to avoid ClassPtrCast assertion failure - auto* pBaseEnv = GPUTestingEnvironment::GetInstance(); - if (!pBaseEnv || pBaseEnv->GetDevice()->GetDeviceInfo().Type != RENDER_DEVICE_TYPE_VULKAN) - { - GTEST_SKIP() << "Vulkan environment not available"; - return; - } - - auto* pEnv = TestingEnvironmentVk::GetInstance(); + TestingEnvironmentVk* pEnv = TestingEnvironmentVk::GetInstance(); if (Compiler == SHADER_COMPILER_DXC) { - if (!VkConvertUBOToPushConstantsTest::DXCompiler || - !VkConvertUBOToPushConstantsTest::DXCompiler->IsLoaded()) + if (!DXCompiler || !DXCompiler->IsLoaded()) { - GTEST_SKIP() << "Skipped because DXCompiler not available"; - return; + GTEST_SKIP() << "DXCompiler is not available"; } } - auto* pContext = pEnv->GetDeviceContext(); - auto* pSwapChain = pEnv->GetSwapChain(); + IDeviceContext* pContext = pEnv->GetDeviceContext(); + ISwapChain* pSwapChain = pEnv->GetSwapChain(); RefCntAutoPtr pTestingSwapChain{pSwapChain, IID_TestingSwapChain}; - auto* pTestingSwapChainVk = ClassPtrCast(pSwapChain); + TestingSwapChainVk* pTestingSwapChainVk = ClassPtrCast(pSwapChain); // Step 1: Render reference using existing ReferenceTriangleRenderer pContext->Flush(); pContext->InvalidateState(); - const float ClearColor[] = {0.0f, 0.0f, 0.0f, 0.0f}; + const float ClearColor[] = {Rnd(), Rnd(), Rnd(), Rnd()}; RenderDrawCommandReferenceVk(pSwapChain, ClearColor); // Take snapshot of reference image @@ -835,38 +799,23 @@ void RunConvertUBOToPushConstantsTest(SHADER_COMPILER Compiler, SHADER_SOURCE_LA VkCommandBuffer vkCmdBuffer = pEnv->AllocateCommandBuffer(); - Renderer.BeginRenderPass(vkCmdBuffer); + Renderer.BeginRenderPass(vkCmdBuffer, ClearColor); // Set push constant data // Factor = (1,1,1,1) to make output identical to reference // Colors match the reference triangle colors (RGB for each vertex) - PushConstantData PushData = {}; - PushData.Factor.x = 1.0f; - PushData.Factor.y = 1.0f; - PushData.Factor.z = 1.0f; - PushData.Factor.w = 1.0f; + PushConstantData PushData{}; + PushData.Factor = {1, 1, 1, 1}; // Vertex colors matching the reference (std140 layout: vec3 padded to 16 bytes) - // Triangle 1: Red, Green, Blue - PushData.Colors[0].x = 1.0f; - PushData.Colors[0].y = 0.0f; - PushData.Colors[0].z = 0.0f; // Red - PushData.Colors[1].x = 0.0f; - PushData.Colors[1].y = 1.0f; - PushData.Colors[1].z = 0.0f; // Green - PushData.Colors[2].x = 0.0f; - PushData.Colors[2].y = 0.0f; - PushData.Colors[2].z = 1.0f; // Blue - // Triangle 2: Red, Green, Blue - PushData.Colors[3].x = 1.0f; - PushData.Colors[3].y = 0.0f; - PushData.Colors[3].z = 0.0f; // Red - PushData.Colors[4].x = 0.0f; - PushData.Colors[4].y = 1.0f; - PushData.Colors[4].z = 0.0f; // Green - PushData.Colors[5].x = 0.0f; - PushData.Colors[5].y = 0.0f; - PushData.Colors[5].z = 1.0f; // Blue + // Triangle 1 + PushData.Colors[0] = {1, 0, 0, 0}; + PushData.Colors[1] = {0, 1, 0, 0}; + PushData.Colors[2] = {0, 0, 1, 0}; + // Triangle 2 + PushData.Colors[3] = {1, 0, 0, 0}; + PushData.Colors[4] = {0, 1, 0, 0}; + PushData.Colors[5] = {0, 0, 1, 0}; Renderer.Draw(vkCmdBuffer, &PushData, sizeof(PushData)); @@ -882,14 +831,10 @@ void RunConvertUBOToPushConstantsTest(SHADER_COMPILER Compiler, SHADER_SOURCE_LA // and DEPTH_STENCIL_ATTACHMENT_OPTIMAL layouts, but Diligent Engine doesn't know this. // We need to update the tracked layouts so that CompareWithSnapshot() can correctly // transition the images for the copy operation. - { - RefCntAutoPtr pRenderTargetVk{pTestingSwapChainVk->GetCurrentBackBufferRTV()->GetTexture(), IID_TextureVk}; - RefCntAutoPtr pDepthBufferVk{pTestingSwapChainVk->GetDepthBufferDSV()->GetTexture(), IID_TextureVk}; - if (pRenderTargetVk) - pRenderTargetVk->SetLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - if (pDepthBufferVk) - pDepthBufferVk->SetLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - } + if (RefCntAutoPtr pRenderTargetVk{pTestingSwapChainVk->GetCurrentBackBufferRTV()->GetTexture(), IID_TextureVk}) + pRenderTargetVk->SetLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + if (RefCntAutoPtr pDepthBufferVk{pTestingSwapChainVk->GetDepthBufferDSV()->GetTexture(), IID_TextureVk}) + pDepthBufferVk->SetLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); // Step 5: Comparison native draw image with ref snapshot pTestingSwapChainVk->Present(); @@ -923,7 +868,6 @@ TEST_F(VkConvertUBOToPushConstantsTest, PatchByStructTypeName_DXC_HLSL) RunConvertUBOToPushConstantsTest(SHADER_COMPILER_DXC, SHADER_SOURCE_LANGUAGE_HLSL, "CB1"); } - } // namespace Testing } // namespace Diligent