feat(agc): accept conservative Z export in DB_SHADER_CONTROL
DecodeState, the draw precheck and DecodePixelStageInfo rejected every nonzero CONSERVATIVE_Z_EXPORT (DB_SHADER_CONTROL bits 13-14). EXPORT_LESS_THAN_Z (1) and EXPORT_GREATER_THAN_Z (2) promise that the exported Z stays on one side of the interpolated Z. The value now reaches the recompiler as a pixel stage field, and a pixel shader that writes FragDepth declares DepthLess or DepthGreater next to DepthReplacing, the SPIR-V modes RADV maps to these values. Vulkan lets those modes only add a depth test of the interpolated Z before shading, so a shader that keeps the promise renders the same. The reserved value 3 still throws. The field is part of the recompile cache key, the draw key and the serialized request (version 7), and ValidateShaders accepts both modes.
This commit is contained in:
parent
681f83e51e
commit
4a67b140fc
|
|
@ -62,6 +62,7 @@ void Driver::lookupDraw(const Submission& submission, const std::shared_ptr<Vulk
|
|||
for (std::uint32_t i = 0; i < pixel.interpolatorCount; ++i) mix(pixel.interpolatorSettings[i]);
|
||||
mix(pixel.inputAddr);
|
||||
for (const bool flag : {pixel.wave32, pixel.hasPerspectiveCenterVgpr, pixel.perspectiveCentroid, pixel.posX, pixel.posY, pixel.posZ, pixel.posW, pixel.frontFace, pixel.ancillary, pixel.sampleShading, pixel.noPerspective, pixel.linearCentroid, pixel.pixelKillEnable, pixel.depthExportEnable, pixel.sampleMaskExportEnable, pixel.earlyZ, pixel.executeOnNoop}) mix(flag);
|
||||
mix(static_cast<std::uint64_t>(pixel.conservativeZExport));
|
||||
for (const auto value : pixel.targetOutputMode) mix(value);
|
||||
for (const auto value : pixel.targetExportMapping) mix(value);
|
||||
std::lock_guard cacheLock(drawCacheMutex);
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ bool Driver::sameDecode(const DrawDecode& a, const DrawDecode& b) {
|
|||
if (std::memcmp(&s.viewport, &t.viewport, sizeof(VkViewport)) != 0 || std::memcmp(&s.scissor, &t.scissor, sizeof(VkRect2D)) != 0) return false;
|
||||
const auto& p = a.pixel;
|
||||
const auto& q = b.pixel;
|
||||
if (p.interpolatorCount != q.interpolatorCount || p.interpolatorSettings != q.interpolatorSettings || p.wave32 != q.wave32 || p.inputAddr != q.inputAddr || p.hasPerspectiveCenterVgpr != q.hasPerspectiveCenterVgpr || p.perspectiveCentroid != q.perspectiveCentroid || p.posX != q.posX || p.posY != q.posY || p.posZ != q.posZ || p.posW != q.posW || p.frontFace != q.frontFace || p.ancillary != q.ancillary || p.sampleShading != q.sampleShading || p.noPerspective != q.noPerspective || p.linearCentroid != q.linearCentroid || p.pixelKillEnable != q.pixelKillEnable || p.depthExportEnable != q.depthExportEnable || p.sampleMaskExportEnable != q.sampleMaskExportEnable || p.earlyZ != q.earlyZ || p.executeOnNoop != q.executeOnNoop || p.targetOutputMode != q.targetOutputMode || p.targetExportMapping != q.targetExportMapping) return false;
|
||||
if (p.interpolatorCount != q.interpolatorCount || p.interpolatorSettings != q.interpolatorSettings || p.wave32 != q.wave32 || p.inputAddr != q.inputAddr || p.hasPerspectiveCenterVgpr != q.hasPerspectiveCenterVgpr || p.perspectiveCentroid != q.perspectiveCentroid || p.posX != q.posX || p.posY != q.posY || p.posZ != q.posZ || p.posW != q.posW || p.frontFace != q.frontFace || p.ancillary != q.ancillary || p.sampleShading != q.sampleShading || p.noPerspective != q.noPerspective || p.linearCentroid != q.linearCentroid || p.pixelKillEnable != q.pixelKillEnable || p.depthExportEnable != q.depthExportEnable || p.sampleMaskExportEnable != q.sampleMaskExportEnable || p.earlyZ != q.earlyZ || p.executeOnNoop != q.executeOnNoop || p.conservativeZExport != q.conservativeZExport || p.targetOutputMode != q.targetOutputMode || p.targetExportMapping != q.targetExportMapping) return false;
|
||||
if (a.roles != b.roles || a.programs.size() != b.programs.size()) return false;
|
||||
for (std::size_t i = 0; i < a.programs.size(); ++i) {
|
||||
const auto& x = a.programs[i];
|
||||
|
|
|
|||
|
|
@ -118,8 +118,9 @@ ShaderRecompiler::ShaderPixelStageInfo DecodePixelStageInfo(const Registers& con
|
|||
const auto shaderControl = read(context, dbShaderControl, RegisterBank::Context);
|
||||
// Bits 9 and 11 are EXEC_ON_HIER_FAIL and ALPHA_TO_MASK_DISABLE on GFX10; neither changes what the
|
||||
// recompiled pixel shader computes.
|
||||
if (((shaderControl >> 13u) & 0x3u) != 0) {
|
||||
throw std::runtime_error("AGC graphics: DB_SHADER_CONTROL.CONSERVATIVE_Z_EXPORT is unsupported");
|
||||
const auto conservativeZExport = (shaderControl >> 13u) & 0x3u;
|
||||
if (conservativeZExport == 3u) {
|
||||
throw std::runtime_error("AGC graphics: DB_SHADER_CONTROL.CONSERVATIVE_Z_EXPORT uses the reserved value 3");
|
||||
}
|
||||
const auto colFormat = nullProgram ? readOr(context, spiShaderColFormat, RegisterBank::Context, 0u) : read(context, spiShaderColFormat, RegisterBank::Context);
|
||||
std::array<std::uint8_t, 8> targetOutputMode{};
|
||||
|
|
@ -152,6 +153,7 @@ ShaderRecompiler::ShaderPixelStageInfo DecodePixelStageInfo(const Registers& con
|
|||
.sampleMaskExportEnable = sampleMaskExportEnable,
|
||||
.earlyZ = zOrder == 1u && !pixelKillEnable && !depthExportEnable && !sampleMaskExportEnable,
|
||||
.executeOnNoop = ((shaderControl >> 10u) & 0x1u) != 0,
|
||||
.conservativeZExport = static_cast<ShaderRecompiler::ConservativeZExport>(conservativeZExport),
|
||||
.targetOutputMode = targetOutputMode,
|
||||
.targetExportMapping = exportMappings
|
||||
};
|
||||
|
|
|
|||
|
|
@ -368,7 +368,7 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
|
|||
mode(spv::ExecutionModeVertexOrderCw, {});
|
||||
Require(module.modes.size() == 3, "unsupported tessellation-evaluation execution mode");
|
||||
} else if (fragment) {
|
||||
for (const auto& [name, operands] : module.modes) Require(operands.empty() && (name == spv::ExecutionModeOriginUpperLeft || name == spv::ExecutionModeEarlyFragmentTests), "unsupported fragment execution mode");
|
||||
for (const auto& [name, operands] : module.modes) Require(operands.empty() && (name == spv::ExecutionModeOriginUpperLeft || name == spv::ExecutionModeEarlyFragmentTests || name == spv::ExecutionModeDepthLess || name == spv::ExecutionModeDepthGreater), "unsupported fragment execution mode");
|
||||
} else Require(module.modes.empty(), "unsupported vertex execution mode");
|
||||
std::set<std::pair<std::uint32_t, std::uint32_t>> descriptors;
|
||||
bool push = false;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ constexpr std::uint32_t LayerExports = (1u << 18u) | (1u << 19u) | (1u << 21u) |
|
|||
constexpr std::uint32_t DepthControlMask = ~0x007007f0u;
|
||||
// EXEC_ON_HIER_FAIL / EXEC_ON_NOOP / EXEC_IF_OVERLAPPED (bits 9, 10, 17) only force the pixel shader
|
||||
// to run, which it always does here.
|
||||
constexpr std::uint32_t ShaderControlMask = ~(0x00009870u | 0x00020600u);
|
||||
constexpr std::uint32_t ShaderControlMask = ~(0x0000f870u | 0x00020600u);
|
||||
constexpr std::uint32_t AlphaToCoverageMask = ~0x0001ff00u;
|
||||
constexpr std::uint32_t ScanModeMask = ~2u;
|
||||
constexpr std::uint32_t ScanControlMask = ~0x06003fffu;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/ShaderInputState.hpp"
|
||||
#include "ControlFlow/RequestSerializer.hpp"
|
||||
#include "CacheKey.hpp"
|
||||
#include <spirv/unified1/spirv.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
|
@ -1283,6 +1284,7 @@ void misalignedShaderDataTests() {
|
|||
|
||||
struct ModuleShape {
|
||||
bool fragment = false;
|
||||
std::optional<std::uint32_t> fragmentMode;
|
||||
bool push = false;
|
||||
std::uint32_t pushLength = 32;
|
||||
std::uint32_t pushStride = 4;
|
||||
|
|
@ -1446,6 +1448,7 @@ std::vector<std::uint32_t> makeModule(const ModuleShape& shape) {
|
|||
else emit(words, spv::OpEntryPoint, {shape.fragment ? spv::ExecutionModelFragment : spv::ExecutionModelVertex, main, 0x6e69616du, 0, output});
|
||||
words[entryPointOffset] += static_cast<std::uint32_t>(extraInterface.size()) << 16u;
|
||||
words.insert(words.end(), extraInterface.begin(), extraInterface.end());
|
||||
if (shape.fragmentMode) emit(words, spv::OpExecutionMode, {main, *shape.fragmentMode});
|
||||
if (shape.fragment) emit(words, spv::OpExecutionMode, {main, spv::ExecutionModeOriginUpperLeft});
|
||||
words.insert(words.end(), annotations.begin(), annotations.end());
|
||||
words.insert(words.end(), declarations.begin(), declarations.end());
|
||||
|
|
@ -1563,6 +1566,81 @@ std::vector<LocatedInput> locatedInputs(std::span<const std::uint32_t> words) {
|
|||
return result;
|
||||
}
|
||||
|
||||
constexpr std::array<std::uint32_t, 3> DepthExportCode{0xf8001881u, 2u, 0xbf810000u};
|
||||
|
||||
AgcDriver::QueueState depthExportQueue(std::uint32_t shaderControl) {
|
||||
auto queue = makeState();
|
||||
queue.context[0x1b3] = 0x402u;
|
||||
queue.context[0x1b4] = 0x402u;
|
||||
queue.context[0x203] = shaderControl;
|
||||
return queue;
|
||||
}
|
||||
|
||||
ShaderRecompiler::RecompileRequest depthExportRequest(std::uint32_t shaderControl) {
|
||||
const auto queue = depthExportQueue(shaderControl);
|
||||
ShaderRecompiler::RecompileRequest request{};
|
||||
request.shader = {ShaderRecompiler::ShaderStage::Fragment, 0x30000u, DepthExportCode, 0, {}};
|
||||
request.context.waveSize = 64;
|
||||
request.context.pixel = AgcDriver::Graphics::DecodePixelStageInfo(queue.context, IdentityExports);
|
||||
request.target.vulkanVersion = 0x00401000u;
|
||||
request.target.spirvVersion = 0x00010300u;
|
||||
request.target.subgroupSize = 64;
|
||||
request.layout.pushConstantSizeBytes = 128;
|
||||
request.useCache = false;
|
||||
return request;
|
||||
}
|
||||
|
||||
std::set<std::uint32_t> depthModes(const ShaderRecompiler::RecompileRequest& request) {
|
||||
const auto result = ShaderRecompiler::Recompile(request);
|
||||
const auto& words = result.spirv.Words();
|
||||
std::set<std::uint32_t> modes;
|
||||
for (std::size_t at = 5; at < words.size() && (words[at] >> 16u) != 0; at += words[at] >> 16u) {
|
||||
if (static_cast<spv::Op>(words[at] & 0xffffu) != spv::OpExecutionMode) continue;
|
||||
const auto mode = words[at + 2];
|
||||
if (mode == spv::ExecutionModeDepthReplacing || mode == spv::ExecutionModeDepthGreater || mode == spv::ExecutionModeDepthLess || mode == spv::ExecutionModeDepthUnchanged) modes.insert(mode);
|
||||
}
|
||||
return modes;
|
||||
}
|
||||
|
||||
void conservativeZExportTests() {
|
||||
const std::array<std::pair<std::uint32_t, std::set<std::uint32_t>>, 4> cases{{
|
||||
{0x0021u, {spv::ExecutionModeDepthReplacing}},
|
||||
{0x2021u, {spv::ExecutionModeDepthReplacing, spv::ExecutionModeDepthLess}},
|
||||
{0x4021u, {spv::ExecutionModeDepthReplacing, spv::ExecutionModeDepthGreater}},
|
||||
{0x2020u, {}}
|
||||
}};
|
||||
const ShaderRecompiler::RequestSerializer serializer;
|
||||
std::set<std::vector<std::uint64_t>> keys;
|
||||
for (const auto& [shaderControl, expected] : cases) {
|
||||
const auto request = depthExportRequest(shaderControl);
|
||||
const auto replayed = serializer.Deserialize(serializer.Serialize(request));
|
||||
Require(depthModes(request) == expected && depthModes(replayed.request) == expected, "DB_SHADER_CONTROL " + std::to_string(shaderControl) + " declared the wrong depth execution modes");
|
||||
std::vector<std::uint64_t> key;
|
||||
ShaderRecompiler::RecompileCacheKey::Build(request, key);
|
||||
keys.insert(key);
|
||||
}
|
||||
Require(keys.size() == cases.size(), "the recompile cache key ignores CONSERVATIVE_Z_EXPORT");
|
||||
for (const auto shaderControl : {0x2020u, 0x4020u}) {
|
||||
const auto queue = depthExportQueue(shaderControl);
|
||||
static_cast<void>(AgcDriver::Graphics::DecodeState(queue));
|
||||
Require(AgcDriver::Graphics::DrawRejection(queue, false).empty(), "the precheck rejected DB_SHADER_CONTROL " + std::to_string(shaderControl));
|
||||
}
|
||||
expectFailure([] { static_cast<void>(depthExportRequest(0x6021u)); }, "CONSERVATIVE_Z_EXPORT");
|
||||
AgcDriver::Graphics::State state{};
|
||||
state.stages.path = AgcDriver::Graphics::ShaderPath::Vertex;
|
||||
ShaderRecompiler::RecompileResult vertex;
|
||||
vertex.spirv = makeModule({});
|
||||
ShaderRecompiler::RecompileResult pixel;
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{{ShaderRecompiler::ShaderStage::Vertex, &vertex, 0}, {ShaderRecompiler::ShaderStage::Fragment, &pixel, 0}}};
|
||||
const VkPhysicalDeviceSubgroupProperties subgroup{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES};
|
||||
for (const auto mode : {spv::ExecutionModeDepthLess, spv::ExecutionModeDepthGreater}) {
|
||||
pixel.spirv = makeModule({.fragment = true, .fragmentMode = mode});
|
||||
AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, false);
|
||||
}
|
||||
pixel.spirv = makeModule({.fragment = true, .fragmentMode = spv::ExecutionModeDepthUnchanged});
|
||||
expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, false); }, "unsupported fragment execution mode");
|
||||
}
|
||||
|
||||
void pixelParameterSlotTests() {
|
||||
using AgcDriver::Graphics::CompiledShader;
|
||||
const VkPhysicalDeviceSubgroupProperties subgroup{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES};
|
||||
|
|
@ -1922,6 +2000,7 @@ int main() {
|
|||
DepthClipTests();
|
||||
DepthStencilTests();
|
||||
DepthBoundsBiasTests();
|
||||
conservativeZExportTests();
|
||||
DisabledColorTests();
|
||||
CompactedExportTests();
|
||||
metadataPassTests();
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@ private:
|
|||
append(key, value.sampleMaskExportEnable);
|
||||
append(key, value.earlyZ);
|
||||
append(key, value.executeOnNoop);
|
||||
append(key, value.conservativeZExport);
|
||||
append(key, value.targetOutputMode);
|
||||
append(key, value.targetExportMapping);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -227,6 +227,14 @@ ProgramRole readProgramRole(Reader& reader) {
|
|||
return static_cast<ProgramRole>(value);
|
||||
}
|
||||
|
||||
ConservativeZExport readConservativeZExport(Reader& reader) {
|
||||
const auto value = reader.ReadU8();
|
||||
if (value > static_cast<std::uint8_t>(ConservativeZExport::GreaterThanZ)) {
|
||||
throw std::runtime_error("RequestSerializer: invalid ConservativeZExport value");
|
||||
}
|
||||
return static_cast<ConservativeZExport>(value);
|
||||
}
|
||||
|
||||
void writeShaderBinary(Writer& writer, const ShaderBinary& binary) {
|
||||
writer.WriteU8(static_cast<std::uint8_t>(binary.stage));
|
||||
writer.WriteU64(binary.codeAddress);
|
||||
|
|
@ -296,6 +304,7 @@ void writePixelInfo(Writer& writer, const ShaderPixelStageInfo& info) {
|
|||
writer.WriteBool(info.sampleMaskExportEnable);
|
||||
writer.WriteBool(info.earlyZ);
|
||||
writer.WriteBool(info.executeOnNoop);
|
||||
writer.WriteU8(static_cast<std::uint8_t>(info.conservativeZExport));
|
||||
for (const std::uint8_t value : info.targetOutputMode) {
|
||||
writer.WriteU8(value);
|
||||
}
|
||||
|
|
@ -328,6 +337,7 @@ ShaderPixelStageInfo readPixelInfo(Reader& reader, std::uint32_t version) {
|
|||
info.sampleMaskExportEnable = reader.ReadBool();
|
||||
info.earlyZ = reader.ReadBool();
|
||||
info.executeOnNoop = reader.ReadBool();
|
||||
if (version >= 7u) info.conservativeZExport = readConservativeZExport(reader);
|
||||
for (std::uint8_t& value : info.targetOutputMode) {
|
||||
value = reader.ReadU8();
|
||||
}
|
||||
|
|
@ -677,7 +687,7 @@ std::string RequestSerializer::Serialize(const RecompileRequest& request) const
|
|||
std::string buffer;
|
||||
Writer writer(buffer);
|
||||
writer.WriteU32(0x41505335u);
|
||||
writer.WriteU32(6u);
|
||||
writer.WriteU32(7u);
|
||||
writeShaderBinary(writer, request.shader);
|
||||
writeGuestContext(writer, request.context);
|
||||
writeSpirvTarget(writer, request.target);
|
||||
|
|
@ -701,7 +711,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 > 6u) throw std::runtime_error("unsupported recompile request serialization version");
|
||||
if (version < 1u || version > 7u) 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);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define CORE_SHADER_RECOMPILIER_OPTIMIZATION_INCLUDE_OPTIMIZATION_SHADERSTAGEINPUTINFO_HPP
|
||||
|
||||
#include "IntermediateRepresentation/IrMetadata.hpp"
|
||||
#include "Recompiler.hpp"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
|
@ -183,6 +184,7 @@ struct ShaderPixelInputInfo {
|
|||
bool psSampleShading = false;
|
||||
bool psEarlyZ = false;
|
||||
bool psExecuteOnNoop = false;
|
||||
ConservativeZExport psConservativeZExport = ConservativeZExport::AnyZ;
|
||||
ShaderStageRuntime stage;
|
||||
|
||||
bool HasPositionInput() const {
|
||||
|
|
|
|||
|
|
@ -98,6 +98,12 @@ constexpr std::uint32_t PixelInputVgpr(std::uint32_t inputAddr, PixelInput input
|
|||
return vgpr;
|
||||
}
|
||||
|
||||
enum class ConservativeZExport : std::uint8_t {
|
||||
AnyZ,
|
||||
LessThanZ,
|
||||
GreaterThanZ
|
||||
};
|
||||
|
||||
struct ShaderPixelStageInfo {
|
||||
std::uint32_t interpolatorCount;
|
||||
std::array<std::uint32_t, 32> interpolatorSettings;
|
||||
|
|
@ -119,6 +125,7 @@ struct ShaderPixelStageInfo {
|
|||
bool sampleMaskExportEnable;
|
||||
bool earlyZ;
|
||||
bool executeOnNoop;
|
||||
ConservativeZExport conservativeZExport;
|
||||
std::array<std::uint8_t, 8> targetOutputMode;
|
||||
std::array<std::uint8_t, 8> targetExportMapping;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -197,6 +197,12 @@ void DefineModule(SpirvEmitterState& state) {
|
|||
state.module.AddExecutionMode(state.mainFunc, spv::ExecutionModeOriginUpperLeft);
|
||||
if (state.depthVariable != 0) {
|
||||
state.module.AddExecutionMode(state.mainFunc, spv::ExecutionModeDepthReplacing);
|
||||
if (pixel.psConservativeZExport == ConservativeZExport::LessThanZ) {
|
||||
state.module.AddExecutionMode(state.mainFunc, spv::ExecutionModeDepthLess);
|
||||
}
|
||||
if (pixel.psConservativeZExport == ConservativeZExport::GreaterThanZ) {
|
||||
state.module.AddExecutionMode(state.mainFunc, spv::ExecutionModeDepthGreater);
|
||||
}
|
||||
}
|
||||
if (pixel.psEarlyZ && !pixel.psPixelKillEnable && !pixel.psDepthExportEnable && !pixel.psSampleMaskExportEnable) {
|
||||
state.module.AddExecutionMode(state.mainFunc, spv::ExecutionModeEarlyFragmentTests);
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ ShaderStageInputInfo BuildShaderStageInputInfo(ShaderStageKind stage, const Gues
|
|||
pixelStorage.psSampleShading = pixel.sampleShading;
|
||||
pixelStorage.psEarlyZ = pixel.earlyZ;
|
||||
pixelStorage.psExecuteOnNoop = pixel.executeOnNoop;
|
||||
pixelStorage.psConservativeZExport = pixel.conservativeZExport;
|
||||
ShaderStageInputInfo result;
|
||||
result.pixel = &pixelStorage;
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [v_rcp, v_rsq, v_sqrt, v_log, v_exp, v_sin and v_cos](../../core/shader/recompiler/Translation/src/FloatInstructions.cpp) (f32 and f16) use the driver's approximation for finite results, not the console's: the low bits can differ. Only NaN, invalid, zero and infinite results and the exact sin/cos zeros are chosen on the bits. v_sin_f32/v_cos_f32 with inputs beyond ±256 cycles were not compared with the console.
|
||||
- `--to-intel` does not lower RDPRU/MCOMMIT; the [matcher](../../core/relinker/codegen/src/x86/Amd64OnlyInstructionMatcher.cpp) fails the relink instead. SHA-1 and SHA-256 instructions with a RIP-relative operand fail the relink.
|
||||
- The length-changing path of the [instruction rewriter](../../core/relinker/codegen/src/x86/X64InstructionRewriter.cpp) is not used by the [converter](../../core/relinker/codegen/src/Amd64OnlyConverter.cpp): it does not adjust VEX/0F38/0F3A RIP-relative operands, data-to-code references (relocations, FDEs, jump tables) or segment sizes, so every substitution keeps the instruction length.
|
||||
- [DB_SHADER_CONTROL.CONSERVATIVE_Z_EXPORT](../../core/libs/prx/libSceAgcDriver/Graphics/src/ShaderInputState.cpp) (libSceAgcDriver) becomes `DepthLess`/`DepthGreater`. For a pixel shader that breaks the promise, the host driver decides whether its interpolated Z is depth-tested before shading; what the console does then was not measured.
|
||||
- The Linux placement of the `--to-intel` stubs in the [ELF patcher](../../core/relinker/elfpatcher/src/linux/LinuxElfPatcher.cpp) is covered only by a synthetic test.
|
||||
- The libc [SSE4a trap emulation](../../core/libs/prx/libc/src/specifics/windows/Sse4aEmulation.hpp) on Windows is superseded by `--to-intel` and remains only until the relinked title has been verified without it.
|
||||
- `--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.
|
||||
|
|
|
|||
Loading…
Reference in New Issue