feat(agc-driver): execute boolean SET_PREDICATION
SET_PREDICATION threw, and packets with the predicate header bit were rejected. The executor now keeps the queue's predication: operations 3 and 4 read a 64- or 32-bit value and bit 8 selects whether predicated packets run when it is non-zero or zero; operation 0 clears it. Before a predicated packet runs while predication is active, queued labels are recorded and the value is read, as for COND_EXEC; the packet is skipped when the predicate fails. Indirect buffers are inlined when a submission is copied, so a predicated INDIRECT_BUFFER call keeps its header in front of the inlined commands and records their end like a COND_EXEC range; the block runs or is skipped on one evaluation, as on the command processor. Query predication, a predicated COND_EXEC, predicated chains and flips inside predicated command buffers throw. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
823e01e3c1
commit
124be3e7e8
|
|
@ -88,6 +88,8 @@ bool AccessesMemory(std::uint32_t header);
|
|||
// synchronization): such a packet needs a pipeline barrier, not a device drain.
|
||||
bool UsesGpuCacheBarrier(std::span<const std::uint32_t> packet);
|
||||
bool IsTagMarker(std::span<const std::uint32_t> packet);
|
||||
inline bool Predicated(std::uint32_t header) { return !FillerPacket(header) && (header & 1u) != 0; }
|
||||
bool PredicationPasses(const QueueState& queue);
|
||||
bool WaitSatisfied(std::span<const std::uint32_t> packet);
|
||||
// WaitSatisfied for a polling loop: the caller has validated the address once, so the value is read
|
||||
// directly instead of through the checked guest memory path.
|
||||
|
|
|
|||
|
|
@ -167,6 +167,12 @@ inline Registers InitialContextRegisters() {
|
|||
return result;
|
||||
}
|
||||
|
||||
struct Predication {
|
||||
std::uint64_t address = 0;
|
||||
std::uint32_t operation = 0;
|
||||
bool executeWhenSet = false;
|
||||
};
|
||||
|
||||
struct QueueState {
|
||||
Registers shader;
|
||||
Registers context = InitialContextRegisters();
|
||||
|
|
@ -180,6 +186,7 @@ struct QueueState {
|
|||
std::uint32_t indexType = 0;
|
||||
std::uint32_t instanceCount = 1;
|
||||
std::vector<std::string> markers;
|
||||
Predication predication;
|
||||
|
||||
void ClearContext() {
|
||||
context = InitialContextRegisters();
|
||||
|
|
|
|||
|
|
@ -117,6 +117,17 @@ void Driver::execute(const Submission& submission) {
|
|||
|
||||
GuestMemory::SetCurrentPacket(header == FlipPacketHeader ? 0xffffu : opcode, submission.queue);
|
||||
CaptureTrace::Log("packet submission=%llu queue=%x offset=%zu header=%08x words=%zu", static_cast<unsigned long long>(submission.serial), submission.queue, cursor, header, packet.size());
|
||||
if (Pm4::Predicated(header) && queue.predication.operation != 0) {
|
||||
recordQueuedLabelsBeforeRead(submission.queue);
|
||||
if (!Pm4::PredicationPasses(queue)) {
|
||||
cursor = opcode == 0x3f ? submission.conditionalEnds.at(cursor) : nextCursor;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (opcode == 0x3f) {
|
||||
cursor = nextCursor;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto flushStart = profilePackets ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{};
|
||||
flushBetweenPackets(submission.queue, header, opcode == 0x49 || opcode == 0x37);
|
||||
|
|
|
|||
|
|
@ -50,6 +50,19 @@ bool Driver::copySegment(Submission& submission, const std::uint32_t* guest, std
|
|||
const bool chain = (guest[cursor + 3] & (1u << 20u)) != 0;
|
||||
require(!chain || guarded.empty(), "a chained INDIRECT_BUFFER inside a conditional execution range is not implemented");
|
||||
GuestMemory::CheckRange(target, targetWords * sizeof(std::uint32_t), alignof(std::uint32_t));
|
||||
if (Pm4::Predicated(header)) {
|
||||
require(!chain, "predicated command buffer chains are not implemented");
|
||||
const auto start = submission.commands.size();
|
||||
submission.commands.insert(submission.commands.end(), guest + cursor, guest + cursor + count);
|
||||
require(!copySegment(submission, target, targetWords, budget), "a REWIND inside a predicated command buffer is not implemented");
|
||||
for (auto inner = start + count; inner < submission.commands.size(); inner += Pm4::PacketWords(submission.commands[inner])) {
|
||||
const auto innerHeader = submission.commands[inner];
|
||||
require(innerHeader != FlipPacketHeader && innerHeader != RenderingWaitPacketHeader, "flips and rendering waits in predicated command buffers are not implemented");
|
||||
}
|
||||
submission.conditionalEnds.emplace(start, submission.commands.size());
|
||||
cursor += count;
|
||||
continue;
|
||||
}
|
||||
if (copySegment(submission, target, targetWords, budget)) {
|
||||
require(guarded.empty(), "a REWIND inside a conditional execution range is not implemented");
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ std::string_view UnsupportedReason(std::uint32_t header) {
|
|||
}
|
||||
}
|
||||
switch (opcode) {
|
||||
case 0x11: case 0x12: case 0x13: case 0x15: case 0x16: case 0x22: case 0x26: case 0x27:
|
||||
case 0x11: case 0x12: case 0x13: case 0x15: case 0x16: case 0x20: case 0x22: case 0x26: case 0x27:
|
||||
case 0x2a: case 0x2d: case 0x2f: case 0x35: case 0x37: case 0x40: case 0x42: case 0x46: case 0x50:
|
||||
case 0x58: case 0x63: case 0x64: case 0x69: case 0x76: case 0x79: case 0x7a:
|
||||
case 0x81: case 0x83: case 0x9f: return {};
|
||||
|
|
@ -175,7 +175,6 @@ std::string_view UnsupportedReason(std::uint32_t header) {
|
|||
return {};
|
||||
case 0x3a: case 0x8d:
|
||||
return "graphics draw, shader stages and guest render-target materialization are not implemented";
|
||||
case 0x20: return "GPU query predication is not implemented";
|
||||
case 0x33: case 0x3f: return "nested command buffers, branching and nested flip reservation are not implemented";
|
||||
case 0x3c: case 0x93: return {};
|
||||
case 0x39:
|
||||
|
|
@ -213,7 +212,7 @@ void Validate(std::span<const std::uint32_t> packet, std::uint32_t queue) {
|
|||
const auto size = [&](std::size_t count) { require(packet.size() == count, "invalid packet size"); };
|
||||
const auto graphics = [&] { require(queue == 0, "graphics packet in compute queue"); };
|
||||
const auto reason = UnsupportedReason(header);
|
||||
if (!reason.empty()) throw std::runtime_error(std::string(reason));
|
||||
if (!reason.empty() && !(opcode == 0x3f && Predicated(header))) throw std::runtime_error(std::string(reason));
|
||||
if (opcode == 0x10) {
|
||||
require((header & 3u) == 0, "unsupported NOP header flags");
|
||||
switch ((header >> 2u) & 0x3fu) {
|
||||
|
|
@ -239,11 +238,11 @@ void Validate(std::span<const std::uint32_t> packet, std::uint32_t queue) {
|
|||
return;
|
||||
}
|
||||
// Header bit 1 selects the compute shader type and bit 2 (register writes) resets the filter CAM;
|
||||
// neither changes what the packet writes. Predication (bit 0) is not implemented.
|
||||
// neither changes what the packet writes.
|
||||
const auto flags = header & 0xffu;
|
||||
const bool registerWrite = opcode == 0x69 || opcode == 0x76 || opcode == 0x79 || opcode == 0x7a;
|
||||
auto allowedFlags = opcode == 0x11 ? 2u : registerWrite ? 6u : opcode == 0x3c || opcode == 0x93 ? 2u : 0u;
|
||||
if (IsTagMarker(packet)) allowedFlags |= 1u;
|
||||
if (opcode != 0x20 && opcode != 0x22) allowedFlags |= 1u;
|
||||
if ((flags & ~allowedFlags) != 0) throw std::runtime_error("PM4 header flags 0x" + ToHex(flags) + " are not implemented");
|
||||
switch (opcode) {
|
||||
case 0x11:
|
||||
|
|
@ -252,6 +251,17 @@ void Validate(std::span<const std::uint32_t> packet, std::uint32_t queue) {
|
|||
if ((header & 2u) == 0) graphics();
|
||||
break;
|
||||
case 0x12: graphics(); size(2); require((packet[1] & ~0xfu) == 0, "unsupported CLEAR_STATE payload bits"); break;
|
||||
case 0x20: {
|
||||
graphics();
|
||||
size(4);
|
||||
const auto operation = (packet[1] >> 16u) & 7u;
|
||||
require(operation != 1 && operation != 2, "GPU query predication is not implemented");
|
||||
require(operation == 0 || operation == 3 || operation == 4, "invalid predication operation");
|
||||
require((packet[1] & ~0x71100u) == 0, "unsupported SET_PREDICATION bits");
|
||||
if (operation != 0) require(address(packet[2], packet[3]) != 0 && (packet[2] & 0xfu) == 0, "unaligned predication address");
|
||||
break;
|
||||
}
|
||||
case 0x3f: size(4); break;
|
||||
case 0x13: case 0x2f: graphics(); size(2); break;
|
||||
case 0x26: graphics(); size(3); break;
|
||||
case 0x27:
|
||||
|
|
@ -476,6 +486,16 @@ std::size_t ConditionalWords(std::span<const std::uint32_t> packet) {
|
|||
return packet[4] & ConditionalWordsMask;
|
||||
}
|
||||
|
||||
bool PredicationPasses(const QueueState& queue) {
|
||||
const auto& predication = queue.predication;
|
||||
if (predication.operation == 0) return true;
|
||||
const std::size_t bytes = predication.operation == 3 ? 8 : 4;
|
||||
std::uint64_t value = 0;
|
||||
const GuestMemory::ReadSiteScope site(GuestMemory::ReadSite::Wait);
|
||||
GuestMemory::Read(predication.address, std::as_writable_bytes(std::span(&value, 1)).first(bytes), bytes);
|
||||
return (value != 0) == predication.executeWhenSet;
|
||||
}
|
||||
|
||||
std::uint32_t ReadCondition(std::span<const std::uint32_t> packet) {
|
||||
require(packet.size() == 5 && ((packet[0] >> 8u) & 0xffu) == 0x22u, "expected COND_EXEC packet");
|
||||
std::uint32_t value = 0;
|
||||
|
|
@ -725,6 +745,12 @@ void Execute(std::span<const std::uint32_t> packet, QueueState& queue) {
|
|||
APS5_LOG_OUT_DEBUG("CLEAR_STATE targetMaskBefore=0x%x shaderMaskBefore=0x%x", queue.context.contains(0x8e) ? queue.context.at(0x8e) : 0u, queue.context.contains(0x8f) ? queue.context.at(0x8f) : 0u);
|
||||
queue.ClearContext(); return;
|
||||
case 0x13: queue.indexBufferSize = packet[1]; return;
|
||||
case 0x20: {
|
||||
const auto operation = (packet[1] >> 16u) & 7u;
|
||||
queue.predication = {operation == 0 ? 0 : address(packet[2], packet[3]), operation, (packet[1] & 0x100u) != 0};
|
||||
return;
|
||||
}
|
||||
case 0x3f: throw std::runtime_error(std::string(UnsupportedReason(packet[0])));
|
||||
case 0x26: queue.indexBase = address(packet[1], packet[2]); return;
|
||||
case 0x2a: queue.indexType = packet[1]; return;
|
||||
case 0x2f: queue.instanceCount = packet[1]; return;
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ void testAutoDraw() {
|
|||
}
|
||||
expectFailure([] { AgcDriver::Pm4::Validate(makePacket(0x2d, {3}), 0); }, "packet size");
|
||||
expectFailure([] { AgcDriver::Pm4::Validate(makePacket(0x2d, {3, 2, 0}), 0); }, "packet size");
|
||||
expectFailure([] { AgcDriver::Pm4::Validate(makePacket(0x2d, {3, 2}, 1), 0); }, "header flags");
|
||||
expectFailure([] { AgcDriver::Pm4::Validate(makePacket(0x2d, {3, 2}, 2), 0); }, "header flags");
|
||||
state.userConfig[0x24a] = std::numeric_limits<std::uint32_t>::max();
|
||||
check(AgcDriver::Pm4::ResolveDraw(makePacket(0x2d, {1, 2}), state).firstVertex == std::numeric_limits<std::uint32_t>::max(), "last vertex rejected");
|
||||
expectFailure([&] { AgcDriver::Pm4::ResolveDraw(makePacket(0x2d, {2, 2}), state); }, "vertex range overflow");
|
||||
|
|
@ -508,7 +508,7 @@ void testEventWrite() {
|
|||
for (const auto bit : {0x40u, 0x80u, 0x800u, 0x80000000u}) {
|
||||
expectFailure([&] { AgcDriver::Pm4::Validate(makePacket(0x46, {0x410u | bit}), 0); }, "reserved bits");
|
||||
}
|
||||
expectFailure([] { AgcDriver::Pm4::Validate(makePacket(0x46, {0x410}, 1), 0); }, "header flags");
|
||||
expectFailure([] { AgcDriver::Pm4::Validate(makePacket(0x46, {0x410}, 2), 0); }, "header flags");
|
||||
expectFailure([] { AgcDriver::Pm4::Validate(makePacket(0x46, {0x410, 0, 0}), 0); }, "packet size");
|
||||
expectFailure([] { AgcDriver::Pm4::Validate(makePacket(0x46, {0x13a, 0, 0}), 0); }, "event type 58");
|
||||
expectFailure([] { AgcDriver::Pm4::Validate(makePacket(0x46, {0x0d}), 0); }, "event type 13");
|
||||
|
|
@ -541,7 +541,7 @@ void testAcquireMem() {
|
|||
packet[index] = value;
|
||||
expectFailure([&] { AgcDriver::Pm4::Validate(packet, 0); }, reason);
|
||||
};
|
||||
invalidWord(0, captured[0] | 1u, "header flags");
|
||||
invalidWord(0, captured[0] | 2u, "header flags");
|
||||
invalidWord(1, 4, "control flags");
|
||||
invalidWord(1, 0x00800000, "control flags");
|
||||
invalidWord(3, 1, "above 40 bits");
|
||||
|
|
@ -554,6 +554,50 @@ void testAcquireMem() {
|
|||
expectFailure([] { AgcDriver::Pm4::Validate(makePacket(0x58, {0, 0, 0, 0, 0, 0, 0, 0}), 0); }, "packet size");
|
||||
}
|
||||
|
||||
void testPredication() {
|
||||
alignas(16) std::uint64_t flag[4] = {0, 0, 0, 0};
|
||||
auto* flag32 = reinterpret_cast<std::uint32_t*>(&flag[2]);
|
||||
const auto setPredication = [&](std::uint32_t operation, bool executeWhenSet, const void* address) {
|
||||
return makePacket(0x20, {(operation << 16u) | (executeWhenSet ? 0x100u : 0u) | 0x1000u, low(address), high(address)});
|
||||
};
|
||||
AgcDriver::QueueState state;
|
||||
check(AgcDriver::Pm4::PredicationPasses(state), "inactive predication must pass");
|
||||
execute(state, setPredication(3, true, &flag[0]));
|
||||
check(state.predication.operation == 3 && state.predication.executeWhenSet && state.predication.address == reinterpret_cast<std::uintptr_t>(&flag[0]), "BOOL64 predication state mismatch");
|
||||
check(!AgcDriver::Pm4::PredicationPasses(state), "zero BOOL64 value must skip draw-visible packets");
|
||||
flag[0] = 1ull << 40u;
|
||||
check(AgcDriver::Pm4::PredicationPasses(state), "upper BOOL64 bits must count");
|
||||
execute(state, setPredication(3, false, &flag[0]));
|
||||
check(!AgcDriver::Pm4::PredicationPasses(state), "non-zero BOOL64 value must skip draw-not-visible packets");
|
||||
flag[0] = 0;
|
||||
check(AgcDriver::Pm4::PredicationPasses(state), "zero BOOL64 value must run draw-not-visible packets");
|
||||
execute(state, setPredication(4, true, &flag[2]));
|
||||
flag[2] = 0xffffffff00000000ull;
|
||||
check(*flag32 == 0 && !AgcDriver::Pm4::PredicationPasses(state), "BOOL32 must read only 32 bits");
|
||||
*flag32 = 7;
|
||||
check(AgcDriver::Pm4::PredicationPasses(state), "non-zero BOOL32 value must run draw-visible packets");
|
||||
execute(state, setPredication(0, false, nullptr));
|
||||
check(state.predication.operation == 0 && AgcDriver::Pm4::PredicationPasses(state), "clear must end predication");
|
||||
|
||||
expectFailure([&] { AgcDriver::Pm4::Validate(setPredication(1, true, &flag[0]), 0); }, "query predication");
|
||||
expectFailure([&] { AgcDriver::Pm4::Validate(setPredication(2, true, &flag[0]), 0); }, "query predication");
|
||||
expectFailure([&] { AgcDriver::Pm4::Validate(setPredication(5, true, &flag[0]), 0); }, "invalid predication operation");
|
||||
expectFailure([&] { AgcDriver::Pm4::Validate(setPredication(3, true, &flag[0]), 0x20); }, "compute queue");
|
||||
expectFailure([&] { AgcDriver::Pm4::Validate(setPredication(3, true, nullptr), 0); }, "unaligned predication address");
|
||||
expectFailure([&] { AgcDriver::Pm4::Validate(setPredication(3, true, reinterpret_cast<const std::uint8_t*>(&flag[0]) + 8), 0); }, "unaligned predication address");
|
||||
auto continued = setPredication(3, true, &flag[0]);
|
||||
continued[1] |= 1u << 31u;
|
||||
expectFailure([&] { AgcDriver::Pm4::Validate(continued, 0); }, "SET_PREDICATION bits");
|
||||
expectFailure([&] { AgcDriver::Pm4::Validate(makePacket(0x20, {3u << 16u, low(&flag[0]), high(&flag[0])}, 1), 0); }, "header flags");
|
||||
|
||||
AgcDriver::Pm4::Validate(makePacket(0x2d, {3, 2}, 1), 0);
|
||||
AgcDriver::Pm4::Validate(makePacket(0x46, {0x410}, 1), 0);
|
||||
const auto call = makePacket(0x3f, {0x1000, 0, 0x0f200010}, 1);
|
||||
AgcDriver::Pm4::Validate(call, 0);
|
||||
expectFailure([&] { AgcDriver::Pm4::Validate(makePacket(0x3f, {0x1000, 0, 0x0f200010}), 0); }, "nested command buffers");
|
||||
expectFailure([&] { AgcDriver::Pm4::Execute(call, state); }, "nested command buffers");
|
||||
}
|
||||
|
||||
void testDriverSubmission() {
|
||||
std::array<std::uint32_t, 2> source{0x10, 73};
|
||||
std::array<std::uint32_t, 1> destination{};
|
||||
|
|
@ -693,6 +737,49 @@ void testConditionalSubmission() {
|
|||
check(results[13] == 0, "a rejected conditional submission executed a guarded packet");
|
||||
}
|
||||
|
||||
void testPredicatedSubmission() {
|
||||
alignas(16) std::uint64_t flag[2] = {0, 0};
|
||||
alignas(16) std::array<std::uint32_t, 4> written{};
|
||||
alignas(16) std::array<std::uint32_t, 1> nestedWritten{};
|
||||
const auto nested = makePacket(0x37, {0x100, low(nestedWritten.data()), high(nestedWritten.data()), 3});
|
||||
const auto call = makePacket(0x3f, {low(nested.data()), high(nested.data()), 0x0f200000u | static_cast<std::uint32_t>(nested.size())}, 1);
|
||||
const auto submit = [](std::vector<std::uint32_t>& commands) {
|
||||
Packet packet{commands.data(), static_cast<std::uint32_t>(commands.size()), 0, {}};
|
||||
check(sceAgcDriverSubmitDcb(&packet) == 0, "predicated submission failed");
|
||||
AgcDriverWaitIdle_nid_postfix();
|
||||
};
|
||||
std::vector<std::uint32_t> commands;
|
||||
for (const auto& packet : {
|
||||
makePacket(0x20, {0x31100, low(&flag[0]), high(&flag[0])}),
|
||||
makePacket(0x37, {0x100, low(&written[0]), high(&written[0]), 7}, 1),
|
||||
call,
|
||||
makePacket(0x20, {0, 0, 0}),
|
||||
makePacket(0x37, {0x100, low(&written[1]), high(&written[1]), 9}, 1)
|
||||
}) commands.insert(commands.end(), packet.begin(), packet.end());
|
||||
submit(commands);
|
||||
check(written[0] == 0, "packet with a false predicate was executed");
|
||||
check(nestedWritten[0] == 0, "command buffer with a false predicate was executed");
|
||||
check(written[1] == 9, "cleared predication skipped a packet");
|
||||
flag[0] = 1;
|
||||
commands.clear();
|
||||
for (const auto& packet : {
|
||||
makePacket(0x20, {0x31100, low(&flag[0]), high(&flag[0])}),
|
||||
makePacket(0x37, {0x100, low(&written[2]), high(&written[2]), 5}, 1),
|
||||
call,
|
||||
makePacket(0x20, {0x31000, low(&flag[0]), high(&flag[0])}),
|
||||
makePacket(0x37, {0x100, low(&written[3]), high(&written[3]), 6}, 1),
|
||||
makePacket(0x20, {0, 0, 0})
|
||||
}) commands.insert(commands.end(), packet.begin(), packet.end());
|
||||
submit(commands);
|
||||
check(written[2] == 5, "packet with a true predicate was skipped");
|
||||
check(nestedWritten[0] == 3, "command buffer with a true predicate was skipped");
|
||||
check(written[3] == 0, "draw-not-visible packet ran with a set value");
|
||||
auto chain = makePacket(0x3f, {low(nested.data()), high(nested.data()), 0x0f300000u | static_cast<std::uint32_t>(nested.size())}, 1);
|
||||
Packet chained{chain.data(), static_cast<std::uint32_t>(chain.size()), 0, {}};
|
||||
expectFailure([&] { sceAgcDriverSubmitDcb(&chained); }, "predicated command buffer chains");
|
||||
AgcDriverWaitIdle_nid_postfix();
|
||||
}
|
||||
|
||||
void testAsyncMemoryFailure() {
|
||||
auto commands = makePacket(0x37, {0x100, 0x1000, 0, 1});
|
||||
Packet packet{commands.data(), static_cast<std::uint32_t>(commands.size()), 0, {}};
|
||||
|
|
@ -728,7 +815,9 @@ int main(int argc, char** argv) {
|
|||
testConditionReadSynchronization();
|
||||
testEventWrite();
|
||||
testAcquireMem();
|
||||
testPredication();
|
||||
testDriverSubmission();
|
||||
testPredicatedSubmission();
|
||||
testConditionalSubmission();
|
||||
LibcRunShutdown_nid_postfix();
|
||||
std::puts("PM4 catalog, registers, state, memory, conditional execution and submission tests passed");
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ void testValidation() {
|
|||
expectFailure([&] { sceAgcDriverSubmitDcb(&packet); });
|
||||
commands[0] = 0xc001105c;
|
||||
expectFailure([&] { sceAgcDriverSubmitDcb(&packet); });
|
||||
commands[0] = 0xc0017601;
|
||||
commands[0] = 0xc0017608;
|
||||
expectFailure([&] { sceAgcDriverSubmitDcb(&packet); });
|
||||
commands[0] = 0xc0017600;
|
||||
commands[1] = 0x10000;
|
||||
|
|
@ -110,7 +110,7 @@ void testClearState() {
|
|||
words[0] = 0xc0011200;
|
||||
packet.dw_num = 3;
|
||||
expectFailure([&] { sceAgcDriverSubmitDcb(&packet); });
|
||||
words[0] = 0xc0001201;
|
||||
words[0] = 0xc0001202;
|
||||
packet.dw_num = 2;
|
||||
expectFailure([&] { sceAgcDriverSubmitDcb(&packet); });
|
||||
words[0] = 0xc0001200;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [sceFontGetRenderScaledKerning](../../core/libs/prx/libSceFont/src/Render.cpp) (libSceFont) - arguments, the cleared output and the bound renderer check taken from `cellFontGetRenderScaledKerning` in [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellFont.cpp) (PS3 predecessor of the library), not confirmed on a title. The kerning is the `sceFontGetKerning` one at the render scale
|
||||
- [sceFontGetPixelResolution](../../core/libs/prx/libSceFont/src/Library.cpp) (libSceFont) - signature taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), not confirmed on a title. The value is the one the library's driver reports (64 for FreeType); the argument checks and the cleared output follow `sceFontGetLibrary`
|
||||
- [sceAgcSetSubmitMode](../../core/libs/prx/libSceAgc/Misc/src/Suspend.cpp) (libSceAgc) - mode values unknown; only 0 is accepted
|
||||
- [SET_PREDICATION](../../core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp) (AGC driver) - boolean predication follows the AMD PM4 encoding [Mesa](https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/amd/common/sid.h) uses: operation 3 reads 64 bits and 4 reads 32 bits, and bit 8 runs predicated packets when the value is non-zero (clear: when it is zero); the bit 12 hint is ignored. The address must have its low 4 bits clear, as `sceAgcDcbSetPredication` writes it. Query predication (Z-pass, primitive count), a predicated COND_EXEC, predicated command buffer chains and flips in predicated command buffers throw
|
||||
- [sceAgcGetIsTrinityMode](../../core/libs/prx/libSceAgc/Misc/src/Platform.cpp) (libSceAgc) - signature from the only PPSA26344 call (a pointer to a one-byte flag, result ignored); a null pointer throws because its error code is unknown
|
||||
- [zARR5aCmkoY](../../core/libs/prx/libSceAgc/DcbFlow/src/Control.cpp) (libSceAgc) - unknown name, signature
|
||||
- [qj7QZpgr9Uw](../../core/libs/prx/libSceAgc/DcbState/src/ContextState.cpp) (libSceAgc) - unknown name
|
||||
|
|
|
|||
Loading…
Reference in New Issue