diff --git a/util/test/demos/test_common.cpp b/util/test/demos/test_common.cpp index 31516d5e4..581c43cfa 100644 --- a/util/test/demos/test_common.cpp +++ b/util/test/demos/test_common.cpp @@ -680,3 +680,247 @@ bool GraphicsTest::FrameLimit() return true; } + +namespace PixelHistory +{ +draw makeDraw(const std::vector &verts) +{ + uint32_t first = (uint32_t)vb.size(); + + vb.insert(vb.end(), verts.begin(), verts.end()); + + return {first, (uint32_t)verts.size()}; +} + +std::vector vb; + +draw DepthWrite; +draw UnboundPS; +draw StencilWrite; +draw Background; +draw CullFront; +draw DepthBoundsPrep; +draw DepthBoundsClip; +draw Draws300; +draw Instances300; +draw MainTest; +draw ScissorFail; +draw ScissorPass; +draw StencilRef; +draw StencilMask; +draw DepthTest; +draw SampleColour; +draw DepthEqualSetup; +draw DepthEqualFail; +draw DepthEqualPass16; +draw DepthEqualPass24; +draw DepthEqualPass32; +draw ColourMask; +draw OverflowingDraw; +draw PerFragDiscard; + +void init() +{ + // this triangle occludes in depth + CullFront = DepthWrite = makeDraw({ + {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 + UnboundPS = StencilWrite = makeDraw({ + {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 + Background = makeDraw({ + {Vec3f(-0.9f, -0.9f, 0.95f), Vec4f(1.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, 0.9f, 0.95f), Vec4f(1.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.9f, -0.9f, 0.95f), Vec4f(1.0f, 0.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }); + + MainTest = makeDraw({ + // large gradient, failing depth and stencil on left side behind DepthWrite and StencilWrite + {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)}, + + // smaller black triangle in front of background but behind gradient above + {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)}, + }); + + // scissor clips part of this triangle + ScissorFail = makeDraw({ + {Vec3f(-0.7f, -0.8f, 0.5f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(-0.5f, -0.5f, 0.5f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.3f, -0.8f, 0.5f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }); + + // scissor does clip some but passes where above fails + ScissorPass = makeDraw({ + {Vec3f(-0.7f, -0.8f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(-0.5f, -0.5f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.3f, -0.8f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }); + + // fails due to stencil ref + StencilRef = makeDraw({ + {Vec3f(-0.6f, -0.75f, 0.5f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(-0.5f, -0.65f, 0.5f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.4f, -0.75f, 0.5f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }); + + // fails due to stencil mask + StencilMask = makeDraw({ + {Vec3f(-0.6f, -0.75f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(-0.5f, -0.65f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.4f, -0.75f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }); + + // Six triangles, five fragments reported. + DepthTest = makeDraw({ + // 0: Fails depth test + {Vec3f(0.0f, -0.8f, 0.97f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.4f, -0.2f, 0.97f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.8f, -0.8f, 0.97f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // 1: Passes + {Vec3f(0.2f, -0.8f, 0.20f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.4f, -0.4f, 0.20f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.6f, -0.8f, 0.20f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // 2: Fails depth test compared to 1st fragment + {Vec3f(0.2f, -0.8f, 0.30f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.4f, -0.6f, 0.30f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.6f, -0.8f, 0.30f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // 3: Passes + {Vec3f(0.2f, -0.8f, 0.10f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.4f, -0.7f, 0.10f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.6f, -0.8f, 0.10f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // 4: Fails depth bounds test + {Vec3f(0.2f, -0.8f, 0.05f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.4f, -0.7f, 0.05f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.6f, -0.8f, 0.05f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + + // 5: Fails backface culling, not reported. + {Vec3f(0.6f, -0.8f, 0.25f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + {Vec3f(0.4f, -0.7f, 0.25f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.2f, -0.8f, 0.25f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + }); + + // depth bounds prep + DepthBoundsPrep = makeDraw({ + {Vec3f(0.6f, 0.3f, 0.3f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.7f, 0.5f, 0.5f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.8f, 0.3f, 0.7f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }); + // depth bounds clip + DepthBoundsClip = makeDraw({ + {Vec3f(0.6f, 0.3f, 0.3f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.7f, 0.5f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.8f, 0.3f, 0.7f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }); + + // 300 draws of 1 triangle + Draws300 = makeDraw({ + {Vec3f(-0.7f, 0.0f, 0.33f), Vec4f(0.5f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.8f, 0.2f, 0.33f), Vec4f(0.5f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + {Vec3f(-0.6f, 0.2f, 0.33f), Vec4f(0.5f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + }); + // 300 instances of 1 triangle + Instances300 = makeDraw({ + {Vec3f(-0.7f, 0.6f, 0.33f), Vec4f(1.0f, 0.5f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.8f, 0.8f, 0.33f), Vec4f(1.0f, 0.5f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + {Vec3f(-0.6f, 0.8f, 0.33f), Vec4f(1.0f, 0.5f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + }); + // sample colouring triangle + SampleColour = makeDraw({ + {Vec3f(0.6f, -0.4f, 0.3f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.7f, -0.2f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.8f, -0.4f, 0.7f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }); + + // depth equal that fails + DepthEqualSetup = makeDraw({ + {Vec3f(-0.1f, -0.8f, 0.1f), Vec4f(0.1f, 0.1f, 0.1f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, -0.6f, 0.1f), Vec4f(0.1f, 0.1f, 0.1f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.1f, -0.8f, 0.1f), Vec4f(0.1f, 0.1f, 0.1f, 1.0f), Vec2f(1.0f, 0.0f)}, + }); + + // depth equal that fails + DepthEqualFail = makeDraw({ + {Vec3f(-0.1f, -0.8f, 0.1f + 5.0e-4f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, -0.6f, 0.1f + 5.0e-4f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.1f, -0.8f, 0.1f + 5.0e-4f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }); + + // D16 depth equal (enough) triangle + DepthEqualPass16 = makeDraw({ + {Vec3f(-0.1f, -0.8f, 0.1f + 1.0e-8f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, -0.6f, 0.1f + 1.0e-8f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.1f, -0.8f, 0.1f + 1.0e-8f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }); + // D24 depth equal (enough) triangle + DepthEqualPass24 = makeDraw({ + {Vec3f(-0.1f, -0.8f, 0.1f + 1.0e-8f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, -0.6f, 0.1f + 1.0e-8f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.1f, -0.8f, 0.1f + 1.0e-8f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }); + // D32 depth equal triangle + DepthEqualPass32 = makeDraw({ + {Vec3f(-0.1f, -0.8f, 0.1f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, -0.6f, 0.1f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.1f, -0.8f, 0.1f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }); + + ColourMask = makeDraw({ + {Vec3f(-0.7f, 0.4f, 0.33f), Vec4f(3.0f, 3.0f, 3.0f, 3.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.8f, 0.6f, 0.33f), Vec4f(3.0f, 3.0f, 3.0f, 3.0f), Vec2f(1.0f, 0.0f)}, + {Vec3f(-0.6f, 0.6f, 0.33f), Vec4f(3.0f, 3.0f, 3.0f, 3.0f), Vec2f(0.0f, 0.0f)}, + }); + + // three triangles that overlap and clamp each component individually + OverflowingDraw = makeDraw({ + {Vec3f(-0.5f, 0.6f, 0.33f), Vec4f(-1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.6f, 0.8f, 0.33f), Vec4f(-1.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + {Vec3f(-0.4f, 0.8f, 0.33f), Vec4f(-1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + + {Vec3f(-0.5f, 0.6f, 0.33f), Vec4f(0.0f, -1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.6f, 0.8f, 0.33f), Vec4f(0.0f, -1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + {Vec3f(-0.4f, 0.8f, 0.33f), Vec4f(0.0f, -1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + + {Vec3f(-0.5f, 0.6f, 0.33f), Vec4f(0.0f, 0.0f, -1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.6f, 0.8f, 0.33f), Vec4f(0.0f, 0.0f, -1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + {Vec3f(-0.4f, 0.8f, 0.33f), Vec4f(0.0f, 0.0f, -1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + }); + + // scissor does clip some but passes where above fails + PerFragDiscard = makeDraw({ + {Vec3f(-0.7f, -0.2f, 0.33f), Vec4f(-1.0f, -1.0f, -1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.8f, 0.0f, 0.33f), Vec4f(-1.0f, -1.0f, -1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + {Vec3f(-0.6f, 0.0f, 0.33f), Vec4f(-1.0f, -1.0f, -1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + + {Vec3f(-0.7f, -0.2f, 0.33f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.8f, 0.0f, 0.33f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + {Vec3f(-0.6f, 0.0f, 0.33f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + }); +}; + +}; diff --git a/util/test/demos/test_common.h b/util/test/demos/test_common.h index 00b288730..90dde9acd 100644 --- a/util/test/demos/test_common.h +++ b/util/test/demos/test_common.h @@ -380,6 +380,44 @@ void OutputPrint(const char *fmt, ...); exit(0); \ } while(0) +namespace PixelHistory +{ +void init(); + +struct draw +{ + uint32_t first; + uint32_t count; +}; + +extern std::vector vb; + +extern draw DepthWrite; +extern draw UnboundPS; +extern draw StencilWrite; +extern draw Background; +extern draw CullFront; +extern draw DepthBoundsPrep; +extern draw DepthBoundsClip; +extern draw Draws300; +extern draw Instances300; +extern draw MainTest; +extern draw ScissorFail; +extern draw ScissorPass; +extern draw StencilRef; +extern draw StencilMask; +extern draw DepthTest; +extern draw SampleColour; +extern draw DepthEqualSetup; +extern draw DepthEqualFail; +extern draw DepthEqualPass16; +extern draw DepthEqualPass24; +extern draw DepthEqualPass32; +extern draw ColourMask; +extern draw OverflowingDraw; +extern draw PerFragDiscard; +}; + namespace TextureZoo { enum class DataType diff --git a/util/test/demos/vk/vk_pixel_history.cpp b/util/test/demos/vk/vk_pixel_history.cpp index 7eec9aa09..4ac1ce909 100644 --- a/util/test/demos/vk/vk_pixel_history.cpp +++ b/util/test/demos/vk/vk_pixel_history.cpp @@ -30,14 +30,7 @@ RD_TEST(VK_Pixel_History, VulkanGraphicsTest) std::string common = R"EOSHADER( -#version 420 core - -struct v2f -{ - vec4 pos; - vec4 col; - vec4 uv; -}; +#version 460 core )EOSHADER"; @@ -45,56 +38,75 @@ struct v2f layout(location = 0) in vec3 Position; layout(location = 1) in vec4 Color; -layout(location = 2) in vec2 UV; -layout(location = 0) out v2f vertOut; +layout(location = 0) INTERP out COL_TYPE outCol; void main() { - vertOut.pos = vec4(Position.xyz, 1); - gl_Position = vertOut.pos; - vertOut.col = Color; - vertOut.uv = vec4(UV.xy, 0, 1); + gl_Position = vec4(Position.xyz, 1); + outCol = ProcessColor(Color); } )EOSHADER"; const std::string pixel = R"EOSHADER( -layout(location = 0) in v2f vertIn; +layout(location = 0) INTERP in COL_TYPE inCol; -layout(location = 0, index = 0) out vec4 Color; +#if COLOR == 1 +layout(location = 0, index = 0) out COL_TYPE Color; +#endif void main() { - if (gl_FragCoord.x < 151 && gl_FragCoord.x > 150) + if (gl_FragCoord.x < DISCARD_X+1 && gl_FragCoord.x > DISCARD_X) discard; - Color = vertIn.col + vec4(0, 0, 0, 1.75); + if (inCol.x > 10000 && inCol.y > 10000 && inCol.z > 10000) + discard; +#if COLOR == 1 + Color = inCol + COL_TYPE(0, 0, 0, ProcessColor(vec4(ALPHA_ADD)).x); +#endif } )EOSHADER"; std::string mspixel = R"EOSHADER( -#version 420 core -layout(location = 0, index = 0) out vec4 Color; +#if COLOR == 1 +layout(location = 0, index = 0) out COL_TYPE Color; +#endif void main() { - if(gl_PrimitiveID == 0) - { - Color = vec4(1, 0, 1, 2.75); - return; - } - +#if COLOR == 1 + vec4 col; if (gl_SampleID == 0) - Color = vec4(1, 0, 0, 2.75); + col = vec4(1, 0, 0, 1+ALPHA_ADD); else if (gl_SampleID == 1) - Color = vec4(0, 0, 1, 2.75); + col = vec4(0, 0, 1, 1+ALPHA_ADD); else if (gl_SampleID == 2) - Color = vec4(0, 1, 1, 2.75); + col = vec4(0, 1, 1, 1+ALPHA_ADD); else if (gl_SampleID == 3) - Color = vec4(1, 1, 1, 2.75); + col = vec4(1, 1, 1, 1+ALPHA_ADD); + Color = ProcessColor(col); +#endif +} + +)EOSHADER"; + + std::string comp = R"EOSHADER( + +layout(set = 0, binding = 0) writeonly uniform IMAGE_TYPE Output; + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main() +{ + vec4 data = vec4(3, 3, 3, 9); + + for(int x=0; x < 10; x++) + for(int y=0; y < 10; y++) + imageStore(Output, ivec3(STORE_X+x, STORE_Y+y, STORE_Z) STORE_SAMPLE, ProcessColor(data)); } )EOSHADER"; @@ -104,210 +116,273 @@ void main() features.depthBounds = true; features.geometryShader = true; features.sampleRateShading = true; + features.shaderStorageImageMultisample = true; + features.shaderStorageImageReadWithoutFormat = true; + features.shaderStorageImageWriteWithoutFormat = true; + + devExts.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME); VulkanGraphicsTest::Prepare(argc, argv); + + if(!Avail.empty()) + return; + + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(phys, VK_FORMAT_D32_SFLOAT_S8_UINT, &props); + if((props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) == 0) + { + Avail = "D32S8 not supported"; + return; + } } - int main() + struct VKTestBatch { - optDevExts.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME); + std::string name; - // initialise, create window, create context, etc - if(!Init()) - return 3; + bool secondary = false; + bool uint = false; - bool KHR_maintenance1 = std::find(devExts.begin(), devExts.end(), - VK_KHR_MAINTENANCE1_EXTENSION_NAME) != devExts.end(); + AllocatedImage colImg; + AllocatedImage depthImg; + + VkFormat depthFormat; + + VkDescriptorSet compset; + VkPipelineLayout compLayout; + VkPipeline comppipe; + + VkFramebuffer fb; + VkRenderPass rp; + + VkExtent2D extent; + + VkPipeline depthWritePipe; + VkPipeline fixedScissorPassPipe; + VkPipeline fixedScissorFailPipe; + VkPipeline dynamicStencilRefPipe; + VkPipeline dynamicStencilMaskPipe; + VkPipeline depthEqualPipe; + VkPipeline depthPipe; + VkPipeline stencilWritePipe; + VkPipeline backgroundPipe; + VkPipeline noFsPipe; + VkPipeline basicPipe; + VkPipeline colorMaskPipe; + VkPipeline cullFrontPipe; + VkPipeline depthBoundsPipe1; + VkPipeline depthBoundsPipe2; + VkPipeline sampleColourPipe; + }; + + std::vector batches; + + void BuildTestBatch(const std::string &name, VkSampleCountFlagBits sampleCount, + VkFormat colourFormat, VkFormat depthStencilFormat, int targetMip = -1, + int targetSlice = -1, int targetDepthSlice = -1) + { + batches.push_back({}); + + VKTestBatch &batch = batches.back(); + batch.name = name; + batch.extent = mainWindow->scissor.extent; + + uint32_t arraySize = targetSlice >= 0 ? 5 : 1; + uint32_t mips = targetMip >= 0 ? 4 : 1; + uint32_t depth = targetDepthSlice >= 0 ? 14 : 0; + + uint32_t mip = (uint32_t)std::max(0, targetMip); + uint32_t slice = (uint32_t)std::max(0, targetSlice); + if(depth > 1) + slice = (uint32_t)targetDepthSlice; 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)}, + std::string defines; + defines += + "#define COLOR " + std::string(colourFormat == VK_FORMAT_UNDEFINED ? "0" : "1") + "\n"; - // 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(1.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, - {Vec3f(0.0f, -0.9f, 0.95f), Vec4f(1.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, - {Vec3f(0.9f, 0.9f, 0.95f), Vec4f(1.0f, 0.0f, 1.0f, 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)}, - - // dynamic triangles - {Vec3f(-0.6f, 0.75f, 0.5f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, - {Vec3f(-0.5f, 0.65f, 0.5f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, - {Vec3f(-0.4f, 0.75f, 0.5f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, - - {Vec3f(-0.6f, 0.75f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, - {Vec3f(-0.5f, 0.65f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, - {Vec3f(-0.4f, 0.75f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, - - {Vec3f(-0.6f, 0.75f, 0.5f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, - {Vec3f(-0.5f, 0.65f, 0.5f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, - {Vec3f(-0.4f, 0.75f, 0.5f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, - - {Vec3f(-0.6f, 0.75f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, - {Vec3f(-0.5f, 0.65f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, - {Vec3f(-0.4f, 0.75f, 0.5f), Vec4f(0.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, - - // Different depth triangles - {Vec3f(0.0f, 0.8f, 0.97f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, - {Vec3f(0.4f, 0.2f, 0.97f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, - {Vec3f(0.8f, 0.8f, 0.97f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, - - {Vec3f(0.2f, 0.8f, 0.20f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, - {Vec3f(0.4f, 0.4f, 0.20f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, - {Vec3f(0.6f, 0.8f, 0.20f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, - - {Vec3f(0.2f, 0.8f, 0.30f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, - {Vec3f(0.4f, 0.6f, 0.30f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, - {Vec3f(0.6f, 0.8f, 0.30f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, - - {Vec3f(0.2f, 0.8f, 0.10f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, - {Vec3f(0.4f, 0.7f, 0.10f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, - {Vec3f(0.6f, 0.8f, 0.10f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, - - // Fails depth bounds test. - {Vec3f(0.2f, 0.8f, 0.05f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, - {Vec3f(0.4f, 0.7f, 0.05f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, - {Vec3f(0.6f, 0.8f, 0.05f), Vec4f(1.0f, 1.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, - - // Should be back face culled. - {Vec3f(0.6f, 0.8f, 0.25f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, - {Vec3f(0.4f, 0.7f, 0.25f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, - {Vec3f(0.2f, 0.8f, 0.25f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, - - // depth bounds prep - {Vec3f(0.6f, -0.3f, 0.3f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, - {Vec3f(0.7f, -0.5f, 0.5f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, - {Vec3f(0.8f, -0.3f, 0.7f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, - // depth bounds clip - {Vec3f(0.6f, -0.3f, 0.3f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, - {Vec3f(0.7f, -0.5f, 0.5f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, - {Vec3f(0.8f, -0.3f, 0.7f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)}, - }; - - // negate y if we're using negative viewport height - if(KHR_maintenance1) + std::string imgprefix; + if(colourFormat == VK_FORMAT_R8G8B8A8_UINT || colourFormat == VK_FORMAT_R16G16B16A16_UINT || + colourFormat == VK_FORMAT_R32G32B32A32_UINT) { - for(DefaultA2V &v : VBData) - v.pos.y = -v.pos.y; + batch.uint = true; + imgprefix = "u"; + defines += R"( + +#define COL_TYPE uvec4 +#define ALPHA_ADD 3 +#define INTERP flat + +uvec4 ProcessColor(vec4 col) +{ + uvec4 ret = uvec4(16*col); + + if(col.x < 0.0f) ret.x = 0xfffffff0; + if(col.y < 0.0f) ret.y = 0xfffffff0; + if(col.z < 0.0f) ret.z = 0xfffffff0; + + + return ret; +} + +)"; + } + else + { + defines += R"( + +#define COL_TYPE vec4 +#define ALPHA_ADD 1.75 +#define INTERP + +vec4 ProcessColor(vec4 col) +{ + vec4 ret = col; + + // this obviously won't overflow F32 but it will overflow anything 16-bit and under + if(col.x < 0.0f) ret.x = 100000.0f; + if(col.y < 0.0f) ret.y = 100000.0f; + if(col.z < 0.0f) ret.z = 100000.0f; + + return ret; +} + +)"; } - 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})); + defines += "#define DISCARD_X " + std::to_string(150 >> mip) + "\n"; + defines += "#define IMAGE_TYPE " + + std::string(depth > 1 ? imgprefix + "image3D" + : sampleCount != VK_SAMPLE_COUNT_1_BIT ? imgprefix + "image2DMSArray" + : imgprefix + "image2DArray") + + "\n"; + defines += "#define STORE_X " + std::to_string(220 >> mip) + "\n"; + defines += "#define STORE_Y " + std::to_string(80 >> mip) + "\n"; + defines += "#define STORE_Z " + std::to_string(slice) + "\n"; + defines += "#define STORE_SAMPLE " + + std::string(sampleCount != VK_SAMPLE_COUNT_1_BIT ? ", 0" : "") + "\n"; + defines += "\n\n"; - vb.upload(VBData); + VkPipelineShaderStageCreateInfo vertexShader = + CompileShaderModule(common + defines + vertex, ShaderLang::glsl, ShaderStage::vert, "main"); + VkPipelineShaderStageCreateInfo fragmentShader = + CompileShaderModule(common + defines + pixel, ShaderLang::glsl, ShaderStage::frag, "main"); - VkFormat depthStencilFormat = VK_FORMAT_UNDEFINED; - { - std::vector formats; - for(VkFormat fmt : - {VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT}) - { - VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(phys, fmt, &props); - if(props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) - { - depthStencilFormat = fmt; - break; - } - } - TEST_ASSERT(depthStencilFormat != VK_FORMAT_UNDEFINED, - "Couldn't find depth/stencil attachment image format"); - } - - // create depth-stencil image - AllocatedImage depthimg( - this, - vkh::ImageCreateInfo(mainWindow->scissor.extent.width, mainWindow->scissor.extent.height, 0, - depthStencilFormat, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT), - VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); - setName(depthimg.image, "depthimg"); - - VkImageView dsvview = createImageView(vkh::ImageViewCreateInfo( - depthimg.image, VK_IMAGE_VIEW_TYPE_2D, depthStencilFormat, {}, - 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( - depthStencilFormat, 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)); + std::vector cols; + std::vector atts; + VkImageView colView = VK_NULL_HANDLE; - renderPassCreateInfo.addSubpass({VkAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL})}, 1, + if(colourFormat != VK_FORMAT_UNDEFINED) + { + batch.colImg = AllocatedImage( + this, + vkh::ImageCreateInfo(batch.extent.width, batch.extent.height, depth, colourFormat, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_STORAGE_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + mips, arraySize, sampleCount, + depth > 1 ? VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT : 0), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + setName(batch.colImg.image, batch.name + " colImg"); + + colView = createImageView(vkh::ImageViewCreateInfo( + batch.colImg.image, VK_IMAGE_VIEW_TYPE_2D_ARRAY, colourFormat, {}, + vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, mip, 1, slice, 1))); + setName(colView, batch.name + " colView"); + + VkImageView storeView = createImageView(vkh::ImageViewCreateInfo( + batch.colImg.image, depth > 1 ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D_ARRAY, + colourFormat, {}, + vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, mip, 1, 0, + VK_REMAINING_ARRAY_LAYERS))); + setName(storeView, batch.name + " storeView"); + + atts.push_back(colView); + + renderPassCreateInfo.attachments.push_back(vkh::AttachmentDescription( + colourFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, + VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE, sampleCount)); + + cols = {VkAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL})}; + + VkDescriptorSetLayout compsetlayout = + createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo({ + {0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT}, + })); + batch.compLayout = createPipelineLayout(vkh::PipelineLayoutCreateInfo({compsetlayout})); + + batch.comppipe = createComputePipeline(vkh::ComputePipelineCreateInfo( + batch.compLayout, CompileShaderModule(common + defines + comp, ShaderLang::glsl, + ShaderStage::comp, "main"))); + + batch.compset = allocateDescriptorSet(compsetlayout); + + vkh::updateDescriptorSets( + device, + { + vkh::WriteDescriptorSet( + batch.compset, 0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + {vkh::DescriptorImageInfo(storeView, VK_IMAGE_LAYOUT_GENERAL, VK_NULL_HANDLE)}), + }); + } + + batch.depthFormat = depthStencilFormat; + VkImageView depthView = VK_NULL_HANDLE; + + if(depthStencilFormat != VK_FORMAT_UNDEFINED) + { + // can't create 2D views of 3D with depth images, so we just use a normal 2D array image + if(depth > 1) + { + arraySize = depth; + depth = 0; + } + + batch.depthImg = AllocatedImage( + this, + vkh::ImageCreateInfo(batch.extent.width, batch.extent.height, 0, depthStencilFormat, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, mips, arraySize, + sampleCount), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + setName(batch.depthImg.image, batch.name + " depthImg"); + + VkImageAspectFlags depthAspects = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + if(depthStencilFormat == VK_FORMAT_D16_UNORM || depthStencilFormat == VK_FORMAT_D32_SFLOAT) + depthAspects = VK_IMAGE_ASPECT_DEPTH_BIT; + + depthView = createImageView( + vkh::ImageViewCreateInfo(batch.depthImg.image, VK_IMAGE_VIEW_TYPE_2D, depthStencilFormat, + {}, vkh::ImageSubresourceRange(depthAspects, mip, 1, slice, 1))); + setName(depthView, batch.name + " depthView"); + + atts.push_back(depthView); + + renderPassCreateInfo.attachments.push_back(vkh::AttachmentDescription( + depthStencilFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, + VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE, sampleCount, + VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE)); + } + + renderPassCreateInfo.addSubpass(cols, + depthView == VK_NULL_HANDLE + ? VK_ATTACHMENT_UNUSED + : (uint32_t)renderPassCreateInfo.attachments.size() - 1, VK_IMAGE_LAYOUT_GENERAL); - VkRenderPass renderPass = createRenderPass(renderPassCreateInfo); + batch.rp = createRenderPass(renderPassCreateInfo); - // create framebuffers using swapchain images and DS image - std::vector fbs; - fbs.resize(mainWindow->GetCount()); + batch.extent.width >>= mip; + batch.extent.height >>= mip; - for(size_t i = 0; i < mainWindow->GetCount(); i++) - fbs[i] = createFramebuffer(vkh::FramebufferCreateInfo( - renderPass, {mainWindow->GetView(i), dsvview}, mainWindow->scissor.extent)); + batch.fb = createFramebuffer(vkh::FramebufferCreateInfo(batch.rp, atts, batch.extent)); - // create PSO vkh::GraphicsPipelineCreateInfo pipeCreateInfo; + pipeCreateInfo.renderPass = batch.rp; + pipeCreateInfo.layout = layout; - pipeCreateInfo.renderPass = renderPass; pipeCreateInfo.vertexInputState.vertexBindingDescriptions = {vkh::vertexBind(0, DefaultA2V)}; pipeCreateInfo.vertexInputState.vertexAttributeDescriptions = { @@ -316,15 +391,11 @@ void main() vkh::vertexAttr(2, 0, DefaultA2V, uv), }; - VkPipelineShaderStageCreateInfo vertexShader = - CompileShaderModule(common + vertex, ShaderLang::glsl, ShaderStage::vert, "main"); - VkPipelineShaderStageCreateInfo fragmentShader = - CompileShaderModule(common + pixel, ShaderLang::glsl, ShaderStage::frag, "main"); - pipeCreateInfo.stages = {vertexShader, fragmentShader}; pipeCreateInfo.rasterizationState.depthClampEnable = VK_FALSE; pipeCreateInfo.rasterizationState.cullMode = VK_CULL_MODE_BACK_BIT; + pipeCreateInfo.multisampleState.rasterizationSamples = sampleCount; pipeCreateInfo.depthStencilState.depthTestEnable = VK_TRUE; pipeCreateInfo.depthStencilState.depthWriteEnable = VK_TRUE; @@ -337,350 +408,457 @@ void main() pipeCreateInfo.depthStencilState.back = pipeCreateInfo.depthStencilState.front; pipeCreateInfo.depthStencilState.depthCompareOp = VK_COMPARE_OP_ALWAYS; - VkPipeline depthWritePipe = createGraphicsPipeline(pipeCreateInfo); + batch.depthWritePipe = createGraphicsPipeline(pipeCreateInfo); + setName(batch.depthWritePipe, batch.name + " depthWritePipe"); + + { + vkh::GraphicsPipelineCreateInfo depthPipeInfo = pipeCreateInfo; + depthPipeInfo.depthStencilState.depthWriteEnable = VK_FALSE; + depthPipeInfo.depthStencilState.depthTestEnable = VK_TRUE; + depthPipeInfo.depthStencilState.depthCompareOp = VK_COMPARE_OP_EQUAL; + + batch.depthEqualPipe = createGraphicsPipeline(depthPipeInfo); + setName(batch.depthEqualPipe, batch.name + " depthEqualPipe"); + } - VkPipeline dynamicScissorPipe, fixedScissorPassPipe, fixedScissorFailPipe, - dynamicStencilRefPipe, dynamicStencilMaskPipe; { vkh::GraphicsPipelineCreateInfo dynamicPipe = pipeCreateInfo; dynamicPipe.depthStencilState.depthWriteEnable = VK_FALSE; dynamicPipe.depthStencilState.depthTestEnable = VK_FALSE; - dynamicScissorPipe = createGraphicsPipeline(dynamicPipe); - setName(dynamicScissorPipe, "dynamicScissorPipe"); - dynamicPipe.dynamicState.dynamicStates = {VK_DYNAMIC_STATE_VIEWPORT}; - dynamicPipe.viewportState.scissors = {{{95, 245}, {10, 10}}}; + dynamicPipe.viewportState.scissors = {{{95 >> mip, 245 >> mip}, {20U >> mip, 8U >> mip}}}; - fixedScissorPassPipe = createGraphicsPipeline(dynamicPipe); - setName(fixedScissorPassPipe, "fixedScissorPassPipe"); + batch.fixedScissorPassPipe = createGraphicsPipeline(dynamicPipe); + setName(batch.fixedScissorPassPipe, batch.name + " fixedScissorPassPipe"); - dynamicPipe.viewportState.scissors = {{{95, 245}, {4, 4}}}; + dynamicPipe.viewportState.scissors = {{{95 >> mip, 245 >> mip}, {8U >> mip, 8U >> mip}}}; - fixedScissorFailPipe = createGraphicsPipeline(dynamicPipe); - setName(fixedScissorFailPipe, "fixedScissorFailPipe"); + batch.fixedScissorFailPipe = createGraphicsPipeline(dynamicPipe); + setName(batch.fixedScissorFailPipe, batch.name + " fixedScissorFailPipe"); dynamicPipe.dynamicState.dynamicStates.push_back(VK_DYNAMIC_STATE_SCISSOR); dynamicPipe.dynamicState.dynamicStates.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE); - dynamicStencilRefPipe = createGraphicsPipeline(dynamicPipe); - setName(dynamicStencilRefPipe, "dynamicStencilRefPipe"); + batch.dynamicStencilRefPipe = createGraphicsPipeline(dynamicPipe); + setName(batch.dynamicStencilRefPipe, batch.name + " dynamicStencilRefPipe"); dynamicPipe.dynamicState.dynamicStates.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK); dynamicPipe.dynamicState.dynamicStates.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); - dynamicStencilMaskPipe = createGraphicsPipeline(dynamicPipe); - setName(dynamicStencilMaskPipe, "dynamicStencilMaskPipe"); + batch.dynamicStencilMaskPipe = createGraphicsPipeline(dynamicPipe); + setName(batch.dynamicStencilMaskPipe, batch.name + " dynamicStencilMaskPipe"); } pipeCreateInfo.depthStencilState.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; - VkPipeline depthPipe; { vkh::GraphicsPipelineCreateInfo depthPipeInfo = pipeCreateInfo; depthPipeInfo.dynamicState.dynamicStates.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS); depthPipeInfo.depthStencilState.depthBoundsTestEnable = VK_TRUE; - depthPipe = createGraphicsPipeline(depthPipeInfo); - setName(depthPipe, "depthPipe"); + batch.depthPipe = createGraphicsPipeline(depthPipeInfo); + setName(batch.depthPipe, batch.name + " depthPipe"); } pipeCreateInfo.depthStencilState.stencilTestEnable = VK_TRUE; - VkPipeline stencilWritePipe = createGraphicsPipeline(pipeCreateInfo); + batch.stencilWritePipe = createGraphicsPipeline(pipeCreateInfo); + setName(batch.stencilWritePipe, batch.name + " stencilWritePipe"); pipeCreateInfo.depthStencilState.stencilTestEnable = VK_FALSE; - VkPipeline backgroundPipe = createGraphicsPipeline(pipeCreateInfo); + batch.backgroundPipe = createGraphicsPipeline(pipeCreateInfo); + setName(batch.backgroundPipe, batch.name + " backgroundPipe"); pipeCreateInfo.stages = {vertexShader}; pipeCreateInfo.depthStencilState.stencilTestEnable = VK_TRUE; pipeCreateInfo.depthStencilState.front.reference = 0x33; - VkPipeline noFsPipe = createGraphicsPipeline(pipeCreateInfo); + batch.noFsPipe = createGraphicsPipeline(pipeCreateInfo); + setName(batch.noFsPipe, batch.name + " noFsPipe"); pipeCreateInfo.stages = {vertexShader, fragmentShader}; pipeCreateInfo.depthStencilState.stencilTestEnable = VK_FALSE; pipeCreateInfo.depthStencilState.front.reference = 0x55; pipeCreateInfo.depthStencilState.stencilTestEnable = VK_TRUE; pipeCreateInfo.depthStencilState.front.compareOp = VK_COMPARE_OP_GREATER; - VkPipeline pipe = createGraphicsPipeline(pipeCreateInfo); + batch.basicPipe = createGraphicsPipeline(pipeCreateInfo); + setName(batch.basicPipe, batch.name + " basicPipe"); pipeCreateInfo.depthStencilState.stencilTestEnable = VK_FALSE; + { + vkh::GraphicsPipelineCreateInfo maskPipeInfo = pipeCreateInfo; + maskPipeInfo.colorBlendState.attachments[0].colorWriteMask = 0x3; + batch.colorMaskPipe = createGraphicsPipeline(maskPipeInfo); + setName(batch.colorMaskPipe, batch.name + " colorMaskPipe"); + } + pipeCreateInfo.rasterizationState.cullMode = VK_CULL_MODE_FRONT_BIT; - VkPipeline cullFrontPipe = createGraphicsPipeline(pipeCreateInfo); + batch.cullFrontPipe = createGraphicsPipeline(pipeCreateInfo); + setName(batch.cullFrontPipe, batch.name + " cullFrontPipe"); pipeCreateInfo.rasterizationState.cullMode = VK_CULL_MODE_BACK_BIT; pipeCreateInfo.depthStencilState.depthBoundsTestEnable = VK_TRUE; pipeCreateInfo.depthStencilState.minDepthBounds = 0.0f; pipeCreateInfo.depthStencilState.maxDepthBounds = 1.0f; - VkPipeline depthBoundsPipe1 = createGraphicsPipeline(pipeCreateInfo); + batch.depthBoundsPipe1 = createGraphicsPipeline(pipeCreateInfo); + setName(batch.depthBoundsPipe1, batch.name + " depthBoundsPipe1"); pipeCreateInfo.depthStencilState.minDepthBounds = 0.4f; pipeCreateInfo.depthStencilState.maxDepthBounds = 0.6f; - VkPipeline depthBoundsPipe2 = createGraphicsPipeline(pipeCreateInfo); + batch.depthBoundsPipe2 = createGraphicsPipeline(pipeCreateInfo); + setName(batch.depthBoundsPipe2, batch.name + " depthBoundsPipe2"); pipeCreateInfo.depthStencilState.depthBoundsTestEnable = VK_FALSE; - renderPassCreateInfo.attachments.pop_back(); - renderPassCreateInfo.subpasses[0].pDepthStencilAttachment = NULL; + pipeCreateInfo.stages[1] = + CompileShaderModule(common + defines + mspixel, ShaderLang::glsl, ShaderStage::frag, "main"); - VkRenderPass subrp = createRenderPass(renderPassCreateInfo); + pipeCreateInfo.depthStencilState.depthWriteEnable = VK_TRUE; + VkSampleMask not3 = 0x7; + if(sampleCount == VK_SAMPLE_COUNT_4_BIT) + pipeCreateInfo.multisampleState.pSampleMask = ¬3; + batch.sampleColourPipe = createGraphicsPipeline(pipeCreateInfo); + setName(batch.sampleColourPipe, batch.name + " sampleColourPipe"); + } - pipeCreateInfo.renderPass = subrp; - pipeCreateInfo.depthStencilState.stencilTestEnable = VK_FALSE; - pipeCreateInfo.depthStencilState.depthCompareOp = VK_COMPARE_OP_ALWAYS; - VkPipeline whitepipe = createGraphicsPipeline(pipeCreateInfo); + void RunDraw(VkCommandBuffer cmd, const PixelHistory::draw &draw, uint32_t numInstances = 1) + { + vkCmdDraw(cmd, draw.count, numInstances, draw.first, 0); + } - 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})); - setName(subimg.image, "subimg"); + void RunBatch(const VKTestBatch &b, VkCommandBuffer cmd) + { + float factor = float(b.extent.width) / float(mainWindow->scissor.extent.width); - 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))); + VkViewport v = {}; + v.width = (float)b.extent.width - (20.0f * factor); + v.height = -(float)b.extent.height + (20.0f * factor); + v.x = floorf(10.0f * factor); + v.y = (float)b.extent.height - ceilf(10.0f * factor); + v.maxDepth = 1.0f; - VkFramebuffer subfb = createFramebuffer(vkh::FramebufferCreateInfo( - subrp, {subview}, - {mainWindow->scissor.extent.width / 4, mainWindow->scissor.extent.height / 4})); + VkRect2D scissor = {{0, 0}, b.extent}; - // Multi sampled + vkCmdSetViewport(cmd, 0, 1, &v); + vkCmdSetScissor(cmd, 0, 1, &scissor); + // draw the setup triangles + + pushMarker(cmd, "Setup"); { - std::vector formats; - for(VkFormat fmt : - {VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT}) + setMarker(cmd, "Depth Write"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.depthWritePipe); + RunDraw(cmd, PixelHistory::DepthWrite); + + setMarker(cmd, "Depth Equal Setup"); + RunDraw(cmd, PixelHistory::DepthEqualSetup); + + setMarker(cmd, "Unbound Fragment Shader"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.noFsPipe); + RunDraw(cmd, PixelHistory::UnboundPS); + + setMarker(cmd, "Stencil Write"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.stencilWritePipe); + RunDraw(cmd, PixelHistory::StencilWrite); + + setMarker(cmd, "Background"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.backgroundPipe); + RunDraw(cmd, PixelHistory::Background); + + setMarker(cmd, "Cull Front"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.cullFrontPipe); + RunDraw(cmd, PixelHistory::CullFront); + + setMarker(cmd, "Depth Bounds Prep"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.depthBoundsPipe1); + RunDraw(cmd, PixelHistory::DepthBoundsPrep); + setMarker(cmd, "Depth Bounds Clip"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.depthBoundsPipe2); + RunDraw(cmd, PixelHistory::DepthBoundsClip); + } + popMarker(cmd); + + pushMarker(cmd, "Stress Test"); + { + pushMarker(cmd, "Lots of Drawcalls"); { - VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(phys, fmt, &props); - if(props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT & - VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) - { - depthStencilFormat = fmt; - break; - } + setMarker(cmd, "300 Draws"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.depthWritePipe); + for(int d = 0; d < 300; ++d) + RunDraw(cmd, PixelHistory::Draws300); } - TEST_ASSERT(depthStencilFormat != VK_FORMAT_UNDEFINED, - "Couldn't find depth/stencil attachment image format"); + popMarker(cmd); + + setMarker(cmd, "300 Instances"); + RunDraw(cmd, PixelHistory::Instances300, 300); + } + popMarker(cmd); + + setMarker(cmd, "Simple Test"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.basicPipe); + RunDraw(cmd, PixelHistory::MainTest); + + setMarker(cmd, "Scissor Fail"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.fixedScissorFailPipe); + RunDraw(cmd, PixelHistory::ScissorFail); + + setMarker(cmd, "Scissor Pass"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.fixedScissorPassPipe); + RunDraw(cmd, PixelHistory::ScissorPass); + + setMarker(cmd, "Stencil Ref"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.dynamicStencilRefPipe); + vkCmdSetScissor(cmd, 0, 1, &scissor); + vkCmdSetStencilReference(cmd, VK_STENCIL_FACE_FRONT_AND_BACK, 0x67); + RunDraw(cmd, PixelHistory::StencilRef); + + setMarker(cmd, "Stencil Mask"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.dynamicStencilMaskPipe); + vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FACE_FRONT_AND_BACK, 0); + vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FACE_FRONT_AND_BACK, 0); + RunDraw(cmd, PixelHistory::StencilMask); + + setMarker(cmd, "Depth Test"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.depthPipe); + vkCmdSetDepthBounds(cmd, 0.15f, 1.0f); + RunDraw(cmd, PixelHistory::DepthTest); + + setMarker(cmd, "Sample Colouring"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.sampleColourPipe); + RunDraw(cmd, PixelHistory::SampleColour); + + setMarker(cmd, "Depth Equal Fail"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.depthEqualPipe); + RunDraw(cmd, PixelHistory::DepthEqualFail); + + setMarker(cmd, "Depth Equal Pass"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.depthEqualPipe); + if(b.depthFormat == VK_FORMAT_D24_UNORM_S8_UINT) + RunDraw(cmd, PixelHistory::DepthEqualPass24); + else if(b.depthFormat == VK_FORMAT_D16_UNORM_S8_UINT || b.depthFormat == VK_FORMAT_D16_UNORM) + RunDraw(cmd, PixelHistory::DepthEqualPass16); + else + RunDraw(cmd, PixelHistory::DepthEqualPass32); + + setMarker(cmd, "Colour Masked"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.colorMaskPipe); + RunDraw(cmd, PixelHistory::ColourMask); + + setMarker(cmd, "Overflowing"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.backgroundPipe); + RunDraw(cmd, PixelHistory::OverflowingDraw); + + setMarker(cmd, "Per-Fragment discarding"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.backgroundPipe); + RunDraw(cmd, PixelHistory::PerFragDiscard); + } + + int main() + { + // initialise, create window, create context, etc + if(!Init()) + return 3; + + PixelHistory::init(); + + AllocatedBuffer vb( + this, + vkh::BufferCreateInfo(sizeof(DefaultA2V) * PixelHistory::vb.size(), + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + vb.upload(PixelHistory::vb.data(), PixelHistory::vb.size() * sizeof(DefaultA2V)); + + VkImage bbBlitSource = VK_NULL_HANDLE; + + struct DepthVariant + { + VkFormat fmt; + std::string name; + bool supported; + }; + + std::vector depthFormats = {{VK_FORMAT_D24_UNORM_S8_UINT, "D24S8", false}, + {VK_FORMAT_D16_UNORM_S8_UINT, "D16S8", false}, + {VK_FORMAT_D32_SFLOAT, "D32", false}, + {VK_FORMAT_D16_UNORM, "D16", false}}; + for(DepthVariant &fmt : depthFormats) + { + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(phys, fmt.fmt, &props); + if(props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) + fmt.supported = true; } - renderPassCreateInfo.attachments[0].samples = VK_SAMPLE_COUNT_4_BIT; - renderPassCreateInfo.attachments.push_back(vkh::AttachmentDescription( - depthStencilFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, - VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_SAMPLE_COUNT_4_BIT, - VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_DONT_CARE)); - renderPassCreateInfo.subpasses.clear(); - renderPassCreateInfo.addSubpass({VkAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL})}, 1, - VK_IMAGE_LAYOUT_GENERAL); + // work around an NV bug with D32S8 when rendering to 3D textures is happening, prefer D24S8 + VkFormat defaultDepthFormat = + depthFormats[0].supported ? VK_FORMAT_D24_UNORM_S8_UINT : VK_FORMAT_D32_SFLOAT_S8_UINT; - VkRenderPass submsrp = createRenderPass(renderPassCreateInfo); + BuildTestBatch("Basic", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, defaultDepthFormat); + bbBlitSource = batches.back().colImg.image; - pipeCreateInfo.stages[1] = - CompileShaderModule(mspixel, ShaderLang::glsl, ShaderStage::frag, "main"); + BuildTestBatch("MSAA", VK_SAMPLE_COUNT_4_BIT, VK_FORMAT_R8G8B8A8_UNORM, defaultDepthFormat); + BuildTestBatch("Colour Only", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, + VK_FORMAT_UNDEFINED); + BuildTestBatch("Depth Only", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_UNDEFINED, defaultDepthFormat); + BuildTestBatch("Mip & Slice", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, + defaultDepthFormat, 2, 3); + BuildTestBatch("Slice MSAA", VK_SAMPLE_COUNT_4_BIT, VK_FORMAT_R8G8B8A8_UNORM, + defaultDepthFormat, -1, 3); + BuildTestBatch("Secondary", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, defaultDepthFormat); + batches.back().secondary = true; - pipeCreateInfo.renderPass = submsrp; - pipeCreateInfo.multisampleState.rasterizationSamples = VK_SAMPLE_COUNT_4_BIT; - pipeCreateInfo.depthStencilState.depthWriteEnable = VK_TRUE; - VkPipeline mspipe = createGraphicsPipeline(pipeCreateInfo); + BuildTestBatch("3D texture", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, + defaultDepthFormat, -1, -1, 8); - AllocatedImage submsimg( - this, - vkh::ImageCreateInfo(mainWindow->scissor.extent.width, mainWindow->scissor.extent.height, 0, - mainWindow->format, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 1, 4, - VK_SAMPLE_COUNT_4_BIT), - VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); - setName(submsimg.image, "submsimg"); - - VkImageView submsview = createImageView(vkh::ImageViewCreateInfo( - submsimg.image, VK_IMAGE_VIEW_TYPE_2D, mainWindow->format, {}, - vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 2, 1))); - - AllocatedImage msimgdepth( - this, - vkh::ImageCreateInfo(mainWindow->scissor.extent.width, mainWindow->scissor.extent.height, 0, - depthStencilFormat, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 1, 4, - VK_SAMPLE_COUNT_4_BIT), - VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); - setName(msimgdepth.image, "msimgdepth"); - - VkImageView msdepthview = createImageView(vkh::ImageViewCreateInfo( - msimgdepth.image, VK_IMAGE_VIEW_TYPE_2D, depthStencilFormat, {}, - vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, 0, 1, 2, - 1))); - - VkFramebuffer submsfb = createFramebuffer(vkh::FramebufferCreateInfo( - submsrp, {submsview, msdepthview}, - {mainWindow->scissor.extent.width, mainWindow->scissor.extent.height})); - - while(Running()) + for(DepthVariant &fmt : depthFormats) { + if(fmt.supported) + BuildTestBatch(fmt.name, VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, fmt.fmt); + } + + BuildTestBatch("F16 UNORM", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R16G16B16A16_UNORM, + defaultDepthFormat); + BuildTestBatch("F16 FLOAT", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R16G16B16A16_SFLOAT, + defaultDepthFormat); + BuildTestBatch("F32 FLOAT", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R32G32B32A32_SFLOAT, + defaultDepthFormat); + + BuildTestBatch("8-bit uint", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UINT, defaultDepthFormat); + BuildTestBatch("16-bit uint", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R16G16B16A16_UINT, + defaultDepthFormat); + BuildTestBatch("32-bit uint", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R32G32B32A32_UINT, + defaultDepthFormat); + + // for dispatch access, ensure all color images are in GENERAL as render passes for slice tests + // will only transition that one slice + for(const VKTestBatch &b : batches) + { + if(b.colImg.image == VK_NULL_HANDLE) + continue; + 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, "Unbound Fragment Shader"); - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, noFsPipe); - vkCmdDraw(cmd, 3, 1, 3, 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); - - setMarker(cmd, "Depth Bounds Prep"); - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, depthBoundsPipe1); - vkCmdDraw(cmd, 3, 1, 63, 0); - setMarker(cmd, "Depth Bounds Clip"); - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, depthBoundsPipe2); - vkCmdDraw(cmd, 3, 1, 66, 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); - - setMarker(cmd, "Fixed Scissor Fail"); - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, fixedScissorFailPipe); - vkCmdDraw(cmd, 3, 1, 33, 0); - - setMarker(cmd, "Fixed Scissor Pass"); - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, fixedScissorPassPipe); - vkCmdDraw(cmd, 3, 1, 36, 0); - - setMarker(cmd, "Dynamic Stencil Ref"); - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, dynamicStencilRefPipe); - vkCmdSetScissor(cmd, 0, 1, &mainWindow->scissor); - vkCmdSetStencilReference(cmd, VK_STENCIL_FACE_FRONT_AND_BACK, 0x67); - vkCmdDraw(cmd, 3, 1, 39, 0); - - setMarker(cmd, "Dynamic Stencil Mask"); - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, dynamicStencilMaskPipe); - vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FACE_FRONT_AND_BACK, 0); - vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FACE_FRONT_AND_BACK, 0); - vkCmdDraw(cmd, 3, 1, 42, 0); - - // Six triangles, five fragments reported. - // 0: Fails depth test - // 1: Passes - // 2: Fails depth test compared to 1st fragment - // 3: Passes - // 4: Fails depth bounds test - // 5: Fails backface culling, not reported. - setMarker(cmd, "Depth Test"); - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, depthPipe); - vkCmdSetDepthBounds(cmd, 0.15f, 1.0f); - vkCmdDraw(cmd, 6 * 3, 1, 45, 0); - - vkCmdEndRenderPass(cmd); - - { - setMarker(cmd, "Multisampled: begin renderpass"); - vkCmdBeginRenderPass(cmd, - vkh::RenderPassBeginInfo( - submsrp, submsfb, mainWindow->scissor, - {vkh::ClearValue(0.f, 1.0f, 0.f, 1.0f), vkh::ClearValue(0.f, 0)}), - VK_SUBPASS_CONTENTS_INLINE); - - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, mspipe); - - setMarker(cmd, "Multisampled: test"); - vkCmdDraw(cmd, 6, 1, 3, 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; - - setMarker(cmd, "Begin RenderPass Secondary"); - vkCmdBeginRenderPass( - cmd, vkh::RenderPassBeginInfo(subrp, subfb, s, {vkh::ClearValue(0.f, 1.0f, 0.f, 1.0f)}), - VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); - - std::vector secondaries; - // Record the first secondary command buffer. - { - VkCommandBuffer cmd2 = GetCommandBuffer(VK_COMMAND_BUFFER_LEVEL_SECONDARY); - secondaries.push_back(cmd2); - vkBeginCommandBuffer( - cmd2, vkh::CommandBufferBeginInfo(VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, - vkh::CommandBufferInheritanceInfo(subrp, 0, subfb))); - vkh::cmdBindVertexBuffers(cmd2, 0, {vb.buffer}, {0}); - vkCmdBindPipeline(cmd2, VK_PIPELINE_BIND_POINT_GRAPHICS, whitepipe); - vkCmdSetViewport(cmd2, 0, 1, &v); - vkCmdSetScissor(cmd2, 0, 1, &s); - setMarker(cmd2, "Secondary: background"); - vkCmdDraw(cmd2, 6, 1, 3, 0); - setMarker(cmd2, "Secondary: culled"); - vkCmdDraw(cmd2, 6, 1, 12, 0); - setMarker(cmd2, "Secondary: pink"); - vkCmdDraw(cmd2, 9, 1, 24, 0); - setMarker(cmd2, "Secondary: red and blue"); - vkCmdDraw(cmd2, 6, 1, 0, 0); - vkEndCommandBuffer(cmd2); - } - - setMarker(cmd, "Secondary Test"); - vkCmdExecuteCommands(cmd, (uint32_t)secondaries.size(), secondaries.data()); - - vkCmdEndRenderPass(cmd); - - FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + vkh::cmdPipelineBarrier( + cmd, {vkh::ImageMemoryBarrier(VK_ACCESS_NONE, VK_ACCESS_MEMORY_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, + b.colImg.image)}); vkEndCommandBuffer(cmd); - Submit(0, 1, {cmd}, secondaries); + + Submit(99, 99, {cmd}); + } + + while(Running()) + { + std::vector primaries; + std::vector secondaries; + + for(const VKTestBatch &b : batches) + { + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0}); + + // set clears correctly for depth-only batches, the extra clear value on colour only is ignored + std::vector clears; + if(b.colImg.image == VK_NULL_HANDLE) + { + clears = {vkh::ClearValue(1.0f, 0)}; + } + else + { + clears = {vkh::ClearValue(0.2f, 0.2f, 0.2f, 1.0f), vkh::ClearValue(1.0f, 0)}; + if(b.uint) + { + clears[0].color.uint32[0] = 80; + clears[0].color.uint32[1] = 80; + clears[0].color.uint32[2] = 80; + clears[0].color.uint32[3] = 16; + } + } + + if(b.secondary) + { + pushMarker(cmd, "Batch: " + b.name); + { + setMarker(cmd, "Begin RenderPass"); + vkCmdBeginRenderPass(cmd, + vkh::RenderPassBeginInfo(b.rp, b.fb, {{0, 0}, b.extent}, clears), + VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); + + VkCommandBuffer cmd2 = GetCommandBuffer(VK_COMMAND_BUFFER_LEVEL_SECONDARY); + secondaries.push_back(cmd2); + vkBeginCommandBuffer( + cmd2, vkh::CommandBufferBeginInfo(VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, + vkh::CommandBufferInheritanceInfo(b.rp, 0, b.fb))); + vkh::cmdBindVertexBuffers(cmd2, 0, {vb.buffer}, {0}); + RunBatch(b, cmd2); + vkEndCommandBuffer(cmd2); + + vkCmdExecuteCommands(cmd, 1, &cmd2); + + vkCmdEndRenderPass(cmd); + } + popMarker(cmd); + } + else + { + pushMarker(cmd, "Batch: " + b.name); + { + setMarker(cmd, "Begin RenderPass"); + + vkCmdBeginRenderPass(cmd, + vkh::RenderPassBeginInfo(b.rp, b.fb, {{0, 0}, b.extent}, clears), + VK_SUBPASS_CONTENTS_INLINE); + + RunBatch(b, cmd); + + vkCmdEndRenderPass(cmd); + + setMarker(cmd, "Compute write"); + if(b.comppipe != VK_NULL_HANDLE) + { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, b.comppipe); + vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, b.compLayout, 0, + {b.compset}, {}); + vkCmdDispatch(cmd, 1, 1, 1); + } + } + popMarker(cmd); + } + + vkEndCommandBuffer(cmd); + + primaries.push_back(cmd); + } + + { + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + if(bbBlitSource != VK_NULL_HANDLE) + { + vkh::cmdPipelineBarrier( + cmd, {vkh::ImageMemoryBarrier( + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, + VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, + VK_IMAGE_LAYOUT_GENERAL, bbBlitSource)}); + blitToSwap(cmd, bbBlitSource, VK_IMAGE_LAYOUT_GENERAL, + mainWindow->GetImage(mainWindow->imgIndex), VK_IMAGE_LAYOUT_GENERAL); + } + + FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + vkEndCommandBuffer(cmd); + + primaries.push_back(cmd); + } + + Submit(0, 1, primaries, secondaries); Present(); } diff --git a/util/test/rdtest/__init__.py b/util/test/rdtest/__init__.py index 3233e5cf5..d02604561 100644 --- a/util/test/rdtest/__init__.py +++ b/util/test/rdtest/__init__.py @@ -10,5 +10,6 @@ from .shared.Overlay_Test import * from .shared.Buffer_Truncation import * from .shared.Discard_Zoo import * from .shared.Subgroup_Zoo import * +from .shared.Pixel_History import * from .shared.Workgroup_Zoo import * from .shared.Groupshared import * diff --git a/util/test/rdtest/shared/Pixel_History.py b/util/test/rdtest/shared/Pixel_History.py new file mode 100644 index 000000000..5021d836a --- /dev/null +++ b/util/test/rdtest/shared/Pixel_History.py @@ -0,0 +1,1006 @@ +import renderdoc as rd +import rdtest +import pprint +import math +from typing import List + + +def float_value(x): return x.floatValue +def uint_value(x): return x.uintValue +def unknown_stencil(x): return x == -2 + +# Not a real test, re-used by API-specific tests + + +class Pixel_History(rdtest.TestCase): + internal = True + demos_test_name = None + + def check_capture(self): + # cache some information since our python bindings deep copy actions and this can add up + self.eventCache = {} + for a in self.controller.GetRootActions(): + self.populate_eventcache(a, '', []) + + self.errored = False + + self.batch_base = self.find_action("Batch:", 0) + while self.batch_base is not None: + with rdtest.log.auto_section(self.batch_base.customName): + self.is_secondary = 'Secondary' in self.batch_base.customName + self.batch_test(self.batch_base.eventId) + self.batch_base = self.find_action( + "Batch:", self.batch_base.eventId+1) + + if self.errored: + raise rdtest.TestFailureException("Detected problems in pixel history") + + def error(self, message): + rdtest.log.error(message) + self.errored = True + + def relative_xy(self, x, y): + self.x, self.y = (x >> self.sub.mip, y >> self.sub.mip) + return (self.x, self.y) + + def populate_eventcache(self, action: rd.ActionDescription, name: str, parents: List[int]): + hierarchy = parents + [action.eventId] + + self.eventCache[action.eventId] = { + 'name': name, + 'flags': action.flags, + 'hierarchy': hierarchy, + } + + for child in action.children: + if child.flags & rd.ActionFlags.SetMarker: + name = child.customName + + self.populate_eventcache(child, name, hierarchy) + + def get_eventname(self, eventId: int) -> str: + return self.eventCache[eventId]['name'] + + def get_actionflags(self, eventId: int) -> rd.ActionFlags: + return self.eventCache[eventId]['flags'] + + def get_hierarchy(self, eventId: int) -> List[int]: + return self.eventCache[eventId]['hierarchy'] + + def batch_test(self, base_eid: int): + test_marker = self.find_action("Simple Test", base_eid) + self.controller.SetFrameEvent(test_marker.next.eventId, True) + + pipe = self.controller.GetPipelineState() + + if len(pipe.GetOutputTargets()) > 0: + col = pipe.GetOutputTargets()[0] + else: + col = None + depth = pipe.GetDepthTarget() + + self.has_colour = (col is not None and col.resource != rd.ResourceId()) + self.has_depth = (depth.resource != rd.ResourceId()) + + if self.has_colour: + rt = col + tex = col.resource + self.is_depth = False + else: + self.check(self.has_depth) + self.is_depth = True + rt = depth + + if self.has_depth: + depth_details = self.get_texture(depth.resource) + self.has_stencil = (depth_details.format.type in [ + rd.ResourceFormatType.D16S8, rd.ResourceFormatType.D24S8, rd.ResourceFormatType.D32S8]) + else: + self.has_stencil = False + + self.tex = tex = rt.resource + tex_details = self.get_texture(tex) + + self.epsilon = 1.0/float(1 << tex_details.format.compByteWidth) + + sub = rd.Subresource() + if tex_details.arraysize > 1 or tex_details.depth > 1: + sub.slice = rt.firstSlice + if tex_details.mips > 1: + sub.mip = rt.firstMip + + self.sub = sub + self.comp = comp = rt.format.compType + + def event_name(x: rd.PixelModification): + return self.get_eventname(x.eventId) + + value_func = float_value + alpha_value = 2.75 # 1 + ALPHA_ADD=1.75 + overflow_value = 100000.0 + clear_col = 0.2 + + if self.comp == rd.CompType.UInt: + alpha_value = 4 # 1 + ALPHA_ADD=3 + clear_col = 5 # uint values are shifted by 16, we clear to 80,80,80,160 + value_func = uint_value + overflow_value = (1 << 28)-1 + + def roundToInf(col): + # for half-floats lower inf to the largest representable value since either is valid + if tex_details.format.compType == rd.CompType.Float and tex_details.format.compByteWidth == 2: + return tuple([65504.0 if x == math.inf else x for x in col]) + + return col + + self.fetch_property = { + '_': None, # dynamically skipped test via with_col or with_depth below + 'event_name': event_name, + 'value': value_func, + 'passed': lambda x: x.Passed(), + 'culled': lambda x: x.backfaceCulled, + 'depth_test_failed': lambda x: x.depthTestFailed, + 'depth_clipped': lambda x: x.depthClipped, + 'sample_masked': lambda x: x.sampleMasked, + 'depth_bounds_failed': lambda x: x.depthBoundsFailed, + 'scissor_clipped': lambda x: x.scissorClipped, + 'stencil_test_failed': lambda x: x.stencilTestFailed, + 'shader_discarded': lambda x: x.shaderDiscarded, + 'shader_out_col': lambda x: value_func(x.shaderOut.col), + 'shader_out_depth': lambda x: x.shaderOut.depth, + 'pre_mod_col': lambda x: value_func(x.preMod.col), + 'post_mod_col': lambda x: roundToInf(value_func(x.postMod.col)), + 'pre_mod_depth': lambda x: x.preMod.depth, + 'post_mod_depth': lambda x: x.postMod.depth, + 'post_mod_stencil': lambda x: x.postMod.stencil, + 'primitive_id': lambda x: x.primitiveID, + 'unknown_post_mod_stencil': lambda x: unknown_stencil(x.postMod.stencil), + 'unboundPS': lambda x: x.unboundPS, + 'directWrite': lambda x: x.directShaderWrite, + } + + def with_depth(x): + return x if self.has_depth else '_' + + def with_stencil(x): + return x if self.has_stencil else '_' + + def fmt_adjusted(r, g, b, a): + if tex_details.format.compType == rd.CompType.UInt: + r = int(r * 16) + g = int(g * 16) + b = int(b * 16) + a = int(a * 16) + + return (r, g, b, a) + + def fmt_clamped(r, g, b, a): + r, g, b, a = fmt_adjusted(r, g, b, a) + + if tex_details.format.compType == rd.CompType.UNorm: + r = max(0, min(1, r)) + g = max(0, min(1, g)) + b = max(0, min(1, b)) + a = max(0, min(1, a)) + elif tex_details.format.compType == rd.CompType.Float and tex_details.format.compByteWidth == 2: + r = max(-65504, min(65504, r)) + g = max(-65504, min(65504, g)) + b = max(-65504, min(65504, b)) + a = max(-65504, min(65504, a)) + elif tex_details.format.compType == rd.CompType.UInt: + maxval = (1 << (8*tex_details.format.compByteWidth))-1 + r = max(0, min(maxval, r)) + b = max(0, min(maxval, b)) + a = max(0, min(maxval, a)) + g = max(0, min(maxval, g)) + + return r, g, b, a + + # can't reliably get colours, depths, or primitive IDs on secondaries + if self.is_secondary: + self.fetch_property['pre_mod_col'] = None + self.fetch_property['post_mod_col'] = None + self.fetch_property['shader_out_col'] = None + self.fetch_property['pre_mod_depth'] = None + self.fetch_property['shader_out_depth'] = None + self.fetch_property['post_mod_depth'] = None + self.fetch_property['post_mod_stencil'] = None + self.fetch_property['unknown_post_mod_stencil'] = None + self.fetch_property['primitive_id'] = None + + if not self.has_colour: + self.fetch_property['pre_mod_col'] = None + self.fetch_property['post_mod_col'] = None + self.fetch_property['shader_out_col'] = None + + if not self.has_depth: + self.fetch_property['depth_test_failed'] = None + self.fetch_property['depth_clipped'] = None + self.fetch_property['depth_bounds_failed'] = None + self.fetch_property['stencil_test_failed'] = None + self.fetch_property['pre_mod_depth'] = None + self.fetch_property['shader_out_depth'] = None + self.fetch_property['post_mod_depth'] = None + self.fetch_property['post_mod_stencil'] = None + self.fetch_property['unknown_post_mod_stencil'] = None + + # don't check stencil if there's no stencil channel + if not self.has_stencil: + self.fetch_property['post_mod_stencil'] = None + self.fetch_property['unknown_post_mod_stencil'] = None + + # check if depth bounds is supportted - D3D11 does not + has_depth_bounds = self.find_action('Depth Bounds Prep') is not None + + x, y = self.relative_xy(110, 100) + rdtest.log.print("Testing Unbound PS {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Unbound Fragment Shader', + 'passed': True, + 'unboundPS': True, + 'primitive_id': 0, + 'post_mod_stencil': 0x33 + }, + { + 'event_name': 'Stencil Write', + 'passed': True, + 'primitive_id': 0, + 'post_mod_stencil': 0x55 + }, + ] + self.check_events(events, modifs) + + x, y = self.relative_xy(170, 140) + rdtest.log.print("Testing Stencil failure {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Unbound Fragment Shader', + 'passed': True, + 'unboundPS': True, + 'primitive_id': 0 + }, + { + 'event_name': 'Stencil Write', + 'passed': True + }, + { + 'event_name': 'Background', + 'depth_test_failed': True, + with_depth('post_mod_col'): fmt_clamped(1, 0, 0, alpha_value) + }, + { + 'event_name': 'Simple Test', + with_stencil('stencil_test_failed'): True + }, + ] + self.check_events(events, modifs) + + x, y = self.relative_xy(170, 160) + rdtest.log.print("Testing Depth failure {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Depth Write', + 'passed': True + }, + { + 'event_name': 'Background', + 'depth_test_failed': True + }, + { + 'event_name': 'Cull Front', + 'culled': True + }, + { + 'event_name': 'Simple Test', + 'depth_test_failed': True + }, + ] + self.check_events(events, modifs) + + x, y = self.relative_xy(150, 250) + rdtest.log.print("Testing Discard {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Background', + 'shader_discarded': True + }, + ] + self.check_events(events, modifs) + + x, y = self.relative_xy(330, 145) + rdtest.log.print("Testing Primitive ID {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Simple Test', + 'passed': True, + 'primitive_id': 3, + 'shader_out_col': fmt_adjusted(0, 0, 0, alpha_value) + }, + ] + self.check_events(events, modifs) + + x, y = self.relative_xy(340, 145) + rdtest.log.print("Testing Depth Clip {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Simple Test', + 'passed': False, + 'depth_clipped': True + }, + ] + self.check_events(events, modifs) + + # if depth bounds test isn't supported these draws won't be emitted + if has_depth_bounds: + x, y = self.relative_xy(330, 102) + rdtest.log.print("Testing Depth Bounds Pass {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Depth Bounds Prep', + 'passed': True, + 'primitive_id': 0, + 'shader_out_col': fmt_adjusted(1, 0, 0, alpha_value) + }, + { + 'event_name': 'Depth Bounds Clip', + 'passed': True, + 'primitive_id': 0, + 'shader_out_col': fmt_adjusted(0, 1, 0, alpha_value) + }, + ] + self.check_events(events, modifs) + + # slightly precise X to ensure it still picks the right edge of the triangle on mip version + x, y = self.relative_xy(318, 102) + rdtest.log.print("Testing Depth Bounds Fail {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Depth Bounds Prep', + 'passed': True, + 'primitive_id': 0, + 'shader_out_col': fmt_adjusted(1, 0, 0, alpha_value) + }, + { + 'event_name': 'Depth Bounds Clip', + with_depth('passed'): False, + 'depth_bounds_failed': True + }, + ] + self.check_events(events, modifs) + + x, y = self.relative_xy(346, 102) + rdtest.log.print("Testing Depth Bounds Fail {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Depth Bounds Prep', + 'passed': True, + 'primitive_id': 0, + 'shader_out_col': fmt_adjusted(1, 0, 0, alpha_value) + }, + { + 'event_name': 'Depth Bounds Clip', + with_depth('passed'): False, + 'depth_bounds_failed': True + }, + ] + self.check_events(events, modifs) + + rdtest.log.print("Testing stencil/scissor checks") + test_marker = self.find_action("Stencil Mask", base_eid) + self.controller.SetFrameEvent(test_marker.next.eventId, True) + + x, y = self.relative_xy(106, 248) + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Background', + 'passed': True + }, + { + 'event_name': 'Scissor Fail', + 'scissor_clipped': True + }, + { + 'event_name': 'Scissor Pass', + 'passed': True, + 'shader_out_col': fmt_adjusted(0, 1, 0, alpha_value), + 'post_mod_col': fmt_clamped(0, 1, 0, alpha_value) + }, + { + 'event_name': 'Stencil Ref', + 'passed': True, + 'shader_out_col': fmt_adjusted(0, 0, 1, alpha_value), + 'post_mod_col': fmt_clamped(0, 0, 1, alpha_value) + }, + { + 'event_name': 'Stencil Mask', + 'passed': True, + 'shader_out_col': fmt_adjusted(0, 1, 1, alpha_value), + 'post_mod_col': fmt_clamped(0, 1, 1, alpha_value) + }, + ] + self.check_events(events, modifs) + + rdtest.log.print("Testing depth test for per fragment reporting") + test_marker = self.find_action("Depth Test", base_eid) + self.controller.SetFrameEvent(test_marker.next.eventId, True) + + x, y = self.relative_xy(275, 258) + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Background', + 'passed': True + }, + { + 'event_name': 'Depth Test', + 'primitive_id': 0, + 'depth_test_failed': True, + 'shader_out_col': fmt_adjusted(0, 1, 0, alpha_value), + 'shader_out_depth': 0.97, + with_depth('post_mod_col'): fmt_clamped(1, 0, 1, alpha_value), + 'post_mod_depth': 0.95, + 'unknown_post_mod_stencil': True + }, + { + 'event_name': 'Depth Test', + 'passed': True, + 'primitive_id': 1, + 'depth_test_failed': False, + 'shader_out_col': fmt_adjusted(1, 1, 0, alpha_value), + 'shader_out_depth': 0.20, + 'post_mod_col': fmt_clamped(1, 1, 0, alpha_value), + 'post_mod_depth': 0.20, + 'unknown_post_mod_stencil': True + }, + { + 'event_name': 'Depth Test', + 'primitive_id': 2, + 'depth_test_failed': True, + 'shader_out_col': fmt_adjusted(1, 0, 0, alpha_value), + 'shader_out_depth': 0.30, + with_depth('post_mod_col'): fmt_clamped(1, 1, 0, alpha_value), + 'post_mod_depth': 0.20, + 'unknown_post_mod_stencil': True + }, + { + 'event_name': 'Depth Test', + 'passed': True, + 'primitive_id': 3, + 'depth_test_failed': False, + 'shader_out_col': fmt_adjusted(0, 0, 1, alpha_value), + 'shader_out_depth': 0.10, + 'post_mod_col': fmt_clamped(0, 0, 1, alpha_value), + 'post_mod_depth': 0.10, + 'unknown_post_mod_stencil': True + }, + ] + + # if we don't have depth bounds testing the final event will pass + if has_depth_bounds: + events.append( + { + 'event_name': 'Depth Test', + 'primitive_id': 4, + 'depth_test_failed': False, + 'depth_bounds_failed': True, + 'shader_out_col': fmt_adjusted(1, 1, 1, alpha_value), + 'shader_out_depth': 0.05, + with_depth('post_mod_col'): fmt_clamped(0, 0, 1, alpha_value), + 'post_mod_depth': 0.10, + 'unknown_post_mod_stencil': False + }) + else: + events.append( + { + 'event_name': 'Depth Test', + 'passed': True, + 'primitive_id': 4, + 'depth_test_failed': False, + 'shader_out_col': fmt_adjusted(1, 1, 1, alpha_value), + 'shader_out_depth': 0.05, + with_depth('post_mod_col'): fmt_clamped(1, 1, 1, alpha_value), + 'post_mod_depth': 0.05, + 'unknown_post_mod_stencil': False + }) + + # can't distinguish per-fragment results in secondaries + if self.is_secondary: + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Background', + 'passed': True + }, + { + 'event_name': 'Depth Test', + 'post_mod_col': fmt_clamped(0, 0, 1, alpha_value), + }, + ] + self.check_events(events, modifs) + + # For pixel 60, 130 inside the light green triangle which is 300 draws of 1 instance of 1 triangle + rdtest.log.print("Testing Lots of Drawcalls") + test_marker = self.find_action("300 Instances", base_eid) + self.controller.SetFrameEvent(test_marker.next.eventId, True) + x, y = self.relative_xy(60, 130) + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + self.check_final_colour(tex, x, y, modifs, sub, comp) + countEvents = 1 + 300 + if len(modifs) != countEvents: + self.error("Expected {} events, got {}".format(countEvents, len(modifs))) + self.check_modifs_consistent(modifs) + + # For pixel 60, 50 inside the orange triangle which is 1 draws of 300 instances of 1 triangle + rdtest.log.print("Testing Lots of Instances") + x, y = self.relative_xy(60, 50) + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + self.check_final_colour(tex, x, y, modifs, sub, comp) + + countEvents = 1 + 255 + # secondaries can't count fragment events, so we only get 1 for the draw + if self.is_secondary: + countEvents = 1 + 1 + if len(modifs) != countEvents: + self.error("Expected {} events, got {}".format(countEvents, len(modifs))) + self.check_modifs_consistent(modifs) + + rdtest.log.print("Testing Sample colouring") + test_marker = self.find_action("Sample Colouring", base_eid) + self.controller.SetFrameEvent(test_marker.next.eventId, True) + x, y = self.relative_xy(330, 200) + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Sample Colouring', + 'passed': True, + 'primitive_id': 0, + 'shader_out_col': fmt_adjusted(1, 0, 0, alpha_value) + }, + ] + self.check_events(events, modifs) + self.check_modifs_consistent(modifs) + + if tex_details.msSamp == 4: + sub.sample = 1 + rdtest.log.print(f"Testing sample {sub.sample}") + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Sample Colouring', + 'passed': True, + 'primitive_id': 0, + 'shader_out_col': fmt_adjusted(0, 0, 1, alpha_value) + }, + ] + self.check_events(events, modifs) + self.check_modifs_consistent(modifs) + + sub.sample = 2 + rdtest.log.print(f"Testing sample {sub.sample}") + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Sample Colouring', + 'passed': True, + 'primitive_id': 0, + 'shader_out_col': fmt_adjusted(0, 1, 1, alpha_value) + }, + ] + self.check_events(events, modifs) + self.check_modifs_consistent(modifs) + + sub.sample = 3 + rdtest.log.print(f"Testing sample {sub.sample}") + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True, + }, + { + 'event_name': 'Sample Colouring', + 'passed': False, + 'sample_masked': True, + }, + ] + self.check_events(events, modifs) + self.check_modifs_consistent(modifs) + + sub.sample = 0 + + rdtest.log.print("Testing depth-equal testing") + test_marker = self.find_action("Depth Equal Pass", base_eid) + self.controller.SetFrameEvent(test_marker.next.eventId, True) + + x, y = self.relative_xy(200, 250) + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Depth Equal Setup', + 'passed': True, + 'shader_out_col': fmt_adjusted(0.1, 0.1, 0.1, alpha_value), + 'shader_out_depth': 0.1, + with_depth('post_mod_col'): fmt_clamped(0.1, 0.1, 0.1, alpha_value), + }, + { + 'event_name': 'Background', + with_depth('passed'): False, + 'depth_test_failed': True, + }, + { + 'event_name': 'Depth Equal Fail', + with_depth('passed'): False, + 'depth_test_failed': True, + 'shader_out_col': fmt_adjusted(0, 0, 0, alpha_value), + 'shader_out_depth': 0.1 + 5.0e-4, + with_depth('post_mod_col'): fmt_clamped(0.1, 0.1, 0.1, alpha_value), + }, + { + 'event_name': 'Depth Equal Pass', + 'passed': True, + 'shader_out_col': fmt_adjusted(1, 1, 1, alpha_value), + 'shader_out_depth': 0.1 + 1e-8, + 'post_mod_col': fmt_clamped(1, 1, 1, alpha_value), + }, + ] + self.check_events(events, modifs) + + rdtest.log.print("Testing colour masking") + test_marker = self.find_action("Colour Masked", base_eid) + self.controller.SetFrameEvent(test_marker.next.eventId, True) + + x, y = self.relative_xy(60, 80) + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Colour Masked', + 'passed': True, + 'shader_out_col': fmt_adjusted(3, 3, 3, 2+alpha_value), + 'post_mod_col': fmt_clamped(3, 3, clear_col, 1), + }, + ] + self.check_events(events, modifs) + + if self.has_colour: + rdtest.log.print("Testing direct writes") + test_marker = self.find_action("Compute write", base_eid) + + # don't have compute writes in secondaries and some tests like D3D MSAA won't have compute writes + if test_marker is not None and not self.is_secondary and base_eid in self.get_hierarchy(test_marker.eventId): + self.controller.SetFrameEvent(test_marker.next.eventId, True) + x, y = self.relative_xy(225, 85) + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Background', + 'passed': True + }, + { + 'event_name': 'Compute write', + 'passed': True, + 'directWrite': True, + 'shader_out_depth': -1, + 'post_mod_stencil': -1, + 'post_mod_depth': -1, + 'post_mod_col': fmt_clamped(3, 3, 3, 9), + }, + ] + self.check_events(events, modifs) + + rdtest.log.print("Testing overflowed writes") + test_marker = self.find_action("Overflowing", base_eid) + self.controller.SetFrameEvent(test_marker.next.eventId, True) + + x, y = self.relative_xy(105, 50) + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Overflowing', + 'passed': True, + 'shader_out_col': fmt_adjusted(overflow_value, 0, 0, alpha_value), + 'post_mod_col': fmt_clamped(overflow_value, 0, 0, alpha_value) + }, + { + 'event_name': 'Overflowing', + 'passed': True, + 'shader_out_col': fmt_adjusted(0, overflow_value, 0, alpha_value), + 'post_mod_col': fmt_clamped(0, overflow_value, 0, alpha_value) + }, + { + 'event_name': 'Overflowing', + 'passed': True, + 'shader_out_col': fmt_adjusted(0, 0, overflow_value, alpha_value), + 'post_mod_col': fmt_clamped(0, 0, overflow_value, alpha_value) + }, + ] + # can't distinguish per-fragment results in secondaries + if self.is_secondary: + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Overflowing', + 'passed': True, + 'shader_out_col': fmt_adjusted(0, 0, overflow_value, alpha_value), + 'post_mod_col': fmt_clamped(0, 0, overflow_value, alpha_value) + }, + ] + self.check_events(events, modifs) + + rdtest.log.print("Testing per-fragment discards") + test_marker = self.find_action("Per-Fragment discarding", base_eid) + self.controller.SetFrameEvent(test_marker.next.eventId, True) + + x, y = self.relative_xy(60, 160) + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs = self.controller.PixelHistory(tex, x, y, sub, comp) + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Per-Fragment discarding', + 'passed': False, + 'shader_discarded': True, + 'shader_out_col': (0, 0, 0, 0), + 'shader_out_depth': -1, + 'unknown_post_mod_stencil': True, + 'primitive_id': 0, + }, + { + 'event_name': 'Per-Fragment discarding', + 'passed': True, + 'primitive_id': 1, + 'shader_out_col': fmt_adjusted(1, 1, 1, alpha_value), + 'shader_out_depth': 0.33, + 'post_mod_col': fmt_clamped(1, 1, 1, alpha_value), + }, + ] + # can't distinguish per-fragment results in secondaries + if self.is_secondary: + events = [ + { + 'event_name': 'Begin RenderPass', + 'passed': True + }, + { + 'event_name': 'Per-Fragment discarding', + 'passed': True, + 'primitive_id': 1, + 'shader_out_col': fmt_adjusted(1, 1, 1, alpha_value), + 'shader_out_depth': 0.33, + 'post_mod_col': fmt_clamped(1, 1, 1, alpha_value), + }, + ] + self.check_events(events, modifs) + + def check_final_colour(self, tex, x, y, modifs: List[rd.PixelModification], sub, comp): + m = modifs[-1] + if self.has_colour: + expected = self.fetch_property['value'](m.postMod.col) + else: + expected = (m.postMod.depth, m.postMod.stencil/255.0, 0, 1) + self.check_pixel_value(tex, x, y, expected, sub=sub, cast=comp) + + def check_events(self, events, modifs: List[rd.PixelModification]): + # remove any modifs that didn't happen in the batch we're looking at - + # targets can be reused between batches + modifs = [ + m for m in modifs if self.batch_base.eventId in self.get_hierarchy(m.eventId)] + + if len(modifs) != len(events): + rdtest.log.print(str([e['event_name'] for e in events])) + rdtest.log.print(str([self.fetch_property['event_name'](m.eventId) for m in modifs])) + self.error(f"Expected {len(events)} events got {len(modifs)}") + return + + if not self.is_secondary: + self.check_modifs_consistent(modifs) + + self.check_final_colour(self.tex, self.x, self.y, + modifs, self.sub, self.comp) + + for i, m in enumerate(modifs): + m = modifs[i] + for prop_name, expected in events[i].items(): + prop_getter = self.fetch_property[prop_name] + + # property disabled, e.g. because colour or depth is not present, or we're in a secondary + if prop_getter is None: + # must have a reason - don't allow tests to skip in the main check + self.check( + not self.has_colour or not self.has_depth or not self.has_stencil or self.is_secondary) + continue + + actual = prop_getter(m) + + epsilon = self.epsilon + + if prop_name == 'shader_out_depth': + # reasonable F32 epsilon close to 1.0 + epsilon = 6.0e-8 + + if not rdtest.value_compare(actual, expected, eps=epsilon): + self.error( + f"eventId {m.eventId}, primitiveID {m.primitiveID}: " + + f"testing {prop_name} on modification {i} expected {expected}, got {actual}") + + def check_modifs_consistent(self, modifs: List[rd.PixelModification]): + # postmod of each should match premod of the next + for i in range(len(modifs) - 1): + m = modifs[i] + n = modifs[i + 1] + + if not m.postMod.IsValid() or not n.preMod.IsValid(): + continue + + a = self.fetch_property['value'](m.postMod.col) + b = self.fetch_property['value'](n.preMod.col) + + # A fragment event. If we have depth postMod.stencil should be unknown and depth should be consistent + if m.eventId == n.eventId and self.has_depth: + if self.has_stencil and not unknown_stencil(m.postMod.stencil): + self.error( + f"postmod stencil at EID {m.eventId} primitive {m.primitiveID}: {m.postMod.stencil} is not unknown") + + if not rdtest.value_compare(m.postMod.depth, n.preMod.depth): + self.error( + f"postmod depth at EID {m.eventId} primitive {m.primitiveID}: {m.postMod.depth} " + + f"doesn't match premod at next primitive {n.primitiveID}: {m.preMod.depth}") + + epsilon = self.epsilon + + if self.is_depth: + a = (m.postMod.depth, m.postMod.stencil) + b = (n.preMod.depth, n.preMod.stencil) + + epsilon = 1.0e-5 + + if not rdtest.value_compare(a, b, eps=epsilon): + self.error( + f"postmod at EID {m.eventId} primitive {m.primitiveID}: {a} " + f"doesn't match premod at {n.eventId} primitive {n.primitiveID}: {b}") + + # if we're missing either colour or depth, or it's a clear (which never is affected by the other even if it's bound), + # check that modifications list that properly + for m in modifs: + if not self.has_colour or (self.get_actionflags(m.eventId) & rd.ActionFlags.ClearDepthStencil): + if not rdtest.value_compare(m.preMod.col.uintValue, (0, 0, 0, 0)): + self.error(f"preMod is not zeroed for missing color at EID {m.eventId}") + if not rdtest.value_compare(m.postMod.col.uintValue, (0, 0, 0, 0)): + self.error(f"postMod is not zeroed for missing color at EID {m.eventId}") + if not rdtest.value_compare(m.shaderOut.col.uintValue, (0, 0, 0, 0)): + self.error(f"shaderOut is not zeroed for missing color at EID {m.eventId}") + if not self.has_depth or (self.get_actionflags(m.eventId) & rd.ActionFlags.ClearColor): + if not rdtest.value_compare(m.preMod.depth, -1.0): + self.error(f"preMod depth is not -1 for missing depth at EID {m.eventId}") + if not rdtest.value_compare(m.preMod.stencil, -1): + self.error(f"preMod stencil is not -1 for missing depth at EID {m.eventId}") + if not rdtest.value_compare(m.postMod.depth, -1.0): + self.error(f"postMod depth is not -1 for missing depth at EID {m.eventId}") + if not rdtest.value_compare(m.postMod.stencil, -1): + self.error(f"postMod stencil is not -1 for missing depth at EID {m.eventId}") + if not rdtest.value_compare(m.shaderOut.depth, -1.0): + self.error(f"shaderOut depth is not -1 for missing depth at EID {m.eventId}") + if not rdtest.value_compare(m.shaderOut.stencil, -1): + self.error(f"shaderOut stencil is not -1 for missing depth at EID {m.eventId}") + + # Check that if the test failed, its postmod is the same as premod + for i in range(len(modifs)): + if not m.Passed() and m.preMod.IsValid() and m.postMod.IsValid(): + a = self.fetch_property['value'](m.preMod.col) + b = self.fetch_property['value'](m.postMod.col) + + epsilon = self.epsilon + + if self.is_depth: + a = (m.preMod.depth, m.preMod.stencil) + b = (m.postMod.depth, m.postMod.stencil) + + epsilon = 1.0e-5 + + if a[1] == -2 or b[1] == -2: + a = (a[0], -2) + b = (b[0], -2) + + if not rdtest.value_compare(a, b, eps=epsilon): + self.error( + f"postmod at EID {m.eventId} primitive {m.primitiveID}: {b} doesn't match premod: {a}") diff --git a/util/test/tests/Vulkan/VK_Pixel_History.py b/util/test/tests/Vulkan/VK_Pixel_History.py index 374504522..c096d2819 100644 --- a/util/test/tests/Vulkan/VK_Pixel_History.py +++ b/util/test/tests/Vulkan/VK_Pixel_History.py @@ -1,457 +1,5 @@ -import renderdoc as rd import rdtest -from typing import List -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 depth_clipped(x): return x.depthClipped -def depth_bounds_failed(x): return x.depthBoundsFailed -def scissor_clipped(x): return x.scissorClipped -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 shader_out_depth(x): return x.shaderOut.depth -def pre_mod_col(x): return value_selector(x.preMod.col) -def post_mod_col(x): return value_selector(x.postMod.col) -def pre_mod_depth(x): return x.preMod.depth -def post_mod_depth(x): return x.postMod.depth -def post_mod_stencil(x): return x.postMod.stencil -def unknown_post_mod_stencil(x): return x.postMod.stencil == -1 or x.postMod.stencil == -2 -def unknown_stencil(x): return x == -1 or x == -2 -def primitive_id(x): return x.primitiveID -def unboundPS(x): return x.unboundPS - -class VK_Pixel_History(rdtest.TestCase): +class VK_Pixel_History(rdtest.Pixel_History): demos_test_name = 'VK_Pixel_History' - 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 - - self.is_depth = False - self.primary_test() - self.multisampled_image_test() - self.secondary_cmd_test() - - self.is_depth = True - self.depth_target_test() - self.multisampled_image_test() - - def primary_test(self): - test_marker: rd.ActionDescription = self.find_action("Test Begin") - self.controller.SetFrameEvent(test_marker.next.eventId, True) - - pipe: rd.PipeState = self.controller.GetPipelineState() - - rt = pipe.GetOutputTargets()[0] - - tex = rt.resource - 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_action("Begin RenderPass").next.eventId - depth_write_eid = self.find_action("Depth Write").next.eventId - stencil_write_eid = self.find_action("Stencil Write").next.eventId - unbound_fs_eid = self.find_action("Unbound Fragment Shader").next.eventId - background_eid = self.find_action("Background").next.eventId - cull_eid = self.find_action("Cull Front").next.eventId - test_eid = self.find_action("Test Begin").next.eventId - fixed_scissor_fail_eid = self.find_action("Fixed Scissor Fail").next.eventId - fixed_scissor_pass_eid = self.find_action("Fixed Scissor Pass").next.eventId - dynamic_stencil_ref_eid = self.find_action("Dynamic Stencil Ref").next.eventId - dynamic_stencil_mask_eid = self.find_action("Dynamic Stencil Mask").next.eventId - depth_test_eid = self.find_action("Depth Test").next.eventId - depth_bounds_prep_eid = self.find_action("Depth Bounds Prep").next.eventId - depth_bounds_clip_eid = self.find_action("Depth Bounds Clip").next.eventId - - # For pixel 110, 100, inside the red triangle with stencil value 0x55 - x, y = 110, 100 - rdtest.log.print("Testing pixel {}, {}".format(x, y)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - events = [ - [[event_id, begin_renderpass_eid], [passed, True]], - [[event_id, unbound_fs_eid], [passed, True], [unboundPS, True], [primitive_id, 0], [post_mod_stencil, 0x33]], - [[event_id, stencil_write_eid], [passed, True], [primitive_id, 0], [post_mod_stencil, 0x55]], - ] - self.check_events(events, modifs, False) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - # 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.format.compType) - events = [ - [[event_id, begin_renderpass_eid], [passed, True]], - [[event_id, unbound_fs_eid], [passed, True], [unboundPS, True], [primitive_id, 0]], - [[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, False) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - x, y = 190, 150 - rdtest.log.print("Testing pixel {}, {}".format(x, y)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - 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, False) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - x, y = 200, 50 - rdtest.log.print("Testing pixel {}, {}".format(x, y)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - 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, False) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - x, y = 150, 250 - rdtest.log.print("Testing pixel {}, {}".format(x, y)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - events = [ - [[event_id, begin_renderpass_eid], [passed, True]], - [[event_id, background_eid], [shader_discarded, True]], - ] - self.check_events(events, modifs, False) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - x, y = 330, 145 - rdtest.log.print("Testing pixel {}, {}".format(x, y)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - events = [ - [[event_id, begin_renderpass_eid], [passed, True]], - [[event_id, test_eid], [passed, True], [primitive_id, 3], [shader_out_col, (0.0, 0.0, 0.0, 2.75)]], - ] - self.check_events(events, modifs, False) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - x, y = 340, 145 - rdtest.log.print("Testing pixel {}, {}".format(x, y)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - events = [ - [[event_id, begin_renderpass_eid], [passed, True]], - [[event_id, test_eid], [passed, False], [depth_clipped, True]], - ] - self.check_events(events, modifs, False) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - x, y = 330, 105 - rdtest.log.print("Testing pixel {}, {}".format(x, y)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - events = [ - [[event_id, begin_renderpass_eid], [passed, True]], - [[event_id, depth_bounds_prep_eid], [passed, True], [primitive_id, 0], [shader_out_col, (1.0, 0.0, 0.0, 2.75)]], - [[event_id, depth_bounds_clip_eid], [passed, True], [primitive_id, 0], [shader_out_col, (0.0, 1.0, 0.0, 2.75)]], - ] - self.check_events(events, modifs, False) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - x, y = 320, 105 - rdtest.log.print("Testing pixel {}, {}".format(x, y)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - events = [ - [[event_id, begin_renderpass_eid], [passed, True]], - [[event_id, depth_bounds_prep_eid], [passed, True], [primitive_id, 0], [shader_out_col, (1.0, 0.0, 0.0, 2.75)]], - [[event_id, depth_bounds_clip_eid], [passed, False], [depth_bounds_failed, True]], - ] - self.check_events(events, modifs, False) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - x, y = 345, 105 - rdtest.log.print("Testing pixel {}, {}".format(x, y)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - events = [ - [[event_id, begin_renderpass_eid], [passed, True]], - [[event_id, depth_bounds_prep_eid], [passed, True], [primitive_id, 0], [shader_out_col, (1.0, 0.0, 0.0, 2.75)]], - [[event_id, depth_bounds_clip_eid], [passed, False], [depth_bounds_failed, True]], - ] - self.check_events(events, modifs, False) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - rdtest.log.print("Testing dynamic state pipelines") - self.controller.SetFrameEvent(dynamic_stencil_mask_eid, True) - - x, y = 100, 250 - rdtest.log.print("Testing pixel {}, {}".format(x, y)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - events = [ - [[event_id, begin_renderpass_eid], [passed, True]], - [[event_id, background_eid], [passed, True]], - [[event_id, fixed_scissor_fail_eid], [scissor_clipped, True]], - [[event_id, fixed_scissor_pass_eid], [passed, True], [shader_out_col, (0.0, 1.0, 0.0, 2.75)], - [post_mod_col, (0.0, 1.0, 0.0, 1.0)]], - [[event_id, dynamic_stencil_ref_eid], [passed, True], [shader_out_col, (0.0, 0.0, 1.0, 2.75)], - [post_mod_col, (0.0, 0.0, 1.0, 1.0)]], - [[event_id, dynamic_stencil_mask_eid], [passed, True], [shader_out_col, (0.0, 1.0, 1.0, 2.75)], - [post_mod_col, (0.0, 1.0, 1.0, 1.0)]], - ] - self.check_events(events, modifs, False) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - rdtest.log.print("Testing depth test for per fragment reporting") - self.controller.SetFrameEvent(depth_test_eid, True) - - x, y = 275, 260 - rdtest.log.print("Testing pixel {}, {}".format(x, y)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - events = [ - [[event_id, begin_renderpass_eid], [passed, True]], - [[event_id, background_eid], [passed, True]], - [[event_id, depth_test_eid], [primitive_id, 0], [depth_test_failed, True], - [shader_out_col, (1.0, 1.0, 1.0, 2.75)], [shader_out_depth, 0.97], [post_mod_col, (1.0, 0.0, 1.0, 1.0)], - [post_mod_depth, 0.95], [unknown_post_mod_stencil, True]], - [[event_id, depth_test_eid], [primitive_id, 1], [depth_test_failed, False], - [shader_out_col, (1.0, 1.0, 0.0, 2.75)], [shader_out_depth, 0.20], [post_mod_col, (1.0, 1.0, 0.0, 1.0)], - [post_mod_depth, 0.20]], - [[event_id, depth_test_eid], [primitive_id, 2], [depth_test_failed, True], - [shader_out_col, (1.0, 0.0, 0.0, 2.75)], [shader_out_depth, 0.30], [post_mod_col, (1.0, 1.0, 0.0, 1.0)], - [post_mod_depth, 0.20]], - [[event_id, depth_test_eid], [primitive_id, 3], [depth_test_failed, False], - [shader_out_col, (0.0, 0.0, 1.0, 2.75)], [shader_out_depth, 0.10], [post_mod_col, (0.0, 0.0, 1.0, 1.0)], - [post_mod_depth, 0.10]], - [[event_id, depth_test_eid], [primitive_id, 4], [depth_test_failed, False], [depth_bounds_failed, True], - [shader_out_col, (1.0, 1.0, 1.0, 2.75)], [shader_out_depth, 0.05], [post_mod_col, (0.0, 0.0, 1.0, 1.0)], - [post_mod_depth, 0.10]], - ] - self.check_events(events, modifs, False) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - def multisampled_image_test(self): - test_marker: rd.ActionDescription = self.find_action("Multisampled: test") - action_eid = test_marker.next.eventId - self.controller.SetFrameEvent(action_eid, True) - - pipe: rd.PipeState = self.controller.GetPipelineState() - rt = pipe.GetOutputTargets()[0] - - if self.is_depth: - rt = pipe.GetDepthTarget() - - sub = rd.Subresource() - tex = rt.resource - tex_details = self.get_texture(tex) - if tex_details.arraysize > 1: - sub.slice = rt.firstSlice - - beg_renderpass_eid = self.find_action("Multisampled: begin renderpass").next.eventId - - x, y = 140, 130 - sub.sample = 1 - rdtest.log.print("Testing pixel {}, {} at sample {}".format(x, y, sub.sample)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - - events = [ - [[event_id, beg_renderpass_eid], [passed, True], [post_mod_depth, 0.0]], - [[event_id, action_eid], [passed, True], [primitive_id, 0], [pre_mod_depth, 0.0], [shader_out_depth, 0.9], - [post_mod_depth, 0.9]], - [[event_id, action_eid], [passed, True], [primitive_id, 1], [shader_out_depth, 0.95], [post_mod_depth, 0.95]], - ] - - if not self.is_depth: - events[0] += [[post_mod_col, (0.0, 1.0, 0.0, 1.0)]] - events[1] += [[shader_out_col, (1.0, 0.0, 1.0, 2.75)], [post_mod_col, (1.0, 0.0, 1.0, 1.0)]] - events[2] += [[shader_out_col, (0.0, 0.0, 1.0, 2.75)], [post_mod_col, (0.0, 0.0, 1.0, 1.0)]] - - self.check_events(events, modifs, True) - - if self.is_depth: - self.check_pixel_value(tex, x, y, [modifs[-1].postMod.depth, float(modifs[-1].postMod.stencil)/255.0, 0.0, 1.0], sub=sub, cast=rt.format.compType) - else: - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - sub.sample = 2 - rdtest.log.print("Testing pixel {}, {} at sample {}".format(x, y, sub.sample)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - events = [ - [[event_id, beg_renderpass_eid], [passed, True], [post_mod_depth, 0.0]], - [[event_id, action_eid], [passed, True], [primitive_id, 0], [pre_mod_depth, 0.0], [shader_out_depth, 0.9], - [post_mod_depth, 0.9]], - [[event_id, action_eid], [passed, True], [primitive_id, 1], [shader_out_depth, 0.95], [post_mod_depth, 0.95]], - ] - - if not self.is_depth: - events[0] += [[post_mod_col, (0.0, 1.0, 0.0, 1.0)]] - events[1] += [[shader_out_col, (1.0, 0.0, 1.0, 2.75)], [post_mod_col, (1.0, 0.0, 1.0, 1.0)]] - events[2] += [[shader_out_col, (0.0, 1.0, 1.0, 2.75)], [post_mod_col, (0.0, 1.0, 1.0, 1.0)]] - - self.check_events(events, modifs, True) - - if self.is_depth: - self.check_pixel_value(tex, x, y, - [modifs[-1].postMod.depth, float(modifs[-1].postMod.stencil) / 255.0, 0.0, 1.0], - sub=sub, cast=rt.format.compType) - else: - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - def secondary_cmd_test(self): - secondary_marker: rd.ActionDescription = self.find_action("Secondary: red and blue") - self.controller.SetFrameEvent(secondary_marker.next.eventId, True) - - pipe: rd.PipeState = self.controller.GetPipelineState() - rt = pipe.GetOutputTargets()[0] - sub = rd.Subresource() - tex = rt.resource - tex_details = self.get_texture(tex) - if tex_details.arraysize > 1: - sub.slice = rt.firstSlice - if tex_details.mips > 1: - sub.mip = rt.firstMip - - sec_beg_renderpass_eid = self.find_action("Begin RenderPass Secondary").next.eventId - background_eid = self.find_action("Secondary: background").next.eventId - culled_eid = self.find_action("Secondary: culled").next.eventId - sec_red_and_blue = self.find_action("Secondary: red and blue").next.eventId - - # Test culling - x, y = 70, 40 - rdtest.log.print("Testing pixel {}, {}".format(x, y)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - events = [ - [[event_id, sec_beg_renderpass_eid], [passed, True], [post_mod_col, (0.0, 1.0, 0.0, 1.0)]], - [[event_id, background_eid], [passed, True], [pre_mod_col, (0.0, 1.0, 0.0, 1.0)]], - [[event_id, culled_eid], [passed, False], [culled, True]], - ] - self.check_events(events, modifs, True) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - # Blue triangle - x, y = 40, 40 - rdtest.log.print("Testing pixel {}, {}".format(x, y)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - events = [ - [[event_id, sec_beg_renderpass_eid], [passed, True], [post_mod_col, (0.0, 1.0, 0.0, 1.0)]], - # This is the first event in the command buffer, should have pre-mod - [[event_id, background_eid], [passed, True], [pre_mod_col, (0.0, 1.0, 0.0, 1.0)]], - # This is the last event in the command buffer, should have post-mod - [[event_id, sec_red_and_blue], [passed, True], [post_mod_col, (0.0, 0.0, 1.0, 1.0)]], - ] - self.check_events(events, modifs, True) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - # Didn't get post mod for background_eid - self.controller.SetFrameEvent(background_eid, True) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - events = [ - [[event_id, sec_beg_renderpass_eid]], - # The only event, should have both pre and post mod. - [[event_id, background_eid], [passed, True], [pre_mod_col, (0.0, 1.0, 0.0, 1.0)], [post_mod_col, (1.0, 0.0, 1.0, 1.0)]], - ] - self.check_events(events, modifs, True) - self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.format.compType) - - def depth_target_test(self): - test_marker: rd.ActionDescription = self.find_action("Test Begin") - self.controller.SetFrameEvent(test_marker.next.eventId, True) - - pipe: rd.PipeState = self.controller.GetPipelineState() - - rt = pipe.GetDepthTarget() - - tex = rt.resource - 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_action("Begin RenderPass").next.eventId - background_eid = self.find_action("Background").next.eventId - test_eid = self.find_action("Test Begin").next.eventId - - x, y = 200, 190 - rdtest.log.print("Testing pixel {}, {}".format(x, y)) - modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - events = [ - [[event_id, begin_renderpass_eid], [passed, True], [post_mod_depth, 1.0]], - [[event_id, background_eid], [passed, True], [primitive_id, 0], [pre_mod_depth, 1.0], [post_mod_depth, 0.95]], - [[event_id, test_eid], [passed, True], [depth_test_failed, False], [primitive_id, 0], [shader_out_depth, 0.5], [post_mod_depth, 0.5]], - [[event_id, test_eid], [passed, False], [depth_test_failed, True], [primitive_id, 1], [shader_out_depth, 0.6], [post_mod_depth, 0.5]], - ] - self.check_events(events, modifs, False) - - - def check_events(self, events, modifs, hasSecondary): - self.check(len(modifs) == len(events), "Expected {} events, got {}".format(len(events), len(modifs))) - # Check for consistency first. For secondary command buffers, - # might not have all information, so don't check for consistency - if not hasSecondary: - 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, eps=1.0/256.0): - raise rdtest.TestFailureException( - "eventId {}, primitiveID {}: testing {} expected {}, got {}".format(modifs[i].eventId, modifs[i].primitiveID, - 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): - a = value_selector(modifs[i].postMod.col) - b = value_selector(modifs[i + 1].preMod.col) - - # A fragment event : postMod.stencil should be unknown - if modifs[i].eventId == modifs[i+1].eventId: - if not unknown_stencil(modifs[i].postMod.stencil): - raise rdtest.TestFailureException( - "postmod stencil at {} primitive {}: {} is not unknown".format(modifs[i].eventId, - modifs[i].primitiveID, - modifs[i].postMod.stencil)) - - if self.is_depth: - a = (modifs[i].postMod.depth, modifs[i].postMod.stencil) - b = (modifs[i + 1].preMod.depth, modifs[i + 1].preMod.stencil) - - if a != b: - raise rdtest.TestFailureException( - "postmod at {} primitive {}: {} doesn't match premod at {} primitive {}: {}".format(modifs[i].eventId, - modifs[i].primitiveID, - a, - modifs[i + 1].eventId, - modifs[i + 1].primitiveID, - b)) - - # Check that if the test failed, its postmod is the same as premod - for i in range(len(modifs)): - if not modifs[i].Passed(): - a = value_selector(modifs[i].preMod.col) - b = value_selector(modifs[i].postMod.col) - - if self.is_depth: - a = (modifs[i].preMod.depth, modifs[i].preMod.stencil) - b = (modifs[i].postMod.depth, modifs[i].postMod.stencil) - - if a[1] == -2 or b[2] == -2: - a = (a[0], -2) - b = (b[0], -2) - - if not rdtest.value_compare(a, b, eps=1.0/256.0): - raise rdtest.TestFailureException( - "postmod at {} primitive {}: {} doesn't match premod: {}".format(modifs[i].eventId, - modifs[i].primitiveID, b, a)) + internal = False