Files
AnyPS5/core/shader/recompiler/Translation/src/ShaderInputInfoBuilder.cpp
T
fdfawef324 1ffc5f672b feat(agc): emulate per-pixel multisample coverage masks
Decode the four PA_SC_AA_MASK fields at the rasterized sample count. Use the native Vulkan mask when all four pixels agree and apply varying masks in the fragment shader, preserving helpers and combining coverage with alpha-to-coverage and shader mask exports. Add the normalized masks to pipeline, draw and shader keys and the versioned replay request.

Refuse unsupported conservative-raster, centroid and early depth/stencil memory-write cases. Synthetic readbacks cover driver submission, depth/stencil suppression, fragment stores and one-sample and four-sample coverage.
2026-10-15 13:00:34 +13:00

234 lines
14 KiB
C++

#include "Translation/ShaderInputInfoBuilder.hpp"
#include "prx/libc/include/HostThreadLocal.hpp"
#include "IntermediateRepresentation/IrMetadata.hpp"
#include <array>
#include <cstdint>
#include <stdexcept>
namespace ShaderRecompiler {
namespace {
IrShaderStage _toIrShaderStage(ShaderStageKind stage) {
switch (stage) {
case ShaderStageKind::Vertex: return IrShaderStage::Vertex;
case ShaderStageKind::Local: return IrShaderStage::Local;
case ShaderStageKind::TessellationControl: return IrShaderStage::TessellationControl;
case ShaderStageKind::TessellationEvaluation: return IrShaderStage::TessellationEvaluation;
case ShaderStageKind::Mesh: return IrShaderStage::Mesh;
default: throw std::runtime_error("ShaderInputInfoBuilder: unexpected vertex-family stage");
}
}
void _detectVertexBuffers(ShaderVertexInputInfo& info) {
info.buffersNum = 0;
for (int ri = 0; ri < info.resourcesNum; ++ri) {
const auto& r = info.resources[ri];
const std::uint16_t stride = static_cast<std::uint16_t>((r.fields[1] >> 16u) & 0x3fffu);
const std::uint64_t base = (static_cast<std::uint64_t>(r.fields[0]) | (static_cast<std::uint64_t>(r.fields[1]) << 32u)) & 0xffffffffffffull;
const std::uint32_t numRecords = r.fields[2];
bool merged = false;
for (int bi = 0; bi < info.buffersNum; ++bi) {
auto& b = info.buffers[bi];
if (b.stride != stride || b.fetchIndex != static_cast<std::uint32_t>(info.resourcesDst[ri].fetchIndex)) continue;
const auto low = base < b.addr ? base : b.addr;
const auto offset1 = base - low;
const auto offset2 = b.addr - low;
if (offset1 >= stride || offset2 >= stride) continue;
if (b.numRecords != numRecords) throw std::runtime_error("ShaderInputInfoBuilder: merged vertex buffers disagree on record count");
b.addr = low;
if (b.attrNum >= ShaderVertexInputBuffer::MaxAttributes) throw std::runtime_error("ShaderInputInfoBuilder: vertex buffer attribute count exceeds the supported domain");
b.attrIndices[b.attrNum++] = ri;
merged = true;
break;
}
if (merged) continue;
if (info.buffersNum >= ShaderVertexInputInfo::MaxResources) throw std::runtime_error("ShaderInputInfoBuilder: vertex buffer count exceeds the supported domain");
auto& b = info.buffers[info.buffersNum++];
b.addr = base;
b.stride = stride;
b.numRecords = numRecords;
b.fetchIndex = static_cast<std::uint32_t>(info.resourcesDst[ri].fetchIndex);
b.attrNum = 1;
b.attrIndices[0] = ri;
}
for (int bi = 0; bi < info.buffersNum; ++bi) {
auto& b = info.buffers[bi];
for (int ri = 0; ri < b.attrNum; ++ri) {
const auto& r = info.resources[b.attrIndices[ri]];
const std::uint64_t base = (static_cast<std::uint64_t>(r.fields[0]) | (static_cast<std::uint64_t>(r.fields[1]) << 32u)) & 0xffffffffffffull;
b.attrOffsets[ri] = static_cast<std::uint32_t>(base - b.addr);
}
}
}
}
ShaderStageInputInfo BuildShaderStageInputInfo(ShaderStageKind stage, const GuestContext& context, std::uint32_t hostSubgroupSize, const MeshConfiguration* mesh, const TessellationConfiguration* tessellation) {
switch (stage) {
case ShaderStageKind::Compute: {
if (!context.compute.has_value()) {
throw std::runtime_error("ShaderInputInfoBuilder: GuestContext.compute is not set");
}
const auto& compute = *context.compute;
struct ComputeStorage {};
auto& computeStorage = HostThreadLocal<ShaderComputeInputInfo, ComputeStorage>();
computeStorage = ShaderComputeInputInfo{};
computeStorage.threadsNum[0] = compute.numThreads[0];
computeStorage.threadsNum[1] = compute.numThreads[1];
computeStorage.threadsNum[2] = compute.numThreads[2];
computeStorage.ldsSizeDwords = compute.ldsSizeDwords;
computeStorage.waveSize = context.waveSize;
computeStorage.hostSubgroupSize = hostSubgroupSize;
computeStorage.groupId[0] = compute.groupIdEnable[0];
computeStorage.groupId[1] = compute.groupIdEnable[1];
computeStorage.groupId[2] = compute.groupIdEnable[2];
computeStorage.tgSizeEn = compute.tgSizeEnable;
computeStorage.scratchSizeDwords = compute.scratchDwords;
computeStorage.threadIdsNum = static_cast<int>(compute.threadIdComponentCount);
computeStorage.partialGroups = compute.PartialGroups();
// Workgroup ids (and the thread-group size word) follow the user SGPRs.
computeStorage.workgroupRegister = static_cast<int>(context.userDataBaseRegister + context.userData.size());
ShaderStageInputInfo result;
result.compute = &computeStorage;
return result;
}
case ShaderStageKind::Pixel: {
if (!context.pixel.has_value()) {
throw std::runtime_error("ShaderInputInfoBuilder: GuestContext.pixel is not set");
}
const auto& pixel = *context.pixel;
struct PixelStorage {};
auto& pixelStorage = HostThreadLocal<ShaderPixelInputInfo, PixelStorage>();
pixelStorage = ShaderPixelInputInfo{};
for (std::uint32_t i = 0; i < 32; ++i) {
pixelStorage.interpolatorSettings[i] = pixel.interpolatorSettings[i];
}
pixelStorage.inputNum = pixel.interpolatorCount;
const auto place = [&](PixelInput input, bool loaded) {
if (!loaded) return;
if ((pixel.inputAddr & PixelInputBit(input)) == 0u) {
throw std::runtime_error("ShaderInputInfoBuilder: a loaded pixel input is missing from SPI_PS_INPUT_ADDR");
}
pixelStorage.psInputVgpr[static_cast<std::uint32_t>(input)] = PixelInputVgpr(pixel.inputAddr, input);
};
place(PixelInput::PerspectiveSample, (pixel.inputAddr & PixelInputBit(PixelInput::PerspectiveSample)) != 0u);
place(PixelInput::PerspectiveCenter, pixel.hasPerspectiveCenterVgpr);
place(PixelInput::PerspectiveCentroid, pixel.perspectiveCentroid);
place(PixelInput::LinearSample, (pixel.inputAddr & PixelInputBit(PixelInput::LinearSample)) != 0u);
place(PixelInput::LinearCenter, pixel.noPerspective);
place(PixelInput::LinearCentroid, pixel.linearCentroid);
place(PixelInput::PositionX, pixel.posX);
place(PixelInput::PositionY, pixel.posY);
place(PixelInput::PositionZ, pixel.posZ);
place(PixelInput::PositionW, pixel.posW);
place(PixelInput::FrontFace, pixel.frontFace);
place(PixelInput::Ancillary, pixel.ancillary);
place(PixelInput::LineStipple, (pixel.inputAddr & PixelInputBit(PixelInput::LineStipple)) != 0u);
place(PixelInput::PositionFixedPoint, (pixel.inputAddr & PixelInputBit(PixelInput::PositionFixedPoint)) != 0u);
for (std::uint32_t i = 0; i < 8; ++i) {
pixelStorage.targetOutputMode[i] = pixel.targetOutputMode[i];
}
pixelStorage.psPosX = pixel.posX;
pixelStorage.psPosY = pixel.posY;
pixelStorage.psPosZ = pixel.posZ;
pixelStorage.psPosW = pixel.posW;
pixelStorage.psFrontFace = pixel.frontFace;
pixelStorage.psFrontFaceAllBits = pixel.frontFaceAllBits;
pixelStorage.psAncillary = pixel.ancillary;
pixelStorage.psNoPerspective = pixel.noPerspective;
pixelStorage.psPixelKillEnable = pixel.pixelKillEnable;
pixelStorage.psDepthExportEnable = pixel.depthExportEnable;
pixelStorage.psSampleMaskExportEnable = pixel.sampleMaskExportEnable;
pixelStorage.psStencilRefExportEnable = pixel.stencilRefExportEnable;
pixelStorage.psSampleShading = pixel.sampleShading;
pixelStorage.psEarlyZ = pixel.earlyZ;
pixelStorage.psExecuteOnNoop = pixel.executeOnNoop;
pixelStorage.psConservativeZExport = pixel.conservativeZExport;
pixelStorage.psOrderedPixelShader = pixel.orderedPixelShader;
if (pixel.ditheredAlphaToCoverage && !pixel.alphaToCoverage) throw std::runtime_error("dithered alpha-to-coverage requires enabled coverage");
pixelStorage.psAlphaToCoverage = pixel.alphaToCoverage;
pixelStorage.psDitheredAlphaToCoverage = pixel.ditheredAlphaToCoverage;
pixelStorage.quadPixelMask = pixel.quadPixelMask;
pixelStorage.quadSampleMask = pixel.quadSampleMask;
ShaderStageInputInfo result;
result.pixel = &pixelStorage;
return result;
}
case ShaderStageKind::Vertex:
case ShaderStageKind::Local:
case ShaderStageKind::TessellationControl:
case ShaderStageKind::TessellationEvaluation:
case ShaderStageKind::Mesh: {
if (!context.vertex.has_value()) {
throw std::runtime_error("ShaderInputInfoBuilder: GuestContext.vertex is not set");
}
const auto& vertex = *context.vertex;
if (vertex.resourcesNum > vertex.resources.size()) throw std::runtime_error("ShaderInputInfoBuilder: invalid vertex resource count");
struct VertexStorage {};
auto& vertexStorage = HostThreadLocal<ShaderVertexInputInfo, VertexStorage>();
vertexStorage = ShaderVertexInputInfo{};
vertexStorage.logicalStage = _toIrShaderStage(stage);
vertexStorage.hostSubgroupSize = hostSubgroupSize;
vertexStorage.fetchEmbedded = vertex.fetchEmbedded;
vertexStorage.paClVsOutCntl = vertex.paClVsOutCntl;
vertexStorage.fetchExternal = false;
vertexStorage.fetchAttribReg = static_cast<int>(vertex.fetchAttribReg);
vertexStorage.fetchBufferReg = static_cast<int>(vertex.fetchBufferReg);
vertexStorage.resourcesNum = static_cast<int>(vertex.resourcesNum);
for (std::uint32_t i = 0; i < static_cast<std::uint32_t>(vertexStorage.resourcesNum); ++i) {
vertexStorage.resources[i].fields = vertex.resources[i].fields;
if (vertex.fetchEmbedded) vertexStorage.resources[i].fields = {0u, 0u, 0u, (static_cast<std::uint32_t>(IrBufferFormat::Format32_32_32_32Float) << 12u) | ShaderImageIdentitySwizzle};
vertexStorage.resourcesDst[i].registerStart = vertex.resourcesDst[i].registerStart;
vertexStorage.resourcesDst[i].registersNum = vertex.resourcesDst[i].registersNum;
vertexStorage.resourcesDst[i].attrId = vertex.resourcesDst[i].attrId;
vertexStorage.resourcesDst[i].fetchIndex = vertex.fetchEmbedded ? 0u : vertex.resourcesDst[i].fetchIndex;
}
_detectVertexBuffers(vertexStorage);
if (stage == ShaderStageKind::Local || stage == ShaderStageKind::TessellationControl || stage == ShaderStageKind::TessellationEvaluation) {
if (tessellation == nullptr) throw std::runtime_error("ShaderInputInfoBuilder: tessellation configuration is missing");
if (tessellation->inputControlPoints == 0u || tessellation->inputControlPoints > 32u || tessellation->outputControlPoints == 0u || tessellation->outputControlPoints > 32u) throw std::runtime_error("ShaderInputInfoBuilder: invalid tessellation control-point counts");
if ((tessellation->domain != 1u && tessellation->domain != 2u) || (tessellation->partitioning != 0u && tessellation->partitioning != 2u && tessellation->partitioning != 3u) || (tessellation->outputTopology != 2u && tessellation->outputTopology != 3u)) throw std::runtime_error("ShaderInputInfoBuilder: unsupported tessellation configuration");
if (tessellation->patchesPerGroup == 0u || tessellation->patchesPerGroup > 256u) throw std::runtime_error("ShaderInputInfoBuilder: invalid tessellation patches per group");
auto& target = vertexStorage.tess;
target.inputControlPoints = tessellation->inputControlPoints;
target.outputControlPoints = tessellation->outputControlPoints;
target.domain = tessellation->domain;
target.partitioning = tessellation->partitioning;
target.outputTopology = tessellation->outputTopology;
target.patchesPerGroup = tessellation->patchesPerGroup;
target.primitiveGroupSize = tessellation->primitiveGroupSize;
target.breakAtInstance = tessellation->breakAtInstance;
target.instancePatches = tessellation->instancePatches;
target.drawParameters = tessellation->drawParameters;
}
if (stage == ShaderStageKind::Mesh) {
if (mesh == nullptr) throw std::runtime_error("ShaderInputInfoBuilder: a mesh-stage program has no mesh configuration");
auto& target = vertexStorage.mesh;
target.threadsNum[0] = mesh->threadsPerGroup;
target.threadsNum[1] = 1u;
target.threadsNum[2] = 1u;
target.ldsSizeDwords = mesh->ldsSizeDwords;
target.waveSize = context.waveSize;
target.hostSubgroupSize = hostSubgroupSize;
target.inputPrimitive = mesh->inputPrimitive;
target.primitivesPerGroup = mesh->primitivesPerGroup;
target.verticesPerGroup = mesh->verticesPerGroup;
target.maxVertices = mesh->maxVertices;
target.maxPrimitives = mesh->maxPrimitives;
target.provokingVertex = mesh->provokingVertex;
target.esgsItemSize = mesh->esgsItemSize;
if (target.threadsNum[0] == 0u || target.maxVertices == 0u || target.maxPrimitives == 0u || target.provokingVertex > 2u) throw std::runtime_error("ShaderInputInfoBuilder: invalid mesh configuration");
}
ShaderStageInputInfo result;
result.vertex = &vertexStorage;
return result;
}
case ShaderStageKind::Unknown:
case ShaderStageKind::Fetch:
throw std::runtime_error("ShaderInputInfoBuilder: unexpected stage");
}
throw std::runtime_error("ShaderInputInfoBuilder: unexpected stage");
}
}