223 lines
13 KiB
C++
223 lines
13 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::PerspectiveCenter, pixel.hasPerspectiveCenterVgpr);
|
|
place(PixelInput::PerspectiveCentroid, pixel.perspectiveCentroid);
|
|
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.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;
|
|
pixelStorage.psAlphaToCoverage = pixel.alphaToCoverage;
|
|
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 != 2u || (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;
|
|
}
|
|
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");
|
|
}
|
|
|
|
}
|