Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <cstdint>
#include <list>
#include <memory>
#include <utility>
#include <vector>
#include "prx/libc/include/general/AtomicSharedPtr.hpp"

Expand Down Expand Up @@ -45,6 +46,9 @@ void ReadUserWords(const QueueState& queue, DrawProgram& program, bool staticAbi
void InitializeMerged(const QueueState& queue, DrawProgram& program, std::uint32_t pointerBase, bool pointerRequired, bool staticAbi);
void DecodeGraphicsPrograms(DrawDecode& decoded, const QueueState& queue, const ShaderRegistry& registry, bool staticAbi, bool includeFragment);

void AppendProgramMemory(std::vector<ShaderRecompiler::MemoryRegion>& memory, const std::vector<DrawProgram>& programs);
std::vector<std::pair<std::size_t, std::size_t>> UserDataWords(const std::vector<DrawProgram>& programs, const std::vector<ShaderRecompiler::ProgramRole>& roles, std::uint32_t location);

struct PreparedGraphicsStage {
std::shared_ptr<const ShaderSnapshot> snapshot;
PreparedShaders::Entry entry;
Expand Down
35 changes: 15 additions & 20 deletions core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,9 @@ DrawVerdict Driver::draw(QueueState& queue, std::span<const std::uint32_t> packe
phaseTiming.Phase(DrawRowDecode);

const auto locate = [&](std::uint32_t location) -> std::optional<std::pair<std::size_t, std::size_t>> {
if (location == 0x280u) return std::nullopt;
for (std::size_t i = 0; i < programs.size(); ++i) {
if (roles[i] == Role::Fragment || roles[i] == Role::GeometryBack || location < programs[i].userDataBase) continue;
const auto word = location - programs[i].userDataBase + (8u - programs[i].firstUserSgpr);
if (word < programs[i].userData.size()) return std::make_pair(i, static_cast<std::size_t>(word));
}
return std::nullopt;
const auto words = UserDataWords(programs, roles, location);
if (words.empty()) return std::nullopt;
return words.front();
};
if (drawParameters.indirect) {
auto& indirect = *drawParameters.indirect;
Expand All @@ -237,13 +233,11 @@ DrawVerdict Driver::draw(QueueState& queue, std::span<const std::uint32_t> packe
}
std::vector<ShaderRecompiler::MemoryRegion> memory;
memory.reserve(2 * programs.size() + 32);
AppendProgramMemory(memory, programs);
std::vector<ShaderRecompiler::LinkedProgram> linked;
linked.reserve(programs.size());
for (std::size_t i = 0; i < programs.size(); ++i) {
const auto& program = programs[i];
for (const auto& region : program.memory) {
if (!region.bytes.empty()) memory.push_back(region);
}
linked.push_back({roles[i], program.binary, program.userDataBase, program.firstUserSgpr, program.userData});
}
timing.Mark("prepare");
Expand Down Expand Up @@ -529,9 +523,9 @@ DrawVerdict Driver::draw(QueueState& queue, std::span<const std::uint32_t> packe
std::vector<Pm4::DrawArguments> records;
for (std::uint32_t record = 0; record < count; ++record) records.push_back(Pm4::ReadDrawArguments(indirect, record));
Graphics::CountIndirectDraw(*indirectCpu, std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - readStart).count());
const auto baseVertexWord = locate(indirect.baseVertexLocation);
const auto startInstanceWord = locate(indirect.startInstanceLocation);
const auto drawIndexWord = indirect.drawIndexEnabled ? locate(indirect.drawIndexLocation) : std::nullopt;
const auto baseVertexWords = UserDataWords(programs, roles, indirect.baseVertexLocation);
const auto startInstanceWords = UserDataWords(programs, roles, indirect.startInstanceLocation);
const auto drawIndexWords = indirect.drawIndexEnabled ? UserDataWords(programs, roles, indirect.drawIndexLocation) : std::vector<std::pair<std::size_t, std::size_t>>{};
const auto drawable = [&](const Pm4::DrawArguments& arguments) { return arguments.count != 0 && arguments.instances != 0 && (!drawParameters.indexed || arguments.firstVertexOrIndex < drawParameters.indexCount); };
std::size_t lastDrawable = records.size();
for (std::size_t record = 0; record < records.size(); ++record) {
Expand All @@ -542,14 +536,15 @@ DrawVerdict Driver::draw(QueueState& queue, std::span<const std::uint32_t> packe
if (traceIndirect) std::fprintf(stderr, "[draw] record %u: count %u instances %u first %u vertexOffset %u startInstance %u\n", record, arguments.count, arguments.instances, arguments.firstVertexOrIndex, arguments.vertexOffset, arguments.firstInstance);
if (arguments.count == 0 || arguments.instances == 0) continue;
std::set<std::size_t> patched;
const auto patch = [&](const std::optional<std::pair<std::size_t, std::size_t>>& word, std::uint32_t value) {
if (!word) return;
writablePrograms[word->first].userData[word->second] = value;
patched.insert(word->first);
const auto patch = [&](const std::vector<std::pair<std::size_t, std::size_t>>& words, std::uint32_t value) {
for (const auto& [program, word] : words) {
writablePrograms[program].userData[word] = value;
patched.insert(program);
}
};
patch(baseVertexWord, indirect.recordBytes == 20 ? arguments.vertexOffset : arguments.firstVertexOrIndex);
patch(startInstanceWord, arguments.firstInstance);
patch(drawIndexWord, record);
patch(baseVertexWords, indirect.recordBytes == 20 ? arguments.vertexOffset : arguments.firstVertexOrIndex);
patch(startInstanceWords, arguments.firstInstance);
patch(drawIndexWords, record);
if (graphics.stages.tessellation) {
for (std::size_t i = 0; i < programs.size(); ++i) {
if (programs[i].binary.stage == Stage::TessellationControl || programs[i].binary.stage == Stage::TessellationEvaluation) patched.insert(i);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,34 @@ void InitializeMerged(const QueueState& queue, DrawProgram& program, std::uint32
const auto address = static_cast<std::uint64_t>(low) | (static_cast<std::uint64_t>(high) << 32u);
require(address != 0 || !pointerRequired, "merged shader user-data address is null");
if (address == 0) return;
GuestMemory::CheckRange(reinterpret_cast<const void*>(address), 8, 4);
if (pointerRequired) GuestMemory::CheckRange(reinterpret_cast<const void*>(address), 8, 4);
program.userData[0] = low;
program.userData[1] = high;
}

void AppendProgramMemory(std::vector<ShaderRecompiler::MemoryRegion>& memory, const std::vector<DrawProgram>& programs) {
for (const auto& program : programs) {
for (const auto& region : program.memory) {
if (region.bytes.empty()) continue;
const bool listed = std::any_of(memory.begin(), memory.end(), [&](const ShaderRecompiler::MemoryRegion& known) { return known.guestAddress == region.guestAddress && known.bytes.data() == region.bytes.data() && known.bytes.size() == region.bytes.size(); });
if (!listed) memory.push_back(region);
}
}
}

std::vector<std::pair<std::size_t, std::size_t>> UserDataWords(const std::vector<DrawProgram>& programs, const std::vector<ShaderRecompiler::ProgramRole>& roles, std::uint32_t location) {
using Role = ShaderRecompiler::ProgramRole;
require(roles.size() == programs.size(), "draw program roles are incomplete");
std::vector<std::pair<std::size_t, std::size_t>> words;
if (location == 0x280u) return words;
for (std::size_t i = 0; i < programs.size(); ++i) {
if (roles[i] == Role::Fragment || roles[i] == Role::GeometryBack || location < programs[i].userDataBase) continue;
const auto word = location - programs[i].userDataBase + (8u - programs[i].firstUserSgpr);
if (word < programs[i].userData.size()) words.emplace_back(i, static_cast<std::size_t>(word));
}
return words;
}

void DecodeGraphicsPrograms(DrawDecode& decoded, const QueueState& queue, const ShaderRegistry& registry, bool staticAbi, bool includeFragment) {
using Stage = ShaderRecompiler::ShaderStage;
using Role = ShaderRecompiler::ProgramRole;
Expand Down Expand Up @@ -95,11 +118,21 @@ void DecodeGraphicsPrograms(DrawDecode& decoded, const QueueState& queue, const
programs.push_back(prepare(programAddress(base), type, stage, resources, users));
roles.push_back(role);
};
const auto& graphics = product->state;
auto& graphics = product->state;
if (graphics.stages.path == Graphics::ShaderPath::Tessellation) {
if (!staticAbi || programAddress(0x148) != 0) append(0x148, 5, Stage::Local, 0x10b, 0x10c, Role::Local);
append(0x108, 7, Stage::TessellationControl, 0x10b, 0x10c, Role::Hull);
InitializeMerged(queue, programs.back(), 0x102, true, staticAbi);
const auto localAddress = programAddress(0x148);
const auto local = !staticAbi || localAddress != 0 ? ProgramSnapshot(registry, localAddress, {3, 5}) : nullptr;
const bool mergedHull = local != nullptr && !local->header.empty() && local->type == 3;
require(graphics.stages.tessellation.has_value(), "tessellation draw has no tessellation configuration");
graphics.stages.tessellation->mergedHull = mergedHull;
if (mergedHull) {
append(0x148, 3, Stage::Local, 0x10b, 0x10c, Role::Local);
append(0x148, 3, Stage::TessellationControl, 0x10b, 0x10c, Role::Hull);
} else {
if (local != nullptr) append(0x148, 5, Stage::Local, 0x10b, 0x10c, Role::Local);
append(0x108, 7, Stage::TessellationControl, 0x10b, 0x10c, Role::Hull);
}
InitializeMerged(queue, programs.back(), 0x102, !mergedHull, staticAbi);
append(0x0c8, 2, Stage::TessellationEvaluation, 0x08b, 0x08c, Role::Domain);
} else if (graphics.stages.path == Graphics::ShaderPath::Geometry) {
const auto frontAddress = programAddress(0xc8);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ void Driver::lookupDraw(const Submission& submission, const std::shared_ptr<Vulk
if (graphics.stages.tessellation) {
const auto& tess = *graphics.stages.tessellation;
for (const auto value : {tess.inputControlPoints, tess.outputControlPoints, tess.domain, tess.partitioning, tess.outputTopology, tess.patchesPerGroup, tess.primitiveGroupSize, static_cast<std::uint32_t>(tess.breakAtInstance)}) mix(value);
mix(tess.mergedHull);
}
mix(graphics.rectList);
mix(programs.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ bool Driver::sameDecode(const DrawDecode& a, const DrawDecode& b) {
const auto sameTess = [](const std::optional<ShaderRecompiler::TessellationConfiguration>& x, const std::optional<ShaderRecompiler::TessellationConfiguration>& y) {
if (x.has_value() != y.has_value()) return false;
if (!x) return true;
return x->inputControlPoints == y->inputControlPoints && x->outputControlPoints == y->outputControlPoints && x->domain == y->domain && x->partitioning == y->partitioning && x->outputTopology == y->outputTopology && x->patchesPerGroup == y->patchesPerGroup && x->primitiveGroupSize == y->primitiveGroupSize && x->breakAtInstance == y->breakAtInstance;
return x->inputControlPoints == y->inputControlPoints && x->outputControlPoints == y->outputControlPoints && x->domain == y->domain && x->partitioning == y->partitioning && x->outputTopology == y->outputTopology && x->patchesPerGroup == y->patchesPerGroup && x->primitiveGroupSize == y->primitiveGroupSize && x->breakAtInstance == y->breakAtInstance && x->mergedHull == y->mergedHull;
};
if (s.stages.path != t.stages.path || s.stages.registerValue != t.stages.registerValue || s.stages.vertexWaveSize != t.stages.vertexWaveSize || s.stages.fragmentWaveSize != t.stages.fragmentWaveSize || !sameMesh(s.stages.mesh, t.stages.mesh) || !sameTess(s.stages.tessellation, t.stages.tessellation)) return false;
if (!sameColor(s.color, t.color) || s.colors.size() != t.colors.size() || s.blends.size() != t.blends.size()) return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ std::optional<ShaderRecompiler::ShaderFloatMode> DecodeFloatMode(const Registers
case 0: rsrc1 = 0x212; fp16OverflowBit = 26; break;
case 1: rsrc1 = 0x00a; fp16OverflowBit = 29; break;
case 2: case 4: case 6: rsrc1 = 0x08a; fp16OverflowBit = 31; break;
case 5: case 7: rsrc1 = 0x10a; fp16OverflowBit = 30; break;
case 3: case 5: case 7: rsrc1 = 0x10a; fp16OverflowBit = 30; break;
default: return std::nullopt;
}
const auto found = registers.find(rsrc1);
Expand Down Expand Up @@ -901,7 +901,7 @@ std::unique_ptr<RegisteredPreparation> PlanRegistered(const ShaderSnapshot& snap
case 1: stage = Stage::Fragment; programRegister = 0x008; resourceRegister = 0x00b; break;
case 2: stage = Stage::Vertex; programRegister = 0x0c8; resourceRegister = 0x08b; firstUser = 8; break;
case 4: stage = Stage::Mesh; programRegister = 0x0c8; resourceRegister = 0x08b; break;
case 5: return {};
case 3: case 5: return {};
case 6: stage = Stage::Mesh; programRegister = 0x088; resourceRegister = 0x08b; break;
case 7: stage = Stage::TessellationControl; programRegister = 0x108; resourceRegister = 0x10b; break;
default:
Expand Down Expand Up @@ -1350,13 +1350,13 @@ std::vector<PreparedGraphicsStage> PrepareGraphicsStages(const DrawDecode& decod
require(expected == decoded.roles, "graphics ABI linked programs do not match the stage routing");
std::vector<ShaderRecompiler::LinkedProgram> linked;
std::vector<ShaderRecompiler::MemoryRegion> memory;
AppendProgramMemory(memory, decoded.programs);
for (std::size_t index = 0; index < decoded.programs.size(); ++index) {
const auto& program = decoded.programs[index];
require(program.snapshot != nullptr && program.codeOffset < program.snapshot->code.size(), "graphics ABI has an invalid registered entry point");
const auto code = std::span(program.snapshot->code).subspan(program.codeOffset);
require(program.binary.code.data() == code.data() && program.binary.code.size() == code.size() && program.binary.codeAddress == program.snapshot->codeAddress + program.codeOffset * sizeof(std::uint32_t), "graphics ABI entry point differs from its snapshot");
linked.push_back({decoded.roles[index], program.binary, program.userDataBase, program.firstUserSgpr, program.userData});
memory.insert(memory.end(), program.memory.begin(), program.memory.end());
}
std::vector<PreparedGraphicsStage> prepared;
std::uint32_t pushOffset = 0;
Expand Down
41 changes: 39 additions & 2 deletions core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4739,7 +4739,7 @@ void verifyPixelRequestSerialization() {
minimal.context.pixel->layerExported = true;
minimal.context.pixel->ditheredAlphaToCoverage = true;
const auto encoded = serializer.Serialize(minimal);
require(requestPrefix(encoded, 8u) == "NVNQQSUAAAA=", "new requests did not use serialization version 37");
require(requestPrefix(encoded, 8u) == "NVNQQSYAAAA=", "new requests did not use serialization version 38");
const std::array<std::uint32_t, 2> fallbacks{0x40u, 0x1ac4u};
auto withFallbacks = minimal;
withFallbacks.context.linkedCallFallbacks = fallbacks;
Expand Down Expand Up @@ -4857,7 +4857,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=", "NVNQQSYAAAA="}) {
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQScAAAA="}) {
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 @@ -5143,6 +5143,42 @@ void verifyDepthRemapRequests() {
require(key != other, "the depth remap mode did not change the shader cache key");
}

void verifyMergedHullConfiguration() {
using namespace ShaderRecompiler;
static constexpr std::array<std::uint32_t, 1> code{0xbf810000u};
const RequestSerializer serializer;
RecompileRequest request{};
request.shader = {ShaderStage::TessellationControl, 0x10000u, code, 0, {}};
request.context.waveSize = 64;
request.context.vertex = ShaderVertexStageInfo{};
request.graphics = GraphicsCompileContext{0u, {}, std::nullopt, TessellationConfiguration{3u, 3u, 1u, 2u, 2u}, {}};
const auto separate = serializer.Serialize(request);
std::vector<std::uint64_t> separateKey;
RecompileCacheKey::Build(request, separateKey);
request.graphics->tessellation->mergedHull = true;
const auto merged = serializer.Serialize(request);
std::vector<std::uint64_t> mergedKey;
RecompileCacheKey::Build(request, mergedKey);
const auto replay = serializer.Deserialize(merged);
require(replay.request.graphics.has_value() && replay.request.graphics->tessellation.has_value() && replay.request.graphics->tessellation->mergedHull, "a merged hull configuration was lost in serialization");
require(!serializer.Deserialize(separate).request.graphics->tessellation->mergedHull, "a separate hull configuration was read as merged");
auto legacy = decodeBase64(merged);
require(legacy.size() > 51u && legacy[legacy.size() - 51u] == 1u, "the merged hull flag is not the last byte of the tessellation configuration");
legacy.erase(legacy.end() - 51);
for (const auto version : {30u, 31u, 32u, 34u, 35u, 36u, 37u}) {
auto older = legacy;
older[4] = static_cast<std::uint8_t>(version);
if (version < 36u) older.pop_back();
if (version < 35u) older.resize(older.size() - 5u);
if (version < 32u) older.pop_back();
const auto upgraded = serializer.Deserialize(encodeBase64(older));
const auto what = "a main version " + std::to_string(version) + " tessellation request";
require(upgraded.request.graphics.has_value() && upgraded.request.graphics->tessellation.has_value() && !upgraded.request.graphics->tessellation->mergedHull, (what + " read a merged hull flag").c_str());
require(serializer.Serialize(upgraded.request) == separate, (what + " misread the fields after its tessellation configuration").c_str());
}
require(mergedKey != separateKey && RecompileCacheKey::ContextHash(request) != RecompileCacheKey::ContextHash(serializer.Deserialize(separate).request), "the cache keys ignore whether the hull program is merged");
}

void verifyLegacyPixelRequests() {
using namespace ShaderRecompiler;
static constexpr std::array<std::string_view, 14> legacyPixelRequests{
Expand Down Expand Up @@ -7303,6 +7339,7 @@ int main(int argc, char** argv) {
verifyPixelRequestSerialization();
verifyVertexInfoVersion19();
verifyDepthRemapRequests();
verifyMergedHullConfiguration();
verifyPositionExportComponents();
verifyLegacyPixelRequests();
verifyPixelExportReplay();
Expand Down
Loading
Loading