From aaffe035ac36b4300b63f4ea62bbb4da0998aee5 Mon Sep 17 00:00:00 2001 From: Yunus Shaikh <75296019+EagleBearerYS@users.noreply.github.com> Date: Sun, 18 Oct 2026 02:36:18 -0700 Subject: [PATCH] test(agc): check that a checkerboard depth pass through the exact sample order path builds no recipe --- .../tests/execution/SampleOrder.cpp | 116 +++++++++++------- 1 file changed, 75 insertions(+), 41 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/SampleOrder.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/SampleOrder.cpp index 37905199da..b25d15ad22 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/SampleOrder.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/SampleOrder.cpp @@ -715,6 +715,7 @@ struct Pass { std::optional order; bool storesInvocations = false; std::function beforeReplay; + bool buildsNoRecipe = false; }; void Draw(AgcDriver::VulkanDevice& device, const Context& context, const Pass& pass) { @@ -799,12 +800,16 @@ void Draw(AgcDriver::VulkanDevice& device, const Context& context, const Pass& p } shaders.push_back({ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}); const AgcDriver::Pm4::DrawParameters draw{0, static_cast(pass.vertices->size()), 0, 1, 0, false}; - if (!pass.beforeReplay) { + if (!pass.beforeReplay && !pass.buildsNoRecipe) { AgcDriver::Graphics::Draw(context, state, draw, shaders); return; } std::shared_ptr recipe; AgcDriver::Graphics::Draw(context, state, draw, shaders, {}, &recipe); + if (pass.buildsNoRecipe) { + Require(recipe == nullptr, "a draw that reorders its samples built a recipe"); + return; + } Require(recipe != nullptr, "the recorded draw built no recipe"); pass.beforeReplay(); const auto replayed = AgcDriver::Graphics::DrawWithRecipe(context, state, draw, shaders, {}, *recipe); @@ -1252,6 +1257,57 @@ int GridReference(AgcDriver::VulkanDevice& device) { return 0; } +void CheckRecipeDepth(AgcDriver::VulkanDevice& device, const Context& context, const std::array& locations, std::size_t run, const std::string& what, bool reordered) { + constexpr std::uint32_t samples = 2u; + AgcDriver::Graphics::DepthTarget depth{reinterpret_cast(DepthMemory.data()) + 256u * 1024u * run, 0u, ReferenceExtent, VK_FORMAT_D32_SFLOAT, 1.0f, 0}; + depth.samples = samples; + const Vertices clear = Screen(ClearDepth); + Pass clearPass; + clearPass.vertices = &clear; + clearPass.code = NullExportCode; + clearPass.samples = samples; + clearPass.extent = ReferenceExtent; + clearPass.depth = depth; + clearPass.depthWrite = true; + clearPass.locations = locations; + auto slopes = clearPass; + slopes.vertices = &Slopes; + auto& vertexBlock = NewBlock(Slopes.size() * sizeof(Slopes[0])); + std::memcpy(reinterpret_cast(static_cast(vertexBlock.Address())), Slopes.data(), Slopes.size() * sizeof(Slopes[0])); + slopes.vertexMemory = reinterpret_cast(static_cast(vertexBlock.Address())); + if (reordered) { + slopes.buildsNoRecipe = true; + Draw(device, context, clearPass); + Draw(device, context, slopes); + Settle(device); + std::printf("%s: builds no recipe, since a draw that reorders its samples is built in full each time, with its sample swaps\n", what.c_str()); + return; + } + const auto read = [&] { return ReadTarget(context, AgcDriver::Graphics::DepthSurfaceOrderTarget(context, depth).view, false, samples); }; + Samples recorded; + slopes.beforeReplay = [&] { + Settle(device); + recorded = read(); + Draw(device, context, clearPass); + Settle(device); + const auto cleared = read(); + Require(std::all_of(cleared.begin(), cleared.end(), [](const std::array& value) { return std::bit_cast(value[0]) == ClearDepth; }), what + ": the clear between the recorded draw and its replay left depth other than the clear value"); + }; + Draw(device, context, clearPass); + Draw(device, context, slopes); + Settle(device); + const auto replayed = read(); + const auto first = PrintDepthLocations(what + ", the recorded draw", LocateDepth(recorded, locations)); + PrintDepthLocations(what + ", the recipe's replay as the first draw of a pass", LocateDepth(replayed, locations)); + Require(first.sloped != 0u, what + ": the recorded draw left no sloped depth"); + const bool readAtGuestLocations = first.guest == first.sloped; + if (!readAtGuestLocations) std::printf("%s: the device reads depth drawn at custom sample locations back at other locations, even after a transition that names them: the replay is compared with the recorded draw as the device reads both back\n", what.c_str()); + const auto differences = Differences(recorded, replayed, samples, what + ", depth replayed from the recipe", true); + std::printf("%s, %ux%u: the recipe's replay differs from the recorded draw in %zu of %zu samples\n", what.c_str(), ReferenceExtent.width, ReferenceExtent.height, differences, recorded.size()); + Require(differences == 0u, what + (readAtGuestLocations ? ": the recipe's replay did not leave the depth the recorded draw left at the guest's sample locations" : ": the recipe's replay did not leave the depth the recorded draw left, as the device reads both back")); + std::printf("%s: a depth-only 2x draw replayed from its recipe as the first draw of a pass leaves the depth the recorded draw left%s, bit for bit\n", what.c_str(), readAtGuestLocations ? "" : " as the device reads both back"); +} + int RecipeDepthLocations(AgcDriver::VulkanDevice& device) { const auto context = AgcDriver::VulkanDeviceTestAccess::ContextOf(device); ClearTargets clearTargets{device, context.device}; @@ -1268,46 +1324,24 @@ int RecipeDepthLocations(AgcDriver::VulkanDevice& device) { std::printf("skipped, draw recipes are off (APS5_NO_DRAW_RECIPE)\n"); return VulkanTestSkipped; } - const auto locations = UniformLocations(VillageLocations, 0u); - AgcDriver::Graphics::DepthTarget depth{reinterpret_cast(DepthMemory.data()), 0u, ReferenceExtent, VK_FORMAT_D32_SFLOAT, 1.0f, 0}; - depth.samples = samples; - const Vertices clear = Screen(ClearDepth); - Pass clearPass; - clearPass.vertices = &clear; - clearPass.code = NullExportCode; - clearPass.samples = samples; - clearPass.extent = ReferenceExtent; - clearPass.depth = depth; - clearPass.depthWrite = true; - clearPass.locations = locations; - auto slopes = clearPass; - slopes.vertices = &Slopes; - auto& vertexBlock = NewBlock(Slopes.size() * sizeof(Slopes[0])); - std::memcpy(reinterpret_cast(static_cast(vertexBlock.Address())), Slopes.data(), Slopes.size() * sizeof(Slopes[0])); - slopes.vertexMemory = reinterpret_cast(static_cast(vertexBlock.Address())); - const auto read = [&] { return ReadTarget(context, AgcDriver::Graphics::DepthSurfaceOrderTarget(context, depth).view, false, samples); }; - Samples recorded; - slopes.beforeReplay = [&] { - Settle(device); - recorded = read(); - Draw(device, context, clearPass); - Settle(device); - const auto cleared = read(); - Require(std::all_of(cleared.begin(), cleared.end(), [](const std::array& value) { return std::bit_cast(value[0]) == ClearDepth; }), "the clear between the recorded draw and its replay left depth other than the clear value"); - }; - Draw(device, context, clearPass); - Draw(device, context, slopes); - Settle(device); - const auto replayed = read(); - const auto first = PrintDepthLocations("the recorded draw", LocateDepth(recorded, locations)); - PrintDepthLocations("the recipe's replay as the first draw of a pass", LocateDepth(replayed, locations)); - Require(first.sloped != 0u, "the recorded draw left no sloped depth"); - const bool readAtGuestLocations = first.guest == first.sloped; - if (!readAtGuestLocations) std::printf("the device reads depth drawn at custom sample locations back at other locations, even after a transition that names them: the replay is compared with the recorded draw as the device reads both back\n"); - const auto differences = Differences(recorded, replayed, samples, "depth replayed from the recipe", true); - std::printf("2x depth at custom sample locations, %ux%u: the recipe's replay differs from the recorded draw in %zu of %zu samples\n", ReferenceExtent.width, ReferenceExtent.height, differences, recorded.size()); - Require(differences == 0u, readAtGuestLocations ? "the recipe's replay did not leave the depth the recorded draw left at the guest's sample locations" : "the recipe's replay did not leave the depth the recorded draw left, as the device reads both back"); - std::puts(readAtGuestLocations ? "a depth-only 2x draw replayed from its recipe as the first draw of a pass leaves the depth the recorded draw left, bit for bit" : "a depth-only 2x draw replayed from its recipe as the first draw of a pass leaves the depth the recorded draw left as the device reads both back, bit for bit"); + CheckRecipeDepth(device, context, UniformLocations(VillageLocations, 0u), 0u, "pixel (0,0)'s locations in every pixel", false); + auto emulated = context; + emulated.sampleLocationGridSampleCounts = 0u; + AgcDriver::Graphics::State probe{}; + probe.samples = samples; + probe.customSampleLocations = true; + probe.sampleLocationGrid = true; + probe.sampleLocations = VillageLocations; + probe.depth = AgcDriver::Graphics::DepthTarget{0x100000u, 0u, ReferenceExtent, VK_FORMAT_D32_SFLOAT, 1.0f, 0}; + probe.depth->samples = samples; + const auto refusal = AgcDriver::Graphics::SampleOrderRefusal(emulated, probe); + if (!refusal.empty()) { + std::printf("the checkerboard through the exact path is not checked: the device does not put its samples in the guest's order: %s\n", refusal.c_str()); + return 0; + } + Require(AgcDriver::Graphics::ReordersSamples(AgcDriver::Graphics::HostSampleLocationsFor(emulated, probe).order), "the context without a 2x2 grid did not take the exact path"); + CheckRecipeDepth(device, emulated, UniformLocations(VillageLocations, 0u), 1u, "pixel (0,0)'s locations in every pixel, without a 2x2 grid", false); + CheckRecipeDepth(device, emulated, VillageLocations, 2u, "the 2x checkerboard through the exact path", true); return 0; }