fix(agc): accept bound copies of a registered shader header that differ only in user_data
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
ca412953ea
commit
cd1f76c831
|
|
@ -12,6 +12,7 @@
|
|||
#include "CompiledVariant.hpp"
|
||||
#include "Optimization/ResourceProgram.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <list>
|
||||
|
|
@ -161,6 +162,18 @@ void ShaderPreparationTransaction::Commit() {
|
|||
committed = true;
|
||||
}
|
||||
|
||||
bool SameHeader(const ShaderSnapshot& snapshot, const Shader* shader) {
|
||||
if (snapshot.headerAddress == reinterpret_cast<std::uintptr_t>(shader)) return true;
|
||||
if (snapshot.header.size() < sizeof(Shader)) return false;
|
||||
GuestMemory::CheckRange(shader, sizeof(Shader), 1);
|
||||
Shader used;
|
||||
Shader registered;
|
||||
std::memcpy(&used, static_cast<const void*>(shader), sizeof(Shader));
|
||||
std::memcpy(®istered, snapshot.header.data(), sizeof(Shader));
|
||||
used.user_data = registered.user_data;
|
||||
return std::memcmp(&used, ®istered, offsetof(Shader, num_sh_registers) + sizeof(Shader::num_sh_registers)) == 0;
|
||||
}
|
||||
|
||||
void PublishRegisteredShader(std::shared_ptr<ShaderRegistry>& registry, const std::shared_ptr<const ShaderSnapshot>& snapshot) {
|
||||
ShaderPreparationTransaction transaction;
|
||||
if (registry != nullptr) {
|
||||
|
|
@ -514,7 +527,7 @@ void Driver::ResolveGraphicsStagesAbi(std::span<const Shader* const> stages, std
|
|||
require(registry != nullptr && registry->contains(address), "graphics ABI refers to an unregistered shader");
|
||||
if (owner == nullptr) owner = registry->at(address);
|
||||
const auto& snapshot = *registry->at(address);
|
||||
require(snapshot.headerAddress == reinterpret_cast<std::uintptr_t>(shader), "graphics ABI refers to a replaced shader header");
|
||||
require(SameHeader(snapshot, shader), "graphics ABI refers to a replaced shader header");
|
||||
require(snapshot.registeredState != nullptr, "registered shader state is missing");
|
||||
const auto& registered = *snapshot.registeredState;
|
||||
for (const auto& [offset, value] : registered.shader) state.shader.insert_or_assign(offset, value);
|
||||
|
|
@ -579,7 +592,7 @@ void Driver::ResolveShaderAbi(const Shader* shader, std::span<const ShaderRegist
|
|||
const auto address = reinterpret_cast<std::uintptr_t>(const_cast<const void*>(shader->code));
|
||||
require(shaders != nullptr && shaders->contains(address), "static ABI refers to an unregistered shader");
|
||||
snapshot = shaders->at(address);
|
||||
require(snapshot->headerAddress == reinterpret_cast<std::uintptr_t>(shader), "static ABI refers to a replaced shader header");
|
||||
require(SameHeader(*snapshot, shader), "static ABI refers to a replaced shader header");
|
||||
}
|
||||
require(snapshot->registeredState != nullptr, "registered shader state is missing");
|
||||
QueueState state{};
|
||||
|
|
@ -641,7 +654,7 @@ void Driver::ResolveGraphicsAbi(const Shader* vertex, const Shader* pixel, std::
|
|||
const auto address = reinterpret_cast<std::uintptr_t>(const_cast<const void*>(shader->code));
|
||||
require(shaders != nullptr && shaders->contains(address), "rectangle ABI refers to an unregistered shader");
|
||||
const auto snapshot = shaders->at(address);
|
||||
require(snapshot->headerAddress == reinterpret_cast<std::uintptr_t>(shader), "rectangle ABI refers to a replaced shader header");
|
||||
require(SameHeader(*snapshot, shader), "rectangle ABI refers to a replaced shader header");
|
||||
return snapshot;
|
||||
};
|
||||
front = lookup(vertex);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
#include <spirv/unified1/spirv.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
|
@ -297,6 +299,15 @@ void Registration(bool indirect) {
|
|||
}));
|
||||
}
|
||||
for (auto& registration : registrations) registration.get();
|
||||
Shader copy = header.shader;
|
||||
copy.user_data = reinterpret_cast<ShaderUserData*>(©);
|
||||
AgcDriverResolveShaderAbi_nid_postfix(©, {}, {});
|
||||
alignas(Shader) std::array<std::byte, sizeof(Shader)> padded{};
|
||||
std::memcpy(padded.data(), ©, sizeof(Shader));
|
||||
padded.back() = std::byte{0x7d};
|
||||
AgcDriverResolveShaderAbi_nid_postfix(reinterpret_cast<Shader*>(padded.data()), {}, {});
|
||||
copy.target ^= 1u;
|
||||
ExpectFailure([&] { AgcDriverResolveShaderAbi_nid_postfix(©, {}, {}); }, "replaced shader header");
|
||||
header.registers[1].value |= 0x100u;
|
||||
ExpectFailure([&] { AgcDriverRegisterShader_nid_postfix(&header.shader); }, "invalid registered program address");
|
||||
header.registers[1].value &= 0xffu;
|
||||
|
|
|
|||
|
|
@ -245,6 +245,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [sceAgcDcbSetZPassPredicationEnableGetSize, sceAgcDcbSetPredicationDisableGetSize, sceAgcDcbSetBoolPredicationEnableGetSize](../../core/libs/prx/libSceAgc/DcbState/src/Predication.cpp) (libSceAgc) - the builders are inline in the SDK and not exported; the size is assumed to be that of the SET_PREDICATION packet `sceAgcDcbSetPredication` writes (16 bytes)
|
||||
- [Resource registration](../../core/libs/prx/libSceAgcDriver/Resource/src/Registration.cpp) (libSceAgcDriver) - registration always fails with `RESOURCE_REGISTRATION_UNAVAILABLE`, so the owner and resource queries, `sceAgcDriverSetResourceUserData`, `sceAgcDriverFindResourcesPublic` and `sceAgcDriverUnregisterAllResourcesForOwner` return the same error without reading their arguments. Their parameter lists follow the libSceGnmDriver counterparts and are unverified. `sceAgcDriverGetResourceRegistrationMaxNameLength` writes 0xfc and returns 0, as [prosper](https://github.com/mattias800/prosper) does from a title's registration configuration; the `uint32_t*` parameter matches the SDK wrapper `sce::Agc::ResourceRegistration::getMaxNameLength(unsigned int*)`. sharpemu writes 256 and KytyPS5 returns `RESOURCE_REGISTRATION_UNAVAILABLE`, so the value is unverified; a null output throws
|
||||
- [Shader registration](../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp) (libSceAgcDriver) - a shader header is accepted at any address: PPSA26344 passes headers 4 and 1 bytes past an 8-byte boundary; whether the console refuses an alignment is unknown
|
||||
- [Shader header copies](../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp) (libSceAgcDriver) - a header bound for a draw or dispatch at another address than the registered one is accepted when its fixed fields match the registered header except `user_data`; the registered header's metadata is used. PPSA21567 binds such a copy of a vertex shader header with `user_data` set to null. Whether the console reads the copy's `user_data` is unknown
|
||||
- [sceNgs2VoiceControl sampler filter](../../core/libs/prx/libSceNgs2.native/src/Voice.cpp) (libSceNgs2.native) - param `0x1000000a`, its layout and meaning follow KytyPS5 only: location 1 type 1 is an RBJ low-pass biquad at the system sample rate after resampling, scaled by `level`, a set `channel_mask` bit bypasses that channel, type 0 turns the filter off. Other locations and types throw. The voice keeps playing a filter tail after its last block until the history is zero, with outputs below 1e-20 flushed to zero (a KytyPS5 choice); the console's tail length is unknown
|
||||
- [sceNgs2PanInit / sceNgs2PanGetVolumeMatrix](../../core/libs/prx/libSceNgs2.native/src/Pan.cpp) (libSceNgs2.native) - follow the PS4 firmware 11.00 `libSceNgs2.sprx`, read and checked against its output for 2, 4, 5 and 7 speakers into 1, 2, 6 and 8 channels; not yet confirmed on the PS5 library. Speakers are taken in the order L R, L R Ls Rs, L R C Ls Rs or L R C Ls Rs Lb Rb and kept in a fixed circular order, not sorted; without angles the defaults are 90/270, 40/110/250/320, 0/40/110/250/320 and 0/40/110/150/210/250/320 degrees. As in that library, defaults converted to radians keep the wrap-around entry in degrees, so angles past the last speaker pan almost fully to it. Error codes are not known for the PS5, so invalid arguments throw, including a speaker angle equal to the unit angle, on which that library loops forever. Angles so large that adding or subtracting a turn leaves them unchanged throw for the same reason
|
||||
- [sceRtcGetDosTime, sceRtcSetDosTime](../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - bit layout from shadPS4 (PS4, the standard FAT date/time). A year outside 1980-2107 makes `GetDosTime` return `INVALID_YEAR` after writing 0 for an earlier one and `0xff9fbf7d` (2107-12-31 23:59:58, the largest encodable time) for a later one, and `SetDosTime` decodes the year as the unsigned 7-bit FAT field (1980-2107) without validating the fields, as the disassembly-derived [fpPS4](https://github.com/red-prig/fpPS4) reimplementation of the PS4 library does; not confirmed on a PS5 title, and shadPS4 instead masks out-of-range years into the packed value and sign-extends the 16-bit date
|
||||
|
|
|
|||
Loading…
Reference in New Issue