diff --git a/renderdoc/driver/vulkan/vk_pixelhistory.cpp b/renderdoc/driver/vulkan/vk_pixelhistory.cpp index 837108191..67b4ee6ee 100644 --- a/renderdoc/driver/vulkan/vk_pixelhistory.cpp +++ b/renderdoc/driver/vulkan/vk_pixelhistory.cpp @@ -1390,7 +1390,10 @@ private: if(eventFlags & TestEnabled_DepthTesting) { - uint32_t pipeFlags = PipelineCreationFlags_FixedColorShader; + // Previous test might have modified the stencil state, which could + // cause this event to fail. + uint32_t pipeFlags = + PipelineCreationFlags_DisableStencilTest | PipelineCreationFlags_FixedColorShader; VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, dynamicScissor, replacementShaders, framebufferIndex); diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index 510c22b86..311cc00a4 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -278,6 +278,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index 6a7cbd911..ce64c6cf2 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -169,6 +169,9 @@ D3D11\demos + + Vulkan\demos + Vulkan\demos diff --git a/util/test/demos/vk/vk_pixel_history_test.cpp b/util/test/demos/vk/vk_pixel_history_test.cpp new file mode 100644 index 000000000..667db5a50 --- /dev/null +++ b/util/test/demos/vk/vk_pixel_history_test.cpp @@ -0,0 +1,388 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2020 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_Pixel_History_Test, VulkanGraphicsTest) +{ + static constexpr const char *Description = "Tests pixel history"; + + std::string common = R"EOSHADER( + +#version 420 core + +struct v2f +{ + vec4 pos; + vec4 col; + vec4 uv; +}; + +)EOSHADER"; + + const std::string vertex = R"EOSHADER( + +layout(location = 0) in vec3 Position; +layout(location = 1) in vec4 Color; +layout(location = 2) in vec2 UV; + +layout(location = 0) out v2f vertOut; + +void main() +{ + vertOut.pos = vec4(Position.xyz, 1); + gl_Position = vertOut.pos; + vertOut.col = Color; + vertOut.uv = vec4(UV.xy, 0, 1); +} + +)EOSHADER"; + + const std::string pixel = R"EOSHADER( + +layout(location = 0) in v2f vertIn; + +layout(location = 0, index = 0) out vec4 Color; + +void main() +{ + if (gl_FragCoord.x < 151 && gl_FragCoord.x > 150) + discard; + Color = vertIn.col; +} + +)EOSHADER"; + + std::string whitepixel = R"EOSHADER( +#version 420 core + +layout(location = 0, index = 0) out vec4 Color; + +void main() +{ + Color = vec4(1,1,1,1); +} + +)EOSHADER"; + + int main() + { + optDevExts.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME); + + // initialise, create window, create context, etc + if(!Init()) + return 3; + + bool KHR_maintenance1 = std::find(devExts.begin(), devExts.end(), + VK_KHR_MAINTENANCE1_EXTENSION_NAME) != devExts.end(); + + VkPipelineLayout layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo()); + + // note that the Y position values are inverted for vulkan 1.0 viewport convention, relative to + // all other APIs + DefaultA2V VBData[] = { + // this triangle occludes in depth + {Vec3f(-0.5f, 0.5f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(-0.5f, 0.0f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.0f, 0.0f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // this triangle occludes in stencil + {Vec3f(-0.5f, 0.0f, 0.9f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.5f, -0.5f, 0.9f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, 0.0f, 0.9f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // this triangle is just in the background to contribute to overdraw + {Vec3f(-0.9f, 0.9f, 0.95f), Vec4f(0.1f, 0.1f, 0.1f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, -0.9f, 0.95f), Vec4f(0.1f, 0.1f, 0.1f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.9f, 0.9f, 0.95f), Vec4f(0.1f, 0.1f, 0.1f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // the draw has a few triangles, main one that is occluded for depth, another that is + // adding to overdraw complexity, one that is backface culled, then a few more of various + // sizes for triangle size overlay + {Vec3f(-0.3f, 0.5f, 0.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(-0.3f, -0.5f, 0.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.5f, 0.0f, 0.5f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + {Vec3f(-0.2f, 0.2f, 0.6f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.2f, 0.0f, 0.6f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.2f, 0.4f, 0.6f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // backface culled + {Vec3f(0.1f, 0.0f, 0.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.5f, 0.2f, 0.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.5f, -0.2f, 0.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // depth clipped (i.e. not clamped) + {Vec3f(0.6f, 0.0f, 0.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.7f, -0.2f, 0.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.8f, 0.0f, 1.5f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // small triangles + // size=0.005 + {Vec3f(0.0f, -0.4f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, -0.41f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.01f, -0.4f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // size=0.015 + {Vec3f(0.0f, -0.5f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, -0.515f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.015f, -0.5f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // size=0.02 + {Vec3f(0.0f, -0.6f, 0.5f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, -0.62f, 0.5f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.02f, -0.6f, 0.5f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // size=0.025 + {Vec3f(0.0f, -0.7f, 0.5f), Vec4f(1.0f, 0.5f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, -0.725f, 0.5f), Vec4f(1.0f, 0.5f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.025f, -0.7f, 0.5f), Vec4f(1.0f, 0.5f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }; + + // negate y if we're using negative viewport height + if(KHR_maintenance1) + { + for(DefaultA2V &v : VBData) + v.pos.y = -v.pos.y; + } + + AllocatedBuffer vb(this, + vkh::BufferCreateInfo(sizeof(VBData), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + vb.upload(VBData); + + // create depth-stencil image + AllocatedImage depthimg(this, vkh::ImageCreateInfo(mainWindow->scissor.extent.width, + mainWindow->scissor.extent.height, 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 renderpass using the DS image + 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_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})}, 1, + VK_IMAGE_LAYOUT_GENERAL); + + VkRenderPass renderPass = createRenderPass(renderPassCreateInfo); + + // 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), dsvview}, mainWindow->scissor.extent)); + + // create PSO + vkh::GraphicsPipelineCreateInfo pipeCreateInfo; + + pipeCreateInfo.layout = layout; + pipeCreateInfo.renderPass = renderPass; + + pipeCreateInfo.vertexInputState.vertexBindingDescriptions = {vkh::vertexBind(0, DefaultA2V)}; + pipeCreateInfo.vertexInputState.vertexAttributeDescriptions = { + vkh::vertexAttr(0, 0, DefaultA2V, pos), vkh::vertexAttr(1, 0, DefaultA2V, col), + vkh::vertexAttr(2, 0, DefaultA2V, uv), + }; + + pipeCreateInfo.stages = { + CompileShaderModule(common + vertex, ShaderLang::glsl, ShaderStage::vert, "main"), + CompileShaderModule(common + pixel, ShaderLang::glsl, ShaderStage::frag, "main"), + }; + + pipeCreateInfo.rasterizationState.depthClampEnable = VK_FALSE; + pipeCreateInfo.rasterizationState.cullMode = VK_CULL_MODE_BACK_BIT; + + pipeCreateInfo.depthStencilState.depthTestEnable = VK_TRUE; + pipeCreateInfo.depthStencilState.depthWriteEnable = VK_TRUE; + pipeCreateInfo.depthStencilState.stencilTestEnable = VK_FALSE; + pipeCreateInfo.depthStencilState.front.compareOp = VK_COMPARE_OP_ALWAYS; + pipeCreateInfo.depthStencilState.front.passOp = VK_STENCIL_OP_REPLACE; + pipeCreateInfo.depthStencilState.front.reference = 0x55; + pipeCreateInfo.depthStencilState.front.compareMask = 0xff; + pipeCreateInfo.depthStencilState.front.writeMask = 0xff; + pipeCreateInfo.depthStencilState.back = pipeCreateInfo.depthStencilState.front; + + pipeCreateInfo.depthStencilState.depthCompareOp = VK_COMPARE_OP_ALWAYS; + VkPipeline depthWritePipe = createGraphicsPipeline(pipeCreateInfo); + + pipeCreateInfo.depthStencilState.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + pipeCreateInfo.depthStencilState.stencilTestEnable = VK_TRUE; + VkPipeline stencilWritePipe = createGraphicsPipeline(pipeCreateInfo); + + pipeCreateInfo.depthStencilState.stencilTestEnable = VK_FALSE; + VkPipeline backgroundPipe = createGraphicsPipeline(pipeCreateInfo); + + pipeCreateInfo.depthStencilState.stencilTestEnable = VK_TRUE; + pipeCreateInfo.depthStencilState.front.compareOp = VK_COMPARE_OP_GREATER; + VkPipeline pipe = createGraphicsPipeline(pipeCreateInfo); + + pipeCreateInfo.rasterizationState.cullMode = VK_CULL_MODE_FRONT_BIT; + VkPipeline cullFrontPipe = createGraphicsPipeline(pipeCreateInfo); + + renderPassCreateInfo.attachments.pop_back(); + renderPassCreateInfo.subpasses[0].pDepthStencilAttachment = NULL; + + VkRenderPass subrp = createRenderPass(renderPassCreateInfo); + + pipeCreateInfo.stages[1] = + CompileShaderModule(whitepixel, ShaderLang::glsl, ShaderStage::frag, "main"); + pipeCreateInfo.renderPass = subrp; + pipeCreateInfo.depthStencilState.stencilTestEnable = VK_FALSE; + pipeCreateInfo.depthStencilState.depthCompareOp = VK_COMPARE_OP_ALWAYS; + VkPipeline whitepipe = createGraphicsPipeline(pipeCreateInfo); + + AllocatedImage subimg( + this, + vkh::ImageCreateInfo(mainWindow->scissor.extent.width, mainWindow->scissor.extent.height, 0, + mainWindow->format, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 4, 5), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + + VkImageView subview = createImageView(vkh::ImageViewCreateInfo( + subimg.image, VK_IMAGE_VIEW_TYPE_2D, mainWindow->format, {}, + vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 2, 1, 2, 1))); + + VkFramebuffer subfb = createFramebuffer(vkh::FramebufferCreateInfo( + subrp, {subview}, + {mainWindow->scissor.extent.width / 4, mainWindow->scissor.extent.height / 4})); + + while(Running()) + { + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + VkViewport v = mainWindow->viewport; + v.x += 10.0f; + v.y += 10.0f; + v.width -= 20.0f; + v.height -= 20.0f; + + // if we're using KHR_maintenance1, check that negative viewport height is handled + if(KHR_maintenance1) + { + v.y += v.height; + v.height = -v.height; + } + + vkCmdSetViewport(cmd, 0, 1, &v); + vkCmdSetScissor(cmd, 0, 1, &mainWindow->scissor); + vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0}); + + setMarker(cmd, "Begin RenderPass"); + vkCmdBeginRenderPass(cmd, + vkh::RenderPassBeginInfo( + renderPass, fbs[mainWindow->imgIndex], mainWindow->scissor, + {vkh::ClearValue(0.2f, 0.2f, 0.2f, 1.0f), vkh::ClearValue(1.0f, 0)}), + VK_SUBPASS_CONTENTS_INLINE); + + // draw the setup triangles + + setMarker(cmd, "Depth Write"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, depthWritePipe); + vkCmdDraw(cmd, 3, 1, 0, 0); + + setMarker(cmd, "Stencil Write"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, stencilWritePipe); + vkCmdDraw(cmd, 3, 1, 3, 0); + + setMarker(cmd, "Background"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, backgroundPipe); + vkCmdDraw(cmd, 3, 1, 6, 0); + + setMarker(cmd, "Cull Front"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, cullFrontPipe); + vkCmdDraw(cmd, 3, 1, 0, 0); + + // add a marker so we can easily locate this draw + setMarker(cmd, "Test Begin"); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); + vkCmdDraw(cmd, 24, 1, 9, 0); + + vkCmdEndRenderPass(cmd); + + v = mainWindow->viewport; + v.width /= 4.0f; + v.height /= 4.0f; + v.x += 5.0f; + v.y += 5.0f; + v.width -= 10.0f; + v.height -= 10.0f; + + if(KHR_maintenance1) + { + v.y += v.height; + v.height = -v.height; + } + + VkRect2D s = mainWindow->scissor; + s.extent.width /= 4; + s.extent.height /= 4; + + vkCmdSetViewport(cmd, 0, 1, &v); + vkCmdSetScissor(cmd, 0, 1, &s); + + vkCmdBeginRenderPass( + cmd, vkh::RenderPassBeginInfo(subrp, subfb, s, {vkh::ClearValue(0.0f, 0.0f, 0.0f, 1.0f)}), + VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, whitepipe); + + setMarker(cmd, "Subresources"); + vkCmdDraw(cmd, 24, 1, 9, 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/tests/Vulkan/Vk_Pixel_History.py b/util/test/tests/Vulkan/Vk_Pixel_History.py new file mode 100644 index 000000000..ca3f49405 --- /dev/null +++ b/util/test/tests/Vulkan/Vk_Pixel_History.py @@ -0,0 +1,130 @@ +import renderdoc as rd +import rdtest + +def value_selector(x): return x.floatValue +def passed(x): return x.Passed() +def event_id(x): return x.eventId +def culled(x): return x.backfaceCulled +def depth_test_failed(x): return x.depthTestFailed +def stencil_test_failed(x): return x.stencilTestFailed +def shader_discarded(x): return x.shaderDiscarded +def shader_out_col(x): return value_selector(x.shaderOut.col) +def post_mod_col(x): return value_selector(x.postMod.col) +def primitive_id(x): return x.primitiveID + +class VK_Pixel_History(rdtest.TestCase): + demos_test_name = 'VK_Pixel_History_Test' + demos_frame_cap = 5 + + def check_capture(self): + apiprops: rd.APIProperties = self.controller.GetAPIProperties() + + if not apiprops.pixelHistory: + rdtest.log.print("Vulkan pixel history not tested") + return + + test_marker: rd.DrawcallDescription = self.find_draw("Test") + self.controller.SetFrameEvent(test_marker.next.eventId, True) + + pipe: rd.PipeState = self.controller.GetPipelineState() + + rt: rd.BoundResource = pipe.GetOutputTargets()[0] + + vp: rd.Viewport = pipe.GetViewport(0) + + tex = rt.resourceId + tex_details = self.get_texture(tex) + + sub = rd.Subresource() + if tex_details.arraysize > 1: + sub.slice = rt.firstSlice + if tex_details.mips > 1: + sub.mip = rt.firstMip + + begin_renderpass_eid = self.find_draw("Begin RenderPass").next.eventId + depth_write_eid = self.find_draw("Depth Write").next.eventId + stencil_write_eid = self.find_draw("Stencil Write").next.eventId + background_eid = self.find_draw("Background").next.eventId + cull_eid = self.find_draw("Cull Front").next.eventId + test_eid = self.find_draw("Test").next.eventId + + # For pixel 190, 149 inside the red triangle + x, y = 190, 149 + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.typeCast) + events = [ + [[event_id, begin_renderpass_eid], [passed, True]], + [[event_id, stencil_write_eid], [passed, True]], + [[event_id, background_eid], [depth_test_failed, True], [post_mod_col, (1.0, 0.0, 0.0, 1.0)]], + [[event_id, test_eid], [stencil_test_failed, True]], + ] + self.check_events(events, modifs) + self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.typeCast) + + x, y = 190, 150 + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.typeCast) + events = [ + [[event_id, begin_renderpass_eid], [passed, True]], + [[event_id, depth_write_eid], [passed, True]], + [[event_id, background_eid], [depth_test_failed, True]], + [[event_id, cull_eid], [culled, True]], + [[event_id, test_eid], [depth_test_failed, True]], + ] + self.check_events(events, modifs) + self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.typeCast) + + x, y = 200, 50 + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.typeCast) + events = [ + [[event_id, begin_renderpass_eid], [passed, True]], + [[event_id, background_eid], [passed, True]], + [[event_id, test_eid], [passed, True], [primitive_id, 7]], + ] + self.check_events(events, modifs) + self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.typeCast) + + x, y = 150, 250 + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.typeCast) + events = [ + [[event_id, begin_renderpass_eid], [passed, True]], + [[event_id, background_eid], [shader_discarded, True]], + ] + self.check_events(events, modifs) + self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.typeCast) + + def check_events(self, events, modifs): + self.check(len(modifs) == len(events)) + # Check for consistency first + self.check_modifs_consistent(modifs) + for i in range(len(modifs)): + for c in range(len(events[i])): + expected = events[i][c][1] + actual = events[i][c][0](modifs[i]) + if not rdtest.value_compare(actual, expected): + raise rdtest.TestFailureException( + "eventId {}, testing {} expected {}, got {}".format(modifs[i].eventId, + events[i][c][0].__name__, + expected, + actual)) + + def check_modifs_consistent(self, modifs): + # postmod of each should match premod of the next + for i in range(len(modifs) - 1): + if value_selector(modifs[i].postMod.col) != value_selector(modifs[i + 1].preMod.col): + raise rdtest.TestFailureException( + "postmod at {}: {} doesn't match premod at {}: {}".format(modifs[i].eventId, + value_selector(modifs[i].postMod.col), + modifs[i + 1].eventId, + value_selector(modifs[i].preMod.col))) + + # Check that if the test failed, its postmod is the same as premod + for i in range(len(modifs)): + if not modifs[i].Passed(): + if not rdtest.value_compare(value_selector(modifs[i].preMod.col), value_selector(modifs[i].postMod.col)): + raise rdtest.TestFailureException( + "postmod at {}: {} doesn't match premod: {}".format(modifs[i].eventId, + value_selector(modifs[i].postMod.col), + value_selector(modifs[i].preMod.col)))