diff --git a/renderdoc/driver/vulkan/vk_serialise.cpp b/renderdoc/driver/vulkan/vk_serialise.cpp index c23ffa012..1251315ce 100644 --- a/renderdoc/driver/vulkan/vk_serialise.cpp +++ b/renderdoc/driver/vulkan/vk_serialise.cpp @@ -2660,24 +2660,75 @@ void DoSerialise(SerialiserType &ser, VkGraphicsPipelineCreateInfo &el) SERIALISE_MEMBER(stageCount); SERIALISE_MEMBER_ARRAY(pStages, stageCount); + bool hasTess = false; + for(uint32_t i = 0; i < el.stageCount; i++) + hasTess |= (el.pStages[i].stage & (VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | + VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)) != 0; + SERIALISE_MEMBER_OPT(pVertexInputState); SERIALISE_MEMBER_OPT(pInputAssemblyState); - SERIALISE_MEMBER_OPT(pTessellationState); - SERIALISE_MEMBER_OPT(pViewportState); + + // if we don't have tessellation shaders, pTessellationState is ignored and may be garbage + if(hasTess) + { + SERIALISE_MEMBER_OPT(pTessellationState); + } + else + { + SERIALISE_MEMBER_OPT_EMPTY(pTessellationState); + } + + // this gets messy. We need to ignore pViewportState, pMultisampleState, pDepthStencilState, and + // pColorBlendState if rasterization is disabled. Unfortunately... pViewportState is BEFORE the + // pRasterization state so we can't check it on read. + // Instead we rely on the fact that while writing we can check it and serialise an explicit NULL + // indicating that it's not present. Then on read we can just read it as if it's either NULL or + // present. + + // this bool just means "we can use SERIALISE_MEMBER_OPT" - i.e. the struct is present, or NULL. + const bool hasValidRasterization = + ser.IsReading() || (ser.IsWriting() && el.pRasterizationState && + el.pRasterizationState->rasterizerDiscardEnable == VK_FALSE); + + if(hasValidRasterization) + { + SERIALISE_MEMBER_OPT(pViewportState); + } + else + { + SERIALISE_MEMBER_OPT_EMPTY(pViewportState); + } + SERIALISE_MEMBER_OPT(pRasterizationState); - SERIALISE_MEMBER_OPT(pMultisampleState); - SERIALISE_MEMBER_OPT(pDepthStencilState); - SERIALISE_MEMBER_OPT(pColorBlendState); + + if(hasValidRasterization) + { + SERIALISE_MEMBER_OPT(pMultisampleState); + SERIALISE_MEMBER_OPT(pDepthStencilState); + SERIALISE_MEMBER_OPT(pColorBlendState); + } + else + { + SERIALISE_MEMBER_OPT_EMPTY(pMultisampleState); + SERIALISE_MEMBER_OPT_EMPTY(pDepthStencilState); + SERIALISE_MEMBER_OPT_EMPTY(pColorBlendState); + } + SERIALISE_MEMBER_OPT(pDynamicState); SERIALISE_MEMBER(layout); SERIALISE_MEMBER(renderPass); SERIALISE_MEMBER(subpass); + // handle must be explicitly ignored if the flag isn't set, since it could be garbage if(el.flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT) + { SERIALISE_MEMBER(basePipelineHandle); + } else + { SERIALISE_MEMBER_EMPTY(basePipelineHandle); + } SERIALISE_MEMBER(basePipelineIndex); } diff --git a/util/test/demos/vk/vk_parameter_zoo.cpp b/util/test/demos/vk/vk_parameter_zoo.cpp index c42f890ed..6cdc9850a 100644 --- a/util/test/demos/vk/vk_parameter_zoo.cpp +++ b/util/test/demos/vk/vk_parameter_zoo.cpp @@ -139,21 +139,60 @@ void main() CompileShaderModule(common + pixel, ShaderLang::glsl, ShaderStage::frag, "main"), }; - VkPipeline pipe; + VkPipeline pipe = createGraphicsPipeline(pipeCreateInfo); - // invalid handle - should not be used because the flag for derived pipelines is not used - pipeCreateInfo.basePipelineHandle = (VkPipeline)0x1234; + { + // invalid handle - should not be used because the flag for derived pipelines is not used + pipeCreateInfo.basePipelineHandle = (VkPipeline)0x1234; - CHECK_VKR(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, pipeCreateInfo, NULL, &pipe)); - vkDestroyPipeline(device, pipe, NULL); + VkPipeline dummy; + CHECK_VKR(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, pipeCreateInfo, NULL, &dummy)); + vkDestroyPipeline(device, dummy, NULL); - pipeCreateInfo.basePipelineHandle = VK_NULL_HANDLE; - pipeCreateInfo.basePipelineIndex = 3; + // invalid index - again should not be used + pipeCreateInfo.basePipelineHandle = VK_NULL_HANDLE; + pipeCreateInfo.basePipelineIndex = 3; - CHECK_VKR(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, pipeCreateInfo, NULL, &pipe)); - vkDestroyPipeline(device, pipe, NULL); + CHECK_VKR(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, pipeCreateInfo, NULL, &dummy)); + vkDestroyPipeline(device, dummy, NULL); - pipe = createGraphicsPipeline(pipeCreateInfo); + pipeCreateInfo.basePipelineIndex = -1; + + // bake the pipeline info so we can mess with the pointers it normally doesn't handle + VkGraphicsPipelineCreateInfo *baked = + (VkGraphicsPipelineCreateInfo *)(const VkGraphicsPipelineCreateInfo *)pipeCreateInfo; + + // NULL should be fine, we have no tessellation shaders + baked->pTessellationState = NULL; + + CHECK_VKR(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, baked, NULL, &dummy)); + vkDestroyPipeline(device, dummy, NULL); + + // same with a garbage pointer + baked->pTessellationState = (VkPipelineTessellationStateCreateInfo *)0x1234; + + CHECK_VKR(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, baked, NULL, &dummy)); + vkDestroyPipeline(device, dummy, NULL); + + // if we disable rasterization, tons of things can be NULL/garbage + pipeCreateInfo.rasterizationState.rasterizerDiscardEnable = VK_TRUE; + + baked->pViewportState = NULL; + baked->pMultisampleState = NULL; + baked->pDepthStencilState = NULL; + baked->pColorBlendState = NULL; + + CHECK_VKR(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, baked, NULL, &dummy)); + vkDestroyPipeline(device, dummy, NULL); + + baked->pViewportState = (VkPipelineViewportStateCreateInfo *)0x1234; + baked->pMultisampleState = (VkPipelineMultisampleStateCreateInfo *)0x1234; + baked->pDepthStencilState = (VkPipelineDepthStencilStateCreateInfo *)0x1234; + baked->pColorBlendState = (VkPipelineColorBlendStateCreateInfo *)0x1234; + + CHECK_VKR(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, baked, NULL, &dummy)); + vkDestroyPipeline(device, dummy, NULL); + } AllocatedBuffer vb( allocator, vkh::BufferCreateInfo(sizeof(DefaultTri), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |