diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawPrecheck.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawPrecheck.cpp index ebee83d0d4..a6787296d4 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawPrecheck.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawPrecheck.cpp @@ -47,7 +47,7 @@ std::optional Driver::precheckDraw(const QueueState& queue, const S const auto primitive = queue.userConfig.find(0x242); const std::uint32_t allOnes = drawParameters.indexSize == 2 ? 0xffffu : 0xffffffffu; const auto type = primitive == queue.userConfig.end() ? 0u : primitive->second & 0x3fu; - const bool strip = type == 3 || type == 5 || type == 6; + const bool strip = type == 3 || type == 5 || type == 6 || type == 21; const bool list = type == 1 || type == 2 || type == 4; const auto restartDevice = device.Load(); if (!strip && !(list && restartDevice != nullptr && restartDevice->PrimitiveListRestart())) { diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index 66c444520c..440a0f22bf 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -92,7 +92,7 @@ VkConservativeRasterizationModeEXT decodeConservativeRasterization(const QueueSt requireConservativeTriangles((domain == 1u || domain == 2u) && (topology == 2u || topology == 3u), topology == 0u ? "points" : topology == 1u || domain == 0u ? "lines" : "other primitive types", "VGT_TF_PARAM", parameters); } else { const auto primitive = read(queue.userConfig, 0x242, RegisterBank::UserConfig); - requireConservativeTriangles(primitive == 4u || primitive == 5u || primitive == 6u, primitive == 1u ? "points" : primitive == 2u || primitive == 3u ? "lines" : primitive == 7u || primitive == 17u ? "rectangles" : "other primitive types", "VGT_PRIMITIVE_TYPE", primitive); + requireConservativeTriangles(primitive == 4u || primitive == 5u || primitive == 6u || primitive == 21u, primitive == 1u ? "points" : primitive == 2u || primitive == 3u ? "lines" : primitive == 7u || primitive == 17u ? "rectangles" : "other primitive types", "VGT_PRIMITIVE_TYPE", primitive); } Require((read(cx, 0x1b3) & 0x44u) == 0, "conservative rasterization with centroid interpolation is unsupported"); return VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT; @@ -118,6 +118,7 @@ std::uint32_t inactiveVertexPointSize(const QueueState& queue) { case 6: case 7: case 17: + case 21: return 1u << 16u; default: return 0; @@ -401,7 +402,7 @@ std::string nonTriangleRejection(const QueueState& queue, const char* message, b const auto topology = (*parameters >> 5u) & 7u; if ((domain != 1u && domain != 2u) || (topology != 2u && topology != 3u)) return zeroMessage(0x2db, *parameters, message); } - } else if (const auto primitive = word(queue.userConfig, 0x242, RegisterBank::UserConfig); primitive && *primitive != 4u && *primitive != 5u && *primitive != 6u && !(rectangles && (*primitive == 7u || *primitive == 17u))) { + } else if (const auto primitive = word(queue.userConfig, 0x242, RegisterBank::UserConfig); primitive && *primitive != 4u && *primitive != 5u && *primitive != 6u && *primitive != 21u && !(rectangles && (*primitive == 7u || *primitive == 17u))) { return zeroMessage(0x242, *primitive, message); } return {}; @@ -1039,7 +1040,10 @@ State DecodeState(const QueueState& queue, bool vertexWorkgroup) { break; case 9: result.topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; break; case 4: result.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; break; - case 5: result.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; break; + case 5: + case 21: + result.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; + break; case 6: result.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; break; default: throw std::runtime_error("AGC graphics: unsupported primitive type " + std::to_string(primitive)); } @@ -1733,7 +1737,7 @@ std::string DrawRejection(const QueueState& queue, bool indexed) { if (indexed && value(queue.userConfig, 0x24b, word) && word != 0) { std::uint32_t primitive = 0; std::uint32_t resetIndex = 0; - if (value(queue.userConfig, 0x242, primitive) && (primitive & 0x3fu) != 1 && (primitive & 0x3fu) != 2 && (primitive & 0x3fu) != 3 && (primitive & 0x3fu) != 4 && (primitive & 0x3fu) != 5 && (primitive & 0x3fu) != 6) return "AGC graphics: primitive restart is only supported for point, line and triangle topologies"; + if (value(queue.userConfig, 0x242, primitive) && (primitive & 0x3fu) != 1 && (primitive & 0x3fu) != 2 && (primitive & 0x3fu) != 3 && (primitive & 0x3fu) != 4 && (primitive & 0x3fu) != 5 && (primitive & 0x3fu) != 6 && (primitive & 0x3fu) != 21) return "AGC graphics: primitive restart is only supported for point, line and triangle topologies"; if (value(cx, 0x103, resetIndex) && (resetIndex & 0xffffu) != 0xffffu) return "AGC graphics: primitive restart index other than all ones is unsupported"; } if (auto reason = nonzero(cx, 0x207, ~(LayerExports | ClipCullExports | inactiveVertexPointSize(queue)), "layer, viewport or auxiliary vertex exports"); !reason.empty()) return reason; diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index 1f3594c1c8..54ac32b25a 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -117,7 +117,7 @@ void inactiveVertexPointSizeTests() { queue.context[0x207] = 0x01210000; return queue; }; - for (const auto primitive : {2u, 4u, 5u, 6u, 7u, 17u}) { + for (const auto primitive : {2u, 4u, 5u, 6u, 7u, 17u, 21u}) { auto queue = pointSizeState(); queue.userConfig[0x242] = primitive; for (const auto raster : {0u, 0x240u, 0x248u}) { @@ -194,6 +194,23 @@ void inactiveVertexPointSizeTests() { rejected(queue); } +void PolygonPrimitiveTests() { + auto queue = makeState(); + queue.userConfig[0x242] = 21; + queue.context[0x1b3] = 2; + queue.context[0x1b4] = 2; + const auto state = AgcDriver::Graphics::DecodeState(queue); + Require(state.topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, "a polygon did not decode to a triangle fan"); + Require(!state.primitiveRestart, "polygon decoding enabled primitive restart by default"); + Require(AgcDriver::Graphics::RasterPrimitiveOf(state) == ShaderRecompiler::RasterPrimitive::Triangle, "a polygon was not classified as a triangle primitive"); + Require(AgcDriver::Graphics::DrawRejection(queue, false).empty(), "the precheck rejected a direct polygon draw"); + queue.userConfig[0x24b] = 1; + queue.context[0x103] = 0xffffffffu; + Require(AgcDriver::Graphics::DrawRejection(queue, true).empty(), "the precheck rejected an indexed polygon draw with restart"); + const auto indexedState = AgcDriver::Graphics::DecodeState(queue); + Require(indexedState.topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN && indexedState.primitiveRestart, "indexed polygon decoding did not keep primitive restart"); +} + void retainedPixelStageTests() { using namespace AgcDriver::Graphics; auto queue = makeState(); @@ -315,6 +332,9 @@ void stateTests() { queue.context[0x103] = 0xffffffffu; Require(AgcDriver::Graphics::DrawRejection(queue, true).empty(), "primitive restart was rejected for an indexed strip"); Require(AgcDriver::Graphics::DecodeState(queue).primitiveRestart, "primitive restart was not decoded for a strip"); + queue.userConfig[0x242] = 21; + Require(AgcDriver::Graphics::DrawRejection(queue, true).empty(), "primitive restart was rejected for an indexed polygon"); + Require(AgcDriver::Graphics::DecodeState(queue).primitiveRestart, "primitive restart was not decoded for a polygon"); queue.context[0x103] = 5; Require(AgcDriver::Graphics::DrawRejection(queue, true).find("all ones") != std::string::npos, "a restart index other than all ones was accepted"); queue = makeState(); @@ -726,10 +746,10 @@ void MsaaEnableSingleSampleTests() { queue = enabled(); queue.userConfig[0x242] = 1; Require(DrawRejection(queue, false).find("primitives other than triangles") != std::string::npos, "the precheck accepted MSAA_ENABLE on a single-sample target for points"); - for (const auto primitive : {5u, 6u}) { + for (const auto primitive : {5u, 6u, 21u}) { queue = enabled(); queue.userConfig[0x242] = primitive; - Require(DecodeState(queue).samples == 1 && DrawRejection(queue, false).empty(), "MSAA_ENABLE on a single-sample target was refused for a triangle fan or strip"); + Require(DecodeState(queue).samples == 1 && DrawRejection(queue, false).empty(), "MSAA_ENABLE on a single-sample target was refused for a triangle fan, strip or polygon"); } queue = enabled(); queue.context[0x292] = 7; @@ -7095,7 +7115,7 @@ void ConservativeRasterizationTests() { queue.context[0x1b4] = 2; Require(AgcDriver::Graphics::DecodeState(queue).conservativeRasterization == VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT, "PA_SC_CONSERVATIVE_RASTERIZATION_CNTL 0x6000 enabled conservative rasterization"); queue.context[0x313] = 0x6001; - for (const auto primitive : {4u, 5u, 6u}) { + for (const auto primitive : {4u, 5u, 6u, 21u}) { queue.userConfig[0x242] = primitive; Require(AgcDriver::Graphics::DecodeState(queue).conservativeRasterization == VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT, "OVER_RAST_ENABLE did not decode to overestimation for primitive type " + std::to_string(primitive)); Require(AgcDriver::Graphics::DrawRejection(queue, false).empty(), "the precheck rejected OVER_RAST_ENABLE"); @@ -8745,6 +8765,7 @@ int main(int argc, char** argv) { ShaderStageTests(); NggVertexWorkgroupTests(); inactiveVertexPointSizeTests(); + PolygonPrimitiveTests(); TuningFieldTests(); PixelInputLayoutTests(); UnexportedPixelInputTests(); diff --git a/docs/dev/debt/libSceAgcDriver.md b/docs/dev/debt/libSceAgcDriver.md index 49c7ebc7f3..bc49a0d84a 100644 --- a/docs/dev/debt/libSceAgcDriver.md +++ b/docs/dev/debt/libSceAgcDriver.md @@ -46,6 +46,7 @@ ### Functional - [Texture cache budgets](../../../core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp) (libSceAgcDriver) - the storage image cache's budget is what the reported heap budget leaves after the heap's use other than the cached storage images, the unused part of the sampled textures' 2 GiB floor and an eighth of the budget, capped at a quarter of the budget and at `TextureCacheBudget` (a quarter of the heap's size, at least 2 GiB), never below 512 MiB; the sampled cache's budget reserves `TextureCacheBudget` for the storage cache. So the sampled budget falls first while the sampled textures alive stay within their floor, and both fall together above it; each cache trims only at its next insert, a storage image is allocated before its insert reads the budget, and both budgets read the heap at most once a second, so a burst between two reads can still exceed the reported budget. The 2 GiB floor and the eighth are the sampled formula's values reused and the 512 MiB floor and the caps the earlier ones; none was measured beyond one title (PPSA11386) on one 10 GB GPU, and a title whose storage working set exceeds the shrunk budget re-creates storage images at each use. +- [Polygon primitive type](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - `VGT_PRIMITIVE_TYPE` 21 (`PrimitiveType::Polygon`) decodes to `VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN` and is treated as inactive for vertex point size, like triangle fans and strips. PPSA01317 (Gran Turismo 7) submits polygon draws; whether the console performs polygon-mode decomposition or custom vertex routing is unverified. - [Native multisampled DCC fast clears](../../../core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp) import uniform 0000/0001/1110/1111 keys into a single-layer, level-zero SW_64KB_R_X color image with 2, 4 or 8 fragments. Color and DCC/CMASK ranges must stay write-watched and unchanged; expanded or mixed initial DCC, register CMASK clears, compressed payloads, color transforms, lossy modes, metadata changes, guest publication and writes over a live owner throw. Driver-mediated reads of the color and metadata stay guarded. The metadata extent follows [AMD Addrlib](https://github.com/GPUOpen-Drivers/pal/blob/c5e800072a32f68b6ccc4422936d96167c6e0728/src/core/imported/addrlib/src/gfx10/gfx10addrlib.cpp#L347-L454) with the [PS5 Mesa model](https://github.com/mihawk-99/PS5_Mesa/blob/0b2d6d1a61d9bbf89cf8beb88a696144f67c61f8/src/amd/vulkan/winsys/ps5/radv_ps5_winsys.c): GFX10.1, 16 pipes, 256-byte interleave, two maximum compressed fragments. Console geometry, concurrent guest mutation and transitions outside this subset are unverified. - [Initial stencil state](../../../core/libs/prx/libSceAgcDriver/Execution/include/QueueState.hpp) uses zero for `DB_STENCIL_CONTROL`, `DB_STENCILREFMASK` and `DB_STENCILREFMASK_BF` (context `0x10b`-`0x10d`), including after `CLEAR_STATE`. The public V0, V4, V5 and V7-V11 [register-default tables](../../../core/libs/prx/libSceAgc/Command/include/RegisterDefaultsData.hpp) supply these values. Linux's [GFX10 clear-state table](https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/amd/amdgpu/clearstate_gfx10.h) agrees on the control register but uses `0x01000000` for the reference masks; the console reset values have not been measured. Planet Coaster (PPSA01736 v01.017) enables stencil tests without writing the control register.