feat(shader): carry the shader float mode into the recompile request
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
4b6ab6bb73
commit
0cbc9586bc
|
|
@ -4,6 +4,7 @@
|
|||
#include "prx/libSceAgcDriver/Execution/include/ShaderMemory.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/QueueState.hpp"
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
|
@ -70,6 +71,7 @@ struct ShaderSnapshot {
|
|||
std::shared_ptr<const ShaderSnapshot> ReadRawComputeShader(std::uint64_t address);
|
||||
|
||||
std::uint64_t NullPixelProgramAddress();
|
||||
std::optional<ShaderRecompiler::ShaderFloatMode> RegisteredFloatMode(const ShaderSnapshot& snapshot);
|
||||
void PublishRegisteredShader(std::shared_ptr<ShaderRegistry>& registry, const std::shared_ptr<const ShaderSnapshot>& snapshot);
|
||||
|
||||
void ResolvePreparedGraphics(const ShaderSnapshot& front, const std::shared_ptr<const ShaderSnapshot>& fragment, std::uint32_t primitiveType, const ShaderRecompiler::SpirvTarget& target);
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet,
|
|||
}
|
||||
ShaderRecompiler::RecompileRequest request{
|
||||
{ShaderRecompiler::ShaderStage::Compute, address, std::span(snapshot.code).subspan(codeOffset), snapshot.headerAddress, snapshot.header},
|
||||
{(packet[4] & 0x8000u) != 0 ? 32u : 64u, 0, userData, compute, std::nullopt, std::nullopt, memory},
|
||||
{(packet[4] & 0x8000u) != 0 ? 32u : 64u, 0, userData, compute, std::nullopt, std::nullopt, memory, RegisteredFloatMode(snapshot)},
|
||||
localDevice->ComputeTarget((packet[4] & 0x8000u) != 0 ? 32u : 64u),
|
||||
{0, 0, 0, 128}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ ShaderRecompiler::RecompileResult Driver::materializeDrawStage(std::size_t i, st
|
|||
const auto waveSize = program.binary.stage == Stage::Fragment ? graphics.stages.fragmentWaveSize : graphics.stages.vertexWaveSize;
|
||||
ShaderRecompiler::RecompileRequest request{
|
||||
program.binary,
|
||||
{waveSize, program.firstUserSgpr, program.userData, std::nullopt, program.binary.stage == Stage::Fragment ? std::optional(pixel) : std::nullopt, vertexInfos[i], memory},
|
||||
{waveSize, program.firstUserSgpr, program.userData, std::nullopt, program.binary.stage == Stage::Fragment ? std::optional(pixel) : std::nullopt, vertexInfos[i], memory, RegisteredFloatMode(*program.snapshot)},
|
||||
localDevice->Target(),
|
||||
{0, 0, pushOffset, (graphics.stages.mesh ? ShaderRecompiler::MeshDrawPushOffsetBytes : Graphics::PipelinePushConstantBytes) - pushOffset},
|
||||
ShaderRecompiler::GraphicsCompileContext{program.firstUserSgpr, linked, graphics.stages.mesh, graphics.stages.tessellation, {drawParameters.indexAddress, drawParameters.indexCount, drawParameters.indexSize, drawParameters.instanceCount}}
|
||||
|
|
|
|||
|
|
@ -235,6 +235,23 @@ ShaderRecompiler::PreparedShaderInvocation InvocationFor(const ShaderSnapshot& s
|
|||
}
|
||||
throw std::runtime_error("AGC driver: prepared shader artifact is missing for the requested static ABI: address=" + std::to_string(request.shader.codeAddress) + " stage=" + std::to_string(static_cast<std::uint32_t>(request.shader.stage)) + " wave=" + std::to_string(request.context.waveSize) + " pushOffset=" + std::to_string(request.layout.pushConstantOffsetBytes) + " pushCapacity=" + std::to_string(request.layout.pushConstantSizeBytes) + " preparedLayouts=" + layouts);
|
||||
}
|
||||
std::optional<ShaderRecompiler::ShaderFloatMode> RegisteredFloatMode(const ShaderSnapshot& snapshot) {
|
||||
if (snapshot.registeredState == nullptr) return std::nullopt;
|
||||
std::uint32_t rsrc1;
|
||||
std::uint32_t fp16OverflowBit;
|
||||
switch (snapshot.type) {
|
||||
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;
|
||||
default: return std::nullopt;
|
||||
}
|
||||
const auto found = snapshot.registeredState->shader.find(rsrc1);
|
||||
if (found == snapshot.registeredState->shader.end()) return std::nullopt;
|
||||
const auto value = found->second;
|
||||
return ShaderRecompiler::ShaderFloatMode{(value >> 12u) & 0xffu, ((value >> 21u) & 1u) != 0u, ((value >> 23u) & 1u) != 0u, ((value >> fp16OverflowBit) & 1u) != 0u};
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename TValue>
|
||||
|
|
@ -352,7 +369,7 @@ std::vector<PreparedShaders::Entry> PrepareRegistered(const ShaderSnapshot& snap
|
|||
auto graph = ShaderRecompiler::GraphBuilder{}.Build(decoded, &swappc);
|
||||
ShaderRecompiler::Structurizer{}.Structurize(graph);
|
||||
const std::array<ShaderRecompiler::MemoryRegion, 2> memory{{{snapshot.codeAddress, std::as_bytes(std::span(snapshot.code))}, {snapshot.headerAddress, snapshot.header}}};
|
||||
ShaderRecompiler::RecompileRequest request{{stage, address, code, snapshot.headerAddress, snapshot.header}, {wave, firstUser, userData, compute, pixel, vertex, memory}, stage == Stage::Compute ? device.ComputeTarget(wave) : device.Target(), {0, 0, 0, 128}, graphics};
|
||||
ShaderRecompiler::RecompileRequest request{{stage, address, code, snapshot.headerAddress, snapshot.header}, {wave, firstUser, userData, compute, pixel, vertex, memory, RegisteredFloatMode(snapshot)}, stage == Stage::Compute ? device.ComputeTarget(wave) : device.Target(), {0, 0, 0, 128}, graphics};
|
||||
if (graphics && graphics->mesh) request.layout.pushConstantSizeBytes = ShaderRecompiler::MeshDrawPushOffsetBytes;
|
||||
std::vector<PreparedShaders::Entry> entries;
|
||||
const auto append = [&] {
|
||||
|
|
@ -404,7 +421,7 @@ std::vector<PreparedGraphicsStage> PrepareGraphicsStages(const DrawDecode& decod
|
|||
const auto wave = fragment ? decoded.state.stages.fragmentWaveSize : decoded.state.stages.vertexWaveSize;
|
||||
std::optional<ShaderRecompiler::ShaderVertexStageInfo> vertex;
|
||||
if (!fragment) vertex = Graphics::DecodeVertexStageInfo(program.binary.header, program.binary.headerAddress, program.userData, nullptr, true);
|
||||
ShaderRecompiler::RecompileRequest request{program.binary, {wave, program.firstUserSgpr, program.userData, {}, fragment ? std::optional(decoded.pixel) : std::nullopt, vertex, memory}, target, {0, 0, pushOffset, capacity - pushOffset}, ShaderRecompiler::GraphicsCompileContext{program.firstUserSgpr, linked, decoded.state.stages.mesh, decoded.state.stages.tessellation, {}}};
|
||||
ShaderRecompiler::RecompileRequest request{program.binary, {wave, program.firstUserSgpr, program.userData, {}, fragment ? std::optional(decoded.pixel) : std::nullopt, vertex, memory, RegisteredFloatMode(*program.snapshot)}, target, {0, 0, pushOffset, capacity - pushOffset}, ShaderRecompiler::GraphicsCompileContext{program.firstUserSgpr, linked, decoded.state.stages.mesh, decoded.state.stages.tessellation, {}}};
|
||||
std::vector<std::uint64_t> key;
|
||||
ShaderRecompiler::BuildPreparedShaderKey(request, key);
|
||||
std::shared_ptr<const ShaderRecompiler::SourceHandle> handle;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "Optimization/ResourceProgram.hpp"
|
||||
#include "Optimization/DescriptorBindingBuilder.hpp"
|
||||
#include "ShaderCacheDirectory.hpp"
|
||||
#include "ControlFlow/RequestSerializer.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
|
|
@ -394,6 +395,21 @@ void verifyKeySensitivity() {
|
|||
changes("the first binding", [](SampleRequest& sample) { sample.request.layout.firstBinding = 1; });
|
||||
changes("the push constant offset", [](SampleRequest& sample) { sample.request.layout.pushConstantOffsetBytes = 16; });
|
||||
changes("the push constant size", [](SampleRequest& sample) { sample.request.layout.pushConstantSizeBytes = 64; });
|
||||
changes("a float mode", [](SampleRequest& sample) { sample.request.context.floatMode = ShaderFloatMode{}; });
|
||||
const ShaderFloatMode astroMode{0xc0u, true, false, false};
|
||||
const auto withMode = [&](const ShaderFloatMode& mode) {
|
||||
SampleRequest sample;
|
||||
sample.request.context.floatMode = mode;
|
||||
return std::pair{sample.Key(), RecompileCacheKey::ContextHash(sample.request)};
|
||||
};
|
||||
const auto astro = withMode(astroMode);
|
||||
require(withMode(astroMode) == astro, "the float mode key is not deterministic");
|
||||
for (const auto& [what, mode] : std::initializer_list<std::pair<const char*, ShaderFloatMode>>{
|
||||
{"FLOAT_MODE", {0x00u, true, false, false}}, {"DX10_CLAMP", {0xc0u, false, false, false}},
|
||||
{"IEEE_MODE", {0xc0u, true, true, false}}, {"FP16_OVFL", {0xc0u, true, false, true}}}) {
|
||||
const auto changed = withMode(mode);
|
||||
require(changed.first != astro.first && changed.second != astro.second, std::string("the key ignores ") + what);
|
||||
}
|
||||
|
||||
SampleRequest moved;
|
||||
moved.userData[0] ^= 0x10000u;
|
||||
|
|
@ -445,6 +461,16 @@ void verifyKeySensitivity() {
|
|||
require(fragment.Key() != fragmentKey, "integer fragment output reused a float interface");
|
||||
}
|
||||
|
||||
void verifyFloatModeSerialization() {
|
||||
const RequestSerializer serializer;
|
||||
SampleRequest sample;
|
||||
require(!serializer.Deserialize(serializer.Serialize(sample.request)).request.context.floatMode.has_value(), "an unknown float mode came back from serialization");
|
||||
const ShaderFloatMode mode{0x04u, false, true, true};
|
||||
sample.request.context.floatMode = mode;
|
||||
const auto back = serializer.Deserialize(serializer.Serialize(sample.request));
|
||||
require(back.request.context.floatMode == mode, "the float mode did not survive serialization");
|
||||
}
|
||||
|
||||
void verifyStore() {
|
||||
require(ShaderDiskCache::Enabled(), "the disk cache is not enabled");
|
||||
SampleRequest sample;
|
||||
|
|
@ -996,6 +1022,7 @@ int main(int argc, char** argv) {
|
|||
verifyEntryRoundTrip();
|
||||
verifyArtifactStorageIsolation();
|
||||
verifyKeySensitivity();
|
||||
verifyFloatModeSerialization();
|
||||
verifyStore();
|
||||
verifyAcrossProcesses(argv[0]);
|
||||
verifyEmissionFailureMemo();
|
||||
|
|
|
|||
|
|
@ -916,12 +916,12 @@ void verifyPixelRequestSerialization() {
|
|||
minimal.context.waveSize = 64;
|
||||
minimal.context.pixel = ShaderPixelStageInfo{};
|
||||
const auto encoded = serializer.Serialize(minimal);
|
||||
require(requestPrefix(encoded, 8u) == "NVNQQQwAAAA=", "new requests did not use serialization version 12");
|
||||
require(requestPrefix(encoded, 8u) == "NVNQQQ0AAAA=", "new requests did not use serialization version 13");
|
||||
constexpr std::size_t mappingOffset = 8u + 37u + 18u + 163u;
|
||||
for (std::size_t bytes = 0; bytes < 8u; ++bytes) {
|
||||
expectFailure([&] { static_cast<void>(serializer.Deserialize(requestPrefix(encoded, mappingOffset + bytes))); }, "truncated data", "a truncated version-12 pixel mapping was accepted");
|
||||
}
|
||||
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQQ0AAAA="}) {
|
||||
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQQ4AAAA="}) {
|
||||
expectFailure([&] { static_cast<void>(serializer.Deserialize(unsupported)); }, "serialization version", "an unsupported request version was accepted");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
|
@ -480,6 +481,41 @@ void testShaderHeaderAlignment() {
|
|||
refused(expectFailure([&] { AgcDriverRegisterShader_nid_postfix(at(4, truncated)); }), "smaller than its fixed fields");
|
||||
}
|
||||
|
||||
void testRegisteredFloatMode() {
|
||||
using AgcDriver::DriverDetail::RegisteredFloatMode;
|
||||
using AgcDriver::DriverDetail::ShaderSnapshot;
|
||||
using AgcDriver::DriverDetail::RegisteredShaderState;
|
||||
struct Stage {
|
||||
std::uint8_t type;
|
||||
std::uint32_t rsrc1;
|
||||
std::uint32_t fp16OverflowBit;
|
||||
};
|
||||
constexpr std::array<Stage, 7> stages{{{0, 0x212, 26}, {1, 0x00a, 29}, {2, 0x08a, 31}, {4, 0x08a, 31}, {6, 0x08a, 31}, {5, 0x10a, 30}, {7, 0x10a, 30}}};
|
||||
constexpr std::array<std::uint32_t, 4> otherBits{26, 29, 30, 31};
|
||||
for (const auto& stage : stages) {
|
||||
const auto mode = [&](std::uint32_t value) {
|
||||
ShaderSnapshot snapshot{0x20000, 0, stage.type, {}, {}};
|
||||
auto state = std::make_shared<RegisteredShaderState>();
|
||||
state->shader.emplace(stage.rsrc1, value);
|
||||
snapshot.registeredState = state;
|
||||
return RegisteredFloatMode(snapshot);
|
||||
};
|
||||
const auto name = "stage type " + std::to_string(stage.type);
|
||||
const auto astro = mode((0xc0u << 12u) | (1u << 21u) | 0x3fu);
|
||||
check(astro.has_value() && astro->floatMode == 0xc0u && astro->dx10Clamp && !astro->ieeeMode && !astro->fp16Overflow, (name + ": FLOAT_MODE 0xc0 with DX10_CLAMP decoded wrong").c_str());
|
||||
const auto ieee = mode(1u << 23u);
|
||||
check(ieee && ieee->ieeeMode && ieee->floatMode == 0u && !ieee->dx10Clamp, (name + ": IEEE_MODE decoded wrong").c_str());
|
||||
check(mode(1u << stage.fp16OverflowBit)->fp16Overflow, (name + ": FP16_OVFL not read from bit " + std::to_string(stage.fp16OverflowBit)).c_str());
|
||||
for (const auto bit : otherBits) {
|
||||
if (bit != stage.fp16OverflowBit) check(!mode(1u << bit)->fp16Overflow, (name + ": bit " + std::to_string(bit) + " read as FP16_OVFL").c_str());
|
||||
}
|
||||
ShaderSnapshot missing{0x20000, 0, stage.type, {}, {}};
|
||||
check(!RegisteredFloatMode(missing).has_value(), (name + ": a snapshot without registered state has a float mode").c_str());
|
||||
missing.registeredState = std::make_shared<RegisteredShaderState>();
|
||||
check(!RegisteredFloatMode(missing).has_value(), (name + ": a missing RSRC1 has a float mode").c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void testWorkerFailure() {
|
||||
std::array<std::uint32_t, 5> words{0xc0031500, 1, 1, 1, 0x41};
|
||||
Packet packet{words.data(), static_cast<std::uint32_t>(words.size()), 0, {}};
|
||||
|
|
@ -580,6 +616,7 @@ int main() {
|
|||
testWaitFreeSubmissionTheCpuWaitsFor();
|
||||
testMultiSubmissions();
|
||||
testShaderHeaderAlignment();
|
||||
testRegisteredFloatMode();
|
||||
testWorkerFailure();
|
||||
check(expectFailure([] { LibcRunShutdown_nid_postfix(); }).find("required shader register") != std::string::npos, "shutdown lost worker failure");
|
||||
std::puts("AGC driver submit tests passed");
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ public:
|
|||
append(key, request.context.compute);
|
||||
append(key, request.context.pixel);
|
||||
append(key, request.context.vertex);
|
||||
append(key, request.context.floatMode);
|
||||
appendMesh(key, request);
|
||||
appendTessellation(key, request);
|
||||
std::uint64_t hash = 0xcbf29ce484222325ull;
|
||||
|
|
@ -82,6 +83,7 @@ private:
|
|||
append(key, request.context.compute);
|
||||
append(key, request.context.pixel);
|
||||
append(key, request.context.vertex);
|
||||
append(key, request.context.floatMode);
|
||||
appendMesh(key, request);
|
||||
appendTessellation(key, request);
|
||||
append(key, request.target);
|
||||
|
|
@ -133,6 +135,13 @@ private:
|
|||
for (const unsigned char byte : value) append(key, byte);
|
||||
}
|
||||
|
||||
static void append(std::vector<std::uint64_t>& key, const ShaderFloatMode& value) {
|
||||
append(key, value.floatMode);
|
||||
append(key, value.dx10Clamp);
|
||||
append(key, value.ieeeMode);
|
||||
append(key, value.fp16Overflow);
|
||||
}
|
||||
|
||||
static void append(std::vector<std::uint64_t>& key, const ShaderComputeStageInfo& value) {
|
||||
append(key, value.numThreads);
|
||||
append(key, value.ldsSizeDwords);
|
||||
|
|
|
|||
|
|
@ -519,6 +519,13 @@ void writeGuestContext(Writer& writer, const GuestContext& context) {
|
|||
writer.WriteU64(region.guestAddress);
|
||||
writer.WriteBytes(region.bytes);
|
||||
}
|
||||
writer.WriteBool(context.floatMode.has_value());
|
||||
if (context.floatMode.has_value()) {
|
||||
writer.WriteU32(context.floatMode->floatMode);
|
||||
writer.WriteBool(context.floatMode->dx10Clamp);
|
||||
writer.WriteBool(context.floatMode->ieeeMode);
|
||||
writer.WriteBool(context.floatMode->fp16Overflow);
|
||||
}
|
||||
}
|
||||
|
||||
GuestContext readGuestContext(Reader& reader, DeserializedRequest& result, std::uint32_t version) {
|
||||
|
|
@ -551,6 +558,14 @@ GuestContext readGuestContext(Reader& reader, DeserializedRequest& result, std::
|
|||
result.memory.push_back(region);
|
||||
}
|
||||
context.memory = result.memory;
|
||||
if (version >= 13u && reader.ReadBool()) {
|
||||
ShaderFloatMode mode;
|
||||
mode.floatMode = reader.ReadU32();
|
||||
mode.dx10Clamp = reader.ReadBool();
|
||||
mode.ieeeMode = reader.ReadBool();
|
||||
mode.fp16Overflow = reader.ReadBool();
|
||||
context.floatMode = mode;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
@ -699,7 +714,7 @@ std::string RequestSerializer::Serialize(const RecompileRequest& request) const
|
|||
std::string buffer;
|
||||
Writer writer(buffer);
|
||||
writer.WriteU32(0x41505335u);
|
||||
writer.WriteU32(12u);
|
||||
writer.WriteU32(13u);
|
||||
writeShaderBinary(writer, request.shader);
|
||||
writeGuestContext(writer, request.context);
|
||||
writeSpirvTarget(writer, request.target);
|
||||
|
|
@ -726,7 +741,7 @@ DeserializedRequest RequestSerializer::Deserialize(std::string_view text) const
|
|||
Reader reader(decoded);
|
||||
if (reader.ReadU32() != 0x41505335u) throw std::runtime_error("invalid recompile request signature");
|
||||
const auto version = reader.ReadU32();
|
||||
if (version < 1u || version > 12u) throw std::runtime_error("unsupported recompile request serialization version");
|
||||
if (version < 1u || version > 13u) throw std::runtime_error("unsupported recompile request serialization version");
|
||||
DeserializedRequest result{};
|
||||
result.request.shader = readShaderBinary(reader, result.shaderCode, result.shaderHeader);
|
||||
result.request.context = readGuestContext(reader, result, version);
|
||||
|
|
|
|||
|
|
@ -155,6 +155,15 @@ struct ShaderVertexStageInfo {
|
|||
bool fetchEmbedded;
|
||||
};
|
||||
|
||||
struct ShaderFloatMode {
|
||||
std::uint32_t floatMode = 0;
|
||||
bool dx10Clamp = false;
|
||||
bool ieeeMode = false;
|
||||
bool fp16Overflow = false;
|
||||
|
||||
bool operator==(const ShaderFloatMode&) const = default;
|
||||
};
|
||||
|
||||
struct GuestContext {
|
||||
std::uint32_t waveSize;
|
||||
std::uint32_t userDataBaseRegister;
|
||||
|
|
@ -163,6 +172,7 @@ struct GuestContext {
|
|||
std::optional<ShaderPixelStageInfo> pixel;
|
||||
std::optional<ShaderVertexStageInfo> vertex;
|
||||
std::span<const MemoryRegion> memory;
|
||||
std::optional<ShaderFloatMode> floatMode;
|
||||
};
|
||||
|
||||
struct MeshTargetLimits {
|
||||
|
|
|
|||
|
|
@ -284,6 +284,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- A [COND_EXEC range](../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Queues/Submission.cpp) must hold whole packets and end inside its command buffer. A chained INDIRECT_BUFFER or a REWIND inside a range throws, as what the CP does with the skipped dwords then is unknown.
|
||||
- A [COND_INDIRECT_BUFFER](../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Queues/Submission.cpp) (`sceAgcCbBranch`) runs only with the always-pass compare function; a comparison or a predicated packet throws. Modes 1 and 2 run the first buffer, assumed to be the if-then and if-then-else modes of AMD's PM4 packet. [sceAgcCbBranch](../../core/libs/prx/libSceAgc/Cb/src/Branch.cpp) does not check the compare address of an always-pass branch or the address of an empty buffer, as PPSA14632 passes an uninitialised compare address and a null else buffer of 0 dwords.
|
||||
- [SET_*_REG_INDIRECT](../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Queues/Submission.cpp) (libSceAgcDriver) reads its register list when the command buffer is submitted, with the packets. The console's graphics PFP reads it when it reaches the packet, so a list written after the submission (by the CPU, or by GPU work ahead of the packet behind PFP_SYNC_ME) is read here before that write.
|
||||
- The [shader float mode](../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp) (`FLOAT_MODE`, `DX10_CLAMP`, `IEEE_MODE` and `FP16_OVFL` of `COMPUTE_PGM_RSRC1` and `SPI_SHADER_PGM_RSRC1_PS/GS/HS`, bit positions from Mesa's `gfx10.json` and `gfx103.json`) is decoded into `GuestContext::floatMode` and is part of the shader cache keys and the serialized request, but no translation reads it yet (#981). Every float semantic still follows the single mode it was measured or written in, as the entries below record. The mode comes from the registered shader header's `sh_registers`, the same source at registration and at draw/dispatch so the prepared artifact keys match. A title that rewrites RSRC1 at run time, or a raw compute program with no header, compiles with that mode or none.
|
||||
- [v_mullit_f32](../../core/shader/recompiler/Translation/src/FloatInstructions.cpp) follows the hardware measured with the MODE register at IEEE=1 and denormals kept (a signaling NaN src0 is quieted, a positive denormal src2 counts as positive): the recompiler does not read MODE, so other IEEE/denormal settings are not modelled.
|
||||
- [v_fma_legacy_f32](../../core/shader/recompiler/Translation/src/FloatInstructions.cpp) (VOP3 0x140) is assumed to be fused, which has not been measured on hardware. LLVM decodes this encoding as `v_mad_legacy_f32` for gfx1013, and `v_mac_legacy_f32` (VOP2 0x06) was measured to round its product before the add, so 0x140 may do the same.
|
||||
- The executable file that [relinker](../../core/relinker/elfpatcher/src/windows/WindowsPeWriter.cpp) generates opens the console when launched, which is inconvenient for playability.
|
||||
|
|
|
|||
Loading…
Reference in New Issue