Merge pull request #1349 from TibixDev/pr/rgba16-component-order
fix(agc): support alternate channel order in 16-bit color targets
This commit is contained in:
commit
ac2c5c08c8
|
|
@ -444,8 +444,7 @@ DecodedColorFormat DecodeColorFormat(std::uint32_t format, std::uint32_t number,
|
|||
if (number == uint) return {VK_FORMAT_R32G32_UINT, 8};
|
||||
return fail();
|
||||
case 12: {
|
||||
if (swap == 1) return fail();
|
||||
const auto reversed = static_cast<std::uint8_t>(swap == 2 ? 0x1bu : swap == 3 ? 0x93u : 0xe4u);
|
||||
const auto reversed = static_cast<std::uint8_t>(alternate ? 0xc6u : swap == 2 ? 0x1bu : swap == 3 ? 0x93u : 0xe4u);
|
||||
if (number == floating) return {VK_FORMAT_R16G16B16A16_SFLOAT, 8, reversed};
|
||||
if (number == unorm) return {VK_FORMAT_R16G16B16A16_UNORM, 8, reversed};
|
||||
if (number == snorm) return {VK_FORMAT_R16G16B16A16_SNORM, 8, reversed};
|
||||
|
|
@ -720,6 +719,8 @@ State DecodeState(const QueueState& queue, bool vertexWorkgroup) {
|
|||
if (collapsed) result.scissor.extent = {0, 0};
|
||||
APS5_LOG_OUT_DEBUG("Scissor offset=(%d,%d) extent=%ux%u", result.scissor.offset.x, result.scissor.offset.y, result.scissor.extent.width, result.scissor.extent.height);
|
||||
result.blends.assign(exportCount, VkPipelineColorBlendAttachmentState{});
|
||||
std::array<float, 3> requiredBlendConstants{};
|
||||
std::uint32_t requiredBlendMask = 0;
|
||||
for (const auto& color : result.colors) {
|
||||
const auto slot = color.slot;
|
||||
const auto blend = read(cx, 0x1e0 + slot);
|
||||
|
|
@ -755,6 +756,16 @@ State DecodeState(const QueueState& queue, bool vertexWorkgroup) {
|
|||
state.dstAlphaBlendFactor = alphaFactorWithOpaqueDestination(state.dstAlphaBlendFactor);
|
||||
}
|
||||
for (std::uint32_t i = 0; i < 4; ++i) result.blendConstants[i] = readFloat(cx, 0x105 + i);
|
||||
const auto constantColor = [](VkBlendFactor factor) { return factor == VK_BLEND_FACTOR_CONSTANT_COLOR || factor == VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; };
|
||||
if ((mapping == 0xe4u || mapping == 0xc6u) && state.colorBlendOp != VK_BLEND_OP_MIN && state.colorBlendOp != VK_BLEND_OP_MAX && (constantColor(state.srcColorBlendFactor) || constantColor(state.dstColorBlendFactor))) {
|
||||
for (std::uint32_t i = 0; i < 3; ++i) {
|
||||
if ((state.colorWriteMask & (1u << i)) == 0) continue;
|
||||
const auto value = result.blendConstants[(mapping >> (2u * i)) & 3u];
|
||||
Require((requiredBlendMask & (1u << i)) == 0 || requiredBlendConstants[i] == value, "color targets require conflicting blend constants after component swap");
|
||||
requiredBlendConstants[i] = value;
|
||||
requiredBlendMask |= 1u << i;
|
||||
}
|
||||
}
|
||||
if (mapping == 0xecu) {
|
||||
const auto destinationAlpha = [](VkBlendFactor factor) { return factor == VK_BLEND_FACTOR_DST_ALPHA || factor == VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA || factor == VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; };
|
||||
if (destinationAlpha(state.srcColorBlendFactor) || destinationAlpha(state.dstColorBlendFactor) || destinationAlpha(state.srcAlphaBlendFactor) || destinationAlpha(state.dstAlphaBlendFactor)) throw std::runtime_error("AGC graphics: blending into an 8_8 color target with the alternate component order with a factor of the destination alpha, which the target keeps in its second channel, is not implemented");
|
||||
|
|
@ -773,6 +784,9 @@ State DecodeState(const QueueState& queue, bool vertexWorkgroup) {
|
|||
}
|
||||
result.blends[color.exportIndex] = state;
|
||||
}
|
||||
for (std::uint32_t i = 0; i < 3; ++i) {
|
||||
if ((requiredBlendMask & (1u << i)) != 0) result.blendConstants[i] = requiredBlendConstants[i];
|
||||
}
|
||||
if (!result.colors.empty()) result.blend = result.blends[result.colors.front().exportIndex];
|
||||
if (result.dualSourceBlend) {
|
||||
Require(exportSlots.size() == 2 && exportSlots[0] == 0 && exportSlots[1] == 1 && exportCount == 1, "dual-source blending needs pixel exports to MRT slots 0 and 1 and color writes to slot 0 alone");
|
||||
|
|
|
|||
|
|
@ -90,6 +90,48 @@ void expectFailure(TAction action, std::string_view reason) {
|
|||
throw std::runtime_error("expected graphics rejection: " + std::string(reason));
|
||||
}
|
||||
|
||||
void hdrComponentSwapTests() {
|
||||
using namespace AgcDriver::Graphics;
|
||||
for (const auto number : {0u, 1u, 4u, 7u}) {
|
||||
auto queue = makeState();
|
||||
queue.context[0x31c] = (queue.context[0x31c] & ~0x1f7cu) | (12u << 2u) | (number << 8u) | (1u << 11u);
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(sliceMemory.data());
|
||||
queue.context[0x318] = static_cast<std::uint32_t>(address >> 8u);
|
||||
queue.context[0x390] = static_cast<std::uint32_t>(address >> 40u);
|
||||
queue.context[0x1b3] = queue.context[0x1b4] = 2;
|
||||
auto state = DecodeState(queue);
|
||||
const auto expected = number == 0 ? VK_FORMAT_R16G16B16A16_UNORM : number == 1 ? VK_FORMAT_R16G16B16A16_SNORM : number == 4 ? VK_FORMAT_R16G16B16A16_UINT : VK_FORMAT_R16G16B16A16_SFLOAT;
|
||||
Require(state.color.format == expected && state.color.elementBytes == 8, "alternate 16-bit color format was not decoded");
|
||||
Require(state.color.componentMapping == 0xc6u && ExportMappings(state)[0] == 0xc6u, "alternate 16-bit color export did not swap red and blue");
|
||||
Require(DecodePixelStageInfo(queue.context, ExportMappings(state)).targetExportMapping[0] == 0xc6u, "pixel export lost the alternate color mapping");
|
||||
queue.context[0x8e] = 1;
|
||||
state = DecodeState(queue);
|
||||
Require(state.blend.colorWriteMask == VK_COLOR_COMPONENT_B_BIT, "alternate red-only write did not select the third storage component");
|
||||
queue.context[0x8e] = 0xf;
|
||||
queue.context[0x1e0] = (1u << 30u) | 13u;
|
||||
for (std::uint32_t i = 0; i < 4; ++i) queue.context[0x105 + i] = std::bit_cast<std::uint32_t>(float(i + 1) / 8.0f);
|
||||
state = DecodeState(queue);
|
||||
Require(state.blendConstants == std::array<float, 4>{0.375f, 0.25f, 0.125f, 0.5f}, "alternate color blend constants were not swizzled with the export");
|
||||
queue.context[0x31c] &= ~(1u << 11u);
|
||||
state = DecodeState(queue);
|
||||
Require(state.color.componentMapping == 0xe4u && state.blendConstants == std::array<float, 4>{0.125f, 0.25f, 0.375f, 0.5f}, "standard color mapping changed");
|
||||
queue.context[0x31c] |= 2u << 11u;
|
||||
expectFailure([&] { DecodeState(queue); }, "reversed component order");
|
||||
}
|
||||
auto queue = makeState();
|
||||
queue.context[0x31c] = (queue.context[0x31c] & ~0x1f7cu) | (12u << 2u) | (7u << 8u) | (1u << 11u);
|
||||
for (const auto offset : {0x318u, 0x31bu, 0x31cu, 0x31du}) queue.context[offset + 0xfu] = queue.context.at(offset);
|
||||
for (const auto offset : {0x390u, 0x3b0u, 0x3b8u}) queue.context[offset + 1u] = queue.context.at(offset);
|
||||
queue.context[0x31c + 0xfu] &= ~(1u << 11u);
|
||||
queue.context[0x1c5] = 0x99u;
|
||||
queue.context[0x8e] = queue.context[0x8f] = 0xffu;
|
||||
queue.context[0x1e0] = queue.context[0x1e1] = (1u << 30u) | 13u;
|
||||
for (std::uint32_t i = 0; i < 4; ++i) queue.context[0x105 + i] = std::bit_cast<std::uint32_t>(float(i + 1) / 8.0f);
|
||||
expectFailure([&] { DecodeState(queue); }, "conflicting blend constants");
|
||||
queue.context[0x107] = queue.context[0x105];
|
||||
Require(DecodeState(queue).colors.size() == 2, "compatible constants rejected mixed component orders");
|
||||
}
|
||||
|
||||
void stateTests() {
|
||||
AgcDriver::QueueState initial;
|
||||
Require(initial.context.at(0x200) == 0 && initial.context.at(0x83) == 0xffff, "initial context state is missing");
|
||||
|
|
@ -833,9 +875,6 @@ void ReversedComponentOrderTests() {
|
|||
const auto wide = AgcDriver::Graphics::DecodeState(queue);
|
||||
Require(wide.colors.size() == 1 && wide.colors[0].format == VK_FORMAT_R16G16B16A16_SFLOAT && wide.colors[0].componentMapping == mapping && wide.blends[0].colorWriteMask == 0xfu, "a 16_16_16_16 float target with a reversed component order did not map exports onto RGBA16");
|
||||
}
|
||||
auto queue = makeState();
|
||||
queue.context[0x31c] = (queue.context[0x31c] & ~((0x1fu << 2u) | (7u << 8u) | (3u << 11u))) | (12u << 2u) | (7u << 8u) | (1u << 11u);
|
||||
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "component swap 1");
|
||||
}
|
||||
|
||||
void CompactedExportTests() {
|
||||
|
|
@ -4118,6 +4157,7 @@ int main() {
|
|||
}
|
||||
RunGuestLeaseWaitTests();
|
||||
stateTests();
|
||||
hdrComponentSwapTests();
|
||||
depthMaintenanceTests();
|
||||
hardwareScreenOffsetTests();
|
||||
srgb8TargetTests();
|
||||
|
|
|
|||
|
|
@ -489,6 +489,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [8_SRGB color targets](../../core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp) (libSceAgcDriver) on a device that cannot attach and blend R8_SRGB render into an RGBA8_SRGB copy of the target, so the sRGB encode, decode and blend rounding are the device's; the console's are not measured.
|
||||
- [8_SRGB color targets](../../core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp) (libSceAgcDriver) on such a device throw unless they are single-mip 2D surfaces with a resident image; linear targets have none.
|
||||
- [COPY_DATA from the GPU clock](../../core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp) (libSceAgcDriver, `SRC_SEL` 9) writes the host steady clock in 10 ns ticks when the packet runs, the same counter as the `RELEASE_MEM` timestamp, not the console's GPU clock. The `COPY_DATA` cache policy fields are accepted and have no effect.
|
||||
- [16-bit RGBA color targets](../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) support the standard and the alternate (`COMP_SWAP` 1) component order. Alternate swaps red and blue in shader exports, write masks and constant-color blend factors, following `ac_translate_colorswap` in [Mesa 26.2.2 ac_formats.c](https://gitlab.freedesktop.org/mesa/mesa/-/blob/mesa-26.2.2/src/amd/common/ac_formats.c). The reverse orders (`COMP_SWAP` 2 and 3) keep throwing when blending, and simultaneous targets requiring conflicting blend constants throw. Console equivalence has not been measured on hardware.
|
||||
- [UINT16_ABGR color exports](../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) - export format 7 is accepted only into an unsigned integer target, and each 16-bit value reaches the target unchanged; a value beyond a narrower target's range (`R8_UINT`) is undefined in Vulkan, and whether the console clamps or truncates it is not confirmed. SINT16_ABGR (8) throws
|
||||
- `--to-intel` guest module trampolines are covered only by a synthetic relinker test; no game title has been verified with them on Linux or Windows.
|
||||
- `--to-rosetta` lowers what Rosetta lacks on top of `--to-intel` ([matcher](../../core/relinker/codegen/src/x86/Amd64OnlyInstructionMatcher.cpp)). Rosetta runs AVX2, FMA, BMI1/2, F16C and MOVBE but reports none of them through CPUID, so code that checks CPUID takes its baseline path. RDSEED becomes RDRAND and CLWB a NOP. RDPID returns processor 0, because Rosetta's TSC_AUX is the constant 0xAAAAAAAB; the same value reaches RDTSCP's ECX, which is not converted. ADCX/ADOX fault under Rosetta and are not converted: compilers do not emit them, and code that checks CPUID sees no ADX. The conversions are covered by the converter tests; that the converted code runs under Rosetta was checked on an M5 Max only, outside this repository's CI.
|
||||
|
|
|
|||
Loading…
Reference in New Issue