Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2282,6 +2282,7 @@ if(BUILD_TESTING)
set_tests_properties(agc_driver_alpha_to_coverage_state PROPERTIES
ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" TIMEOUT 30 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
add_test(NAME agc_driver_dithered_alpha_to_coverage COMMAND agc_driver_alpha_to_coverage_tests --dithered8)
add_test(NAME agc_driver_two_sample_dithered_alpha_to_coverage COMMAND agc_driver_alpha_to_coverage_tests --dithered2)
set(multisampledDccClearTests)
foreach(samples IN ITEMS 2 4 8)
set(testName "agc_driver_msaa_dcc_clear_${samples}")
Expand All @@ -2291,12 +2292,12 @@ if(BUILD_TESTING)
set_tests_properties(agc_driver_msaa_dcc_state ${multisampledDccClearTests} PROPERTIES
ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" TIMEOUT 30 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
set_tests_properties(${multisampledDccClearTests} PROPERTIES SKIP_RETURN_CODE 77)
set_tests_properties(agc_driver_dithered_alpha_to_coverage PROPERTIES
set_tests_properties(agc_driver_dithered_alpha_to_coverage agc_driver_two_sample_dithered_alpha_to_coverage PROPERTIES
ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 TIMEOUT 30 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(agc_driver_alpha_to_coverage_state agc_driver_undithered_alpha_to_coverage_state agc_driver_depth_bounds_state agc_driver_msaa_dcc_state agc_driver_unused_viewport_depth PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
set_tests_properties(agc_driver_dithered_alpha_to_coverage ${multisampledDccClearTests} PROPERTIES ENVIRONMENT_MODIFICATION
set_tests_properties(agc_driver_dithered_alpha_to_coverage agc_driver_two_sample_dithered_alpha_to_coverage ${multisampledDccClearTests} PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ std::shared_ptr<DrawDecode> Driver::decodeDraw(const QueueState& queue, const Su
if (product->pixel.ancillary) product->pixel.rasterPrimitive = Graphics::RasterPrimitiveOf(product->state);
if (product->pixel.ancillary) product->pixel.layerExported = product->state.layerExports != 0;
product->pixel.ditheredAlphaToCoverage = product->state.ditheredAlphaToCoverage;
product->pixel.ditheredAlphaSamples = product->state.ditheredAlphaSamples;
product->pixel.iteratesSamples = product->state.perSampleShading;
product->pixel.sampleLocations = Graphics::PixelSampleLocations(product->state, product->pixel);
return product;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ void Driver::lookupDraw(const Submission& submission, const std::shared_ptr<Vulk
mix(pixel.alphaToCoverage);
mix(static_cast<std::uint64_t>(pixel.rasterPrimitive));
mix(pixel.ditheredAlphaToCoverage);
mix(pixel.ditheredAlphaSamples);
mix(pixel.quadPixelMask);
for (const auto mask : pixel.quadSampleMask) mix(mask);
mix(pixel.iteratesSamples);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ bool Driver::sameDecode(const DrawDecode& a, const DrawDecode& b) {
const auto& y = b.programs[i];
if (x.binary.stage != y.binary.stage || x.binary.codeAddress != y.binary.codeAddress || x.userDataBase != y.userDataBase || x.firstUserSgpr != y.firstUserSgpr || x.userData != y.userData || x.snapshot != y.snapshot || x.codeOffset != y.codeOffset || x.tailContinuation != y.tailContinuation) return false;
}
if (p.targetExportPacking != q.targetExportPacking || p.dualSourceBlend != q.dualSourceBlend || p.reversedBlend != q.reversedBlend || p.targetMerge != q.targetMerge || p.alphaToCoverage != q.alphaToCoverage || p.rasterPrimitive != q.rasterPrimitive || p.ditheredAlphaToCoverage != q.ditheredAlphaToCoverage || p.quadPixelMask != q.quadPixelMask || p.quadSampleMask != q.quadSampleMask || p.iteratesSamples != q.iteratesSamples || p.positionLocation != q.positionLocation || p.sampleLocations != q.sampleLocations || p.sampleOrder != q.sampleOrder) return false;
if (p.targetExportPacking != q.targetExportPacking || p.dualSourceBlend != q.dualSourceBlend || p.reversedBlend != q.reversedBlend || p.targetMerge != q.targetMerge || p.alphaToCoverage != q.alphaToCoverage || p.rasterPrimitive != q.rasterPrimitive || p.ditheredAlphaToCoverage != q.ditheredAlphaToCoverage || p.ditheredAlphaSamples != q.ditheredAlphaSamples || p.quadPixelMask != q.quadPixelMask || p.quadSampleMask != q.quadSampleMask || p.iteratesSamples != q.iteratesSamples || p.positionLocation != q.positionLocation || p.sampleLocations != q.sampleLocations || p.sampleOrder != q.sampleOrder) return false;
if (s.samples != t.samples || s.mergedCoverage != t.mergedCoverage || s.truncatedCoverage != t.truncatedCoverage || s.quadSampleMask != t.quadSampleMask || s.pixelEarlyZ != t.pixelEarlyZ || s.pixelKill != t.pixelKill) return false;
return true;
}
Expand Down
1 change: 1 addition & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ struct State {
} depthRemap;
bool alphaToCoverage = false;
bool ditheredAlphaToCoverage = false;
std::uint8_t ditheredAlphaSamples = 8;
bool perSampleShading = false;
bool mergedCoverage = false;
bool truncatedCoverage = false;
Expand Down
46 changes: 37 additions & 9 deletions core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,11 @@ constexpr std::uint32_t PixelStageRunsMask = 0x000207c7u;
constexpr std::uint32_t AlphaToCoverageMask = ~0x0001ff00u;
constexpr std::uint32_t AlphaToCoverageUndithered = 0x0000aa01u;
constexpr std::uint32_t AlphaToCoverageDithered = 0x00018701u;
constexpr std::uint32_t AlphaToCoverageTwoSampleDithered = 0x0000e701u;

bool ditheredAlphaToCoverageWord(std::uint32_t word) {
return word == AlphaToCoverageDithered || word == AlphaToCoverageTwoSampleDithered;
}

bool alphaToCoverageDisabled(const Registers& cx) {
const auto control = find(cx, 0x203);
Expand All @@ -241,6 +246,22 @@ bool ditheredAlphaSamplesSupported(const Registers& cx) {
return config != cx.end() && (config->second & 0x700007u) == 0x300003u && eqaa != cx.end() && (eqaa->second & 0x207777u) == 0x3303u;
}

bool twoSampleDitheredAlphaSamplesSupported(const Registers& cx) {
const auto config = find(cx, 0x2f8);
const auto eqaa = find(cx, 0x201);
return config != cx.end() && (config->second & 0x700007u) == 0x100001u && eqaa != cx.end() && (eqaa->second & 0x207077u) == 0x1001u && ((eqaa->second >> 8u) & 7u) <= 1u;
}

bool twoSampleDitheredAlphaLocationsSupported(const Registers& cx) {
const auto first = find(cx, 0x2fe);
if (first == cx.end() || (first->second != 0xcc44u && first->second != 0xc44cu)) return false;
for (std::uint32_t pixel = 1; pixel < 4; ++pixel) {
const auto locations = find(cx, 0x2fe + pixel * 4u);
if (locations == cx.end() || locations->second != first->second) return false;
}
return true;
}

bool ditheredAlphaLocationsSupported(const Registers& cx) {
for (std::uint32_t pixel = 0; pixel < 4; ++pixel) {
const auto first = find(cx, 0x2fe + pixel * 4u);
Expand Down Expand Up @@ -585,7 +606,7 @@ std::string singleSampleMsaaRejection(const QueueState& queue) {
const auto offset = 0x2feu + pixel * 4u;
if (const auto locations = word(cx, offset); locations && (*locations & 0xffu) != 0) return zeroMessage(offset, *locations, "MSAA_ENABLE on a single-sample target whose sample is off the pixel center");
}
if (const auto alphaToMask = word(cx, 0x2dc); alphaToMask && (*alphaToMask & 1u) != 0 && !(*alphaToMask == AlphaToCoverageDithered && alphaToCoverageDisabled(cx))) return zeroMessage(0x2dc, *alphaToMask, "MSAA_ENABLE on a single-sample target with alpha-to-coverage");
if (const auto alphaToMask = word(cx, 0x2dc); alphaToMask && (*alphaToMask & 1u) != 0 && !(ditheredAlphaToCoverageWord(*alphaToMask) && alphaToCoverageDisabled(cx))) return zeroMessage(0x2dc, *alphaToMask, "MSAA_ENABLE on a single-sample target with alpha-to-coverage");
if (const auto conservative = word(cx, 0x313); conservative && (*conservative & ~0x001f6000u) != 0) return zeroMessage(0x313, *conservative, "MSAA_ENABLE on a single-sample target with conservative rasterization");
return nonTriangleRejection(queue, "MSAA_ENABLE on a single-sample target for primitives other than triangles and rectangles", true);
}
Expand Down Expand Up @@ -1424,14 +1445,19 @@ State DecodeState(const QueueState& queue, bool vertexWorkgroup) {
}
zero(cx, 0x203, shaderControlMask(read(cx, 0x1c4)), "depth export, shader coverage or ordered fragment execution");
const auto alpha = read(cx, 0x2dc);
const bool alphaDisabled = alpha == AlphaToCoverageDithered && alphaToCoverageDisabled(cx);
result.alphaToCoverage = alpha == AlphaToCoverageUndithered || (!alphaDisabled && alpha == AlphaToCoverageDithered);
result.ditheredAlphaToCoverage = result.alphaToCoverage && alpha == AlphaToCoverageDithered;
const bool alphaDisabled = ditheredAlphaToCoverageWord(alpha) && alphaToCoverageDisabled(cx);
result.alphaToCoverage = alpha == AlphaToCoverageUndithered || (!alphaDisabled && ditheredAlphaToCoverageWord(alpha));
result.ditheredAlphaToCoverage = result.alphaToCoverage && ditheredAlphaToCoverageWord(alpha);
if (!result.alphaToCoverage) zero(cx, 0x2dc, alphaDisabled ? AlphaToCoverageMask & ~1u : AlphaToCoverageMask, "dithered or rounded alpha-to-coverage");
if (result.ditheredAlphaToCoverage) {
if (result.ditheredAlphaToCoverage && alpha == AlphaToCoverageDithered) {
Require(ditheredAlphaSamplesSupported(cx), "dithered alpha-to-coverage requires eight matched raster/DB_EQAA samples and one pixel iteration");
Require(!result.customSampleLocations, "dithered alpha-to-coverage with custom sample locations is unsupported");
}
if (result.ditheredAlphaToCoverage && alpha == AlphaToCoverageTwoSampleDithered) {
Require(twoSampleDitheredAlphaSamplesSupported(cx), "DB_ALPHA_TO_MASK 0xe701 requires two matched raster/DB_EQAA samples and one pixel iteration");
Require(twoSampleDitheredAlphaLocationsSupported(cx), "DB_ALPHA_TO_MASK 0xe701 is supported only with the two-sample locations (4,4),(-4,-4) or (-4,4),(4,-4) in every pixel");
result.ditheredAlphaSamples = 2;
}
const bool depthKeepsSampleZero = result.depth && result.depth->samples == 1 && result.samples > 1;
zero(cx, 0x292, ScanModeMask & ~1u, "scan conversion mode");
if (depthKeepsSampleZero && (read(cx, 0x292) & 1u) == 0) {
Expand Down Expand Up @@ -2263,13 +2289,15 @@ std::string DrawRejection(const QueueState& queue, bool indexed) {
static_cast<void>(value(cx, 0x1c4, zFormat));
if (auto reason = nonzero(cx, 0x203, shaderControlMask(zFormat), "depth export, shader coverage or ordered fragment execution"); !reason.empty()) return reason;
if (value(cx, 0x2dc, word)) {
const bool alphaDisabled = word == AlphaToCoverageDithered && alphaToCoverageDisabled(cx);
if ((word != AlphaToCoverageUndithered && word != AlphaToCoverageDithered) || alphaDisabled) {
const bool alphaDisabled = ditheredAlphaToCoverageWord(word) && alphaToCoverageDisabled(cx);
if ((word != AlphaToCoverageUndithered && !ditheredAlphaToCoverageWord(word)) || alphaDisabled) {
if (auto reason = nonzero(cx, 0x2dc, alphaDisabled ? AlphaToCoverageMask & ~1u : AlphaToCoverageMask, "dithered or rounded alpha-to-coverage"); !reason.empty()) return reason;
}
if (!alphaDisabled && word == AlphaToCoverageDithered && !ditheredAlphaSamplesSupported(cx)) return "AGC graphics: dithered alpha-to-coverage requires eight matched raster/DB_EQAA samples and one pixel iteration";
if (!alphaDisabled && word == AlphaToCoverageDithered) {
if (!ditheredAlphaLocationsSupported(cx)) return "AGC graphics: dithered alpha-to-coverage with custom sample locations is unsupported";
if (!alphaDisabled && word == AlphaToCoverageTwoSampleDithered && !twoSampleDitheredAlphaSamplesSupported(cx)) return "AGC graphics: DB_ALPHA_TO_MASK 0xe701 requires two matched raster/DB_EQAA samples and one pixel iteration";
if (!alphaDisabled && ditheredAlphaToCoverageWord(word)) {
if (word == AlphaToCoverageDithered && !ditheredAlphaLocationsSupported(cx)) return "AGC graphics: dithered alpha-to-coverage with custom sample locations is unsupported";
if (word == AlphaToCoverageTwoSampleDithered && !twoSampleDitheredAlphaLocationsSupported(cx)) return "AGC graphics: DB_ALPHA_TO_MASK 0xe701 is supported only with the two-sample locations (4,4),(-4,-4) or (-4,4),(4,-4) in every pixel";
std::uint32_t format = 0;
if (value(cx, 0x1c5, format)) {
format &= 0xfu;
Expand Down
65 changes: 64 additions & 1 deletion core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4810,8 +4810,71 @@ void UnditheredAlphaToCoverageTests() {
}
}

void TwoSampleDitheredAlphaToCoverageTests() {
auto dithered = makeState();
dithered.context[0x203] = 0x8010;
dithered.context[0x201] = 0x00101001;
dithered.context[0x2dc] = 0xe701;
dithered.context[0x2f8] = 0x00108001;
dithered.context[0x292] = 3;
dithered.context[0x31d] = 0x9000;
dithered.context[0x1c5] = 4;
dithered.context[0x1b3] = 2;
dithered.context[0x1b4] = 2;
const auto locate = [&](std::uint32_t locations) {
for (std::uint32_t pixel = 0; pixel < 4; ++pixel) dithered.context[0x2fe + pixel * 4u] = locations;
};
for (const auto locations : {0xcc44u, 0xc44cu}) {
locate(locations);
for (const auto eqaa : {0x00101001u, 0x00101101u}) {
dithered.context[0x201] = eqaa;
const auto rejection = AgcDriver::Graphics::DrawRejection(dithered, false);
Require(rejection.empty(), "two-sample DB_ALPHA_TO_MASK 0xe701 refused: " + rejection);
const auto state = AgcDriver::Graphics::DecodeState(dithered);
Require(state.alphaToCoverage && state.ditheredAlphaToCoverage && state.ditheredAlphaSamples == 2 && state.samples == 2, "two-sample DB_ALPHA_TO_MASK 0xe701 did not select the two-sample dither");
}
}
dithered.context[0x201] = 0x00101001;
for (const auto eqaa : {0x00103001u, 0x00101011u, 0x00101003u, 0x00101201u, 0x00301001u}) {
dithered.context[0x201] = eqaa;
expectFailure([&] { AgcDriver::Graphics::DecodeState(dithered); }, "DB_EQAA");
Require(AgcDriver::Graphics::DrawRejection(dithered, false).find("DB_EQAA") != std::string::npos, "the precheck accepted unmatched two-sample dither samples");
}
dithered.context[0x201] = 0x00101001;
locate(0x44ccu);
expectFailure([&] { AgcDriver::Graphics::DecodeState(dithered); }, "two-sample locations");
Require(AgcDriver::Graphics::DrawRejection(dithered, false).find("two-sample locations") != std::string::npos, "the precheck accepted unmeasured two-sample dither locations");
locate(0xc44cu);
dithered.context[0x2fe + 12u] = 0xcc44u;
expectFailure([&] { AgcDriver::Graphics::DecodeState(dithered); }, "two-sample locations");
Require(AgcDriver::Graphics::DrawRejection(dithered, false).find("two-sample locations") != std::string::npos, "the precheck accepted two-sample dither locations that vary by pixel");
locate(0xc44cu);
for (const auto format : {3u, 5u, 6u}) {
dithered.context[0x1c5] = format;
expectFailure([&] { AgcDriver::Graphics::DecodeState(dithered); }, "FP16_ABGR or 32_ABGR");
Require(AgcDriver::Graphics::DrawRejection(dithered, false).find("FP16_ABGR or 32_ABGR") != std::string::npos, "the precheck accepted an unmeasured two-sample dither export format");
}
dithered.context[0x1c5] = 9;
Require(AgcDriver::Graphics::DecodeState(dithered).ditheredAlphaSamples == 2, "a 32_ABGR export refused the two-sample dither");
dithered.context[0x1c5] = 4;
dithered.context[0x203] |= 0x800u;
Require(!AgcDriver::Graphics::DecodeState(dithered).alphaToCoverage, "the shader disable override was ignored for DB_ALPHA_TO_MASK 0xe701");
Require(AgcDriver::Graphics::DrawRejection(dithered, false).empty(), "the precheck ignored the shader disable override for DB_ALPHA_TO_MASK 0xe701");
dithered.context[0x203] &= ~0x800u;
dithered.context[0x201] = 0x00133303;
dithered.context[0x2f8] = 0x0030e003;
dithered.context[0x31d] = 0x1b000;
for (std::uint32_t pixel = 0; pixel < 4; ++pixel) {
dithered.context[0x2fe + pixel * 4u] = 0xbd153fd1u;
dithered.context[0x2ff + pixel * 4u] = 0x9773f95bu;
}
expectFailure([&] { AgcDriver::Graphics::DecodeState(dithered); }, "DB_EQAA");
Require(AgcDriver::Graphics::DrawRejection(dithered, false).find("DB_EQAA") != std::string::npos, "the precheck accepted DB_ALPHA_TO_MASK 0xe701 with eight samples");
}

void AlphaToCoverageTests() {
UnditheredAlphaToCoverageTests();
TwoSampleDitheredAlphaToCoverageTests();
auto dithered = makeState();
dithered.context[0x203] = 0x8010;
dithered.context[0x201] = 0x00133303;
Expand All @@ -4829,7 +4892,7 @@ void AlphaToCoverageTests() {
Require(rejection.empty(), "eight-sample default dither refused: " + rejection);
const auto ditheredState = AgcDriver::Graphics::DecodeState(dithered);
Require(ditheredState.alphaToCoverage && ditheredState.samples == 8, "default dither did not enable eight-sample coverage");
Require(ditheredState.ditheredAlphaToCoverage, "default dither did not reach the shader state");
Require(ditheredState.ditheredAlphaToCoverage && ditheredState.ditheredAlphaSamples == 8, "default dither did not reach the shader state");
for (const auto eqaa : {0x00133333u, 0x00132303u, 0x00133203u, 0x00333303u}) {
dithered.context[0x201] = eqaa;
expectFailure([&] { AgcDriver::Graphics::DecodeState(dithered); }, "DB_EQAA");
Expand Down
6 changes: 3 additions & 3 deletions core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5501,7 +5501,7 @@ void verifyPixelRequestSerialization() {
minimal.context.pixel->layerExported = true;
minimal.context.pixel->ditheredAlphaToCoverage = true;
const auto encoded = serializer.Serialize(minimal);
require(requestPrefix(encoded, 8u) == "NVNQQS4AAAA=", "new requests did not use serialization version 46");
require(requestPrefix(encoded, 8u) == "NVNQQS8AAAA=", "new requests did not use serialization version 47");
const std::array<std::uint32_t, 2> fallbacks{0x40u, 0x1ac4u};
auto withFallbacks = minimal;
withFallbacks.context.linkedCallFallbacks = fallbacks;
Expand Down Expand Up @@ -5619,7 +5619,7 @@ void verifyPixelRequestSerialization() {
for (std::size_t bytes = 0; bytes < 45u; ++bytes) {
expectFailure([&] { static_cast<void>(serializer.Deserialize(requestPrefix(encoded, mappingOffset + bytes))); }, "truncated data", "a truncated pixel mapping, packing, dual-source, alpha-to-coverage, stencil reference export flag, primitive class, layer export, dither or front-face flag, quad pixel mask or quad sample mask was accepted");
}
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQS8AAAA="}) {
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQTAAAAA="}) {
expectFailure([&] { static_cast<void>(serializer.Deserialize(unsupported)); }, "serialization version", "an unsupported request version was accepted");
}
static constexpr std::string_view version18Request{
Expand Down Expand Up @@ -6005,7 +6005,7 @@ void verifyMergedHullConfiguration() {
require(!serializer.Deserialize(separate).request.graphics->tessellation->mergedHull, "a separate hull configuration was read as merged");
constexpr std::size_t pointTailBytes = 13u;
auto legacy = decodeBase64(merged);
require(legacy.size() > 54u + pointTailBytes && legacy[4] == 46u, "a merged hull request did not use serialization version 46");
require(legacy.size() > 54u + pointTailBytes && legacy[4] == 47u, "a merged hull request did not use serialization version 47");
legacy.resize(legacy.size() - sizeof(std::uint32_t) - 2u - pointTailBytes);
legacy.pop_back();
require(legacy[legacy.size() - 52u] == 1u, "the merged hull flag is not the last byte of the tessellation configuration");
Expand Down
Loading
Loading