feat(libSceAgc): implement DCB atomic GDS operations and correct size accessor

This commit is contained in:
fogydes authored and Adrià Franch committed 2026-10-19 04:37:29 +02:00
1 parent ccbd40240f
commit cf994f1f60
5 files changed
+63 -14

No files matched your search

@@ -1,4 +1,12 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBFLOW_INCLUDE_CONTROL_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_DCBFLOW_INCLUDE_CONTROL_HPP
#include "SceTypes.hpp"
#include <cstdint>
extern "C" {
std::uint32_t* APS5_VABI sceAgcDcbAtomicGds(CommandBuffer* buf, std::uint32_t control, std::uint32_t mode, std::uint32_t gdsOp, std::uint32_t src, std::uint16_t offset, std::uint16_t index, std::uint32_t mask, std::uint32_t data, std::uint64_t compareData, std::uint64_t extraData);
std::uint32_t* APS5_VABI sceAgcDcbAtomicGds_0900(CommandBuffer* buf, std::uint32_t control, std::uint32_t mode, std::uint32_t gdsOp, std::uint32_t src, std::uint16_t offset, std::uint16_t index, std::uint32_t mask, std::uint32_t data, std::uint64_t compareData, std::uint64_t extraData);
}
#endif
@@ -2,7 +2,11 @@
#define CORE_LIBS_PRX_LIBSCEAGC_DCBFLOW_INCLUDE_MEMORY_HPP
#include "SceTypes.hpp"
#include <cstdint>
extern "C" std::uint32_t* APS5_VABI sceAgcDcbAcquireMem(CommandBuffer* buf, std::uint8_t engine, std::uint32_t cbDbOp, std::uint32_t gcrControl, const volatile void* base, std::uint64_t sizeBytes, std::uint32_t pollCycles);
extern "C" {
std::uint32_t* APS5_VABI sceAgcDcbAcquireMem(CommandBuffer* buf, std::uint8_t engine, std::uint32_t cbDbOp, std::uint32_t gcrControl, const volatile void* base, std::uint64_t sizeBytes, std::uint32_t pollCycles);
std::uint32_t APS5_VABI sceAgcDcbAtomicGdsGetSize();
}
#endif
@@ -12,10 +12,19 @@
extern "C" {
// unknown signature
void* APS5_VABI sceAgcDcbAtomicGds_0900(void) {
NotImplemented_nid_no_patch(__func__);
return nullptr;
std::uint32_t* APS5_VABI sceAgcDcbAtomicGds_0900(CommandBuffer* buf, std::uint32_t control, std::uint32_t mode, std::uint32_t gdsOp, std::uint32_t src, std::uint16_t offset, std::uint16_t index, std::uint32_t mask, std::uint32_t data, std::uint64_t compareData, std::uint64_t extraData) {
const auto operation = (control & 0xffu) | ((control & 0x200u) << 7u) |
((control & 0x20u) << 13u) | ((control & 0x40u) << 14u) |
((control & 0x20u) == 0 && mode == 1 ? 0x20000u : 0u);
return Agc::Command::Emit(buf, 0x1du, {
operation, ((gdsOp & 1u) << 8u) | (src & 0x3fu), offset, index,
mask & 0x00ff00ffu, data, static_cast<std::uint32_t>(compareData),
static_cast<std::uint32_t>(compareData >> 32u), static_cast<std::uint32_t>(extraData),
static_cast<std::uint32_t>(extraData >> 32u)}, __func__);
}
std::uint32_t* APS5_VABI sceAgcDcbAtomicGds(CommandBuffer* buf, std::uint32_t control, std::uint32_t mode, std::uint32_t gdsOp, std::uint32_t src, std::uint16_t offset, std::uint16_t index, std::uint32_t mask, std::uint32_t data, std::uint64_t compareData, std::uint64_t extraData) {
return sceAgcDcbAtomicGds_0900(buf, control, mode, gdsOp, src, offset, index, mask, data, compareData, extraData);
}
@@ -53,7 +53,7 @@ std::uint32_t APS5_VABI sceAgcDcbAtomicMemGetSize() {
}
std::uint32_t APS5_VABI sceAgcDcbAtomicGdsGetSize() {
return 36;
return 44;
}
std::uint32_t* APS5_VABI sceAgcDcbWriteData(CommandBuffer* buf, std::uint8_t dst, std::uint8_t cachePolicy, std::uint64_t address, const void* data, std::uint32_t numDwords, std::uint8_t increment, std::uint8_t writeConfirm) {
@@ -2,6 +2,7 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libc/include/Shutdown.hpp"
#include <algorithm>
#include <array>
#include <cstdint>
#include <cstdio>
@@ -11,6 +12,9 @@ extern "C" {
std::uint32_t APS5_VABI sceAgcDcbQueueEndOfShaderActionGetSize();
std::uint32_t APS5_VABI sceAgcAcbQueueEndOfShaderActionGetSize();
std::uint32_t* APS5_VABI sceAgcCbReleaseMem(CommandBuffer*, std::uint8_t, std::uint16_t, std::uint8_t, std::uint8_t, const volatile Label*, std::uint8_t, std::uint64_t, std::uint16_t, std::uint16_t, std::uint8_t, std::uint32_t);
std::uint32_t APS5_VABI sceAgcDcbAtomicGdsGetSize();
std::uint32_t* APS5_VABI sceAgcDcbAtomicGds_0900(CommandBuffer*, std::uint32_t, std::uint32_t, std::uint32_t, std::uint32_t, std::uint16_t, std::uint16_t, std::uint32_t, std::uint32_t, std::uint64_t, std::uint64_t);
std::uint32_t* APS5_VABI sceAgcDcbAtomicGds(CommandBuffer*, std::uint32_t, std::uint32_t, std::uint32_t, std::uint32_t, std::uint16_t, std::uint16_t, std::uint32_t, std::uint32_t, std::uint64_t, std::uint64_t);
}
namespace {
@@ -19,19 +23,43 @@ void check(bool condition, const char* message) {
if (!condition) throw std::runtime_error(message);
}
void testEndOfShaderActionSize() {
std::array<std::uint32_t, 16> words{};
CommandBuffer buffer{words.data(), words.data() + words.size(), words.data(), words.data() + words.size(), nullptr, nullptr, 0};
const auto* packet = sceAgcCbReleaseMem(&buffer, 0x2f, 0, 0, 0, nullptr, 0, 0, 0, 0, 0, 0);
const auto releaseBytes = static_cast<std::uint32_t>((buffer.cursor_up - packet) * sizeof(std::uint32_t));
check(((packet[0] >> 8u) & 0xffu) == 0x49u && releaseBytes == 32, "end-of-shader RELEASE_MEM size mismatch");
check(sceAgcDcbQueueEndOfShaderActionGetSize() == releaseBytes, "DCB end-of-shader action size differs from its RELEASE_MEM");
check(sceAgcDcbQueueEndOfShaderActionGetSize() == sceAgcAcbQueueEndOfShaderActionGetSize(), "DCB and ACB end-of-shader action sizes differ");
}
void testAtomicGds() {
constexpr std::uint32_t expectedBytes = 11u * sizeof(std::uint32_t);
check(sceAgcDcbAtomicGdsGetSize() == expectedBytes, "DCB atomic GDS size does not match 11-dword packet");
std::array<std::uint32_t, 32> words{};
CommandBuffer buffer{words.data(), words.data() + words.size(), words.data(), words.data() + words.size(), nullptr, nullptr, 0};
const auto* packet = sceAgcDcbAtomicGds_0900(&buffer, 0x261u, 1, 3, 7, 0x100, 0x200, 0x00ff00ffu, 0x12345678u, 0x1122334455667788ull, 0x99aabbccddeeff00ull);
check(packet != nullptr && packet == words.data(), "DCB atomic GDS returned unexpected packet pointer");
check(buffer.cursor_up == words.data() + 11, "DCB atomic GDS cursor advance does not match 11 dwords");
check(packet[0] == 0xc0091d00u, "DCB atomic GDS header opcode mismatch");
std::array<std::uint32_t, 32> unversionedWords{};
CommandBuffer unversionedBuffer{unversionedWords.data(), unversionedWords.data() + unversionedWords.size(), unversionedWords.data(), unversionedWords.data() + unversionedWords.size(), nullptr, nullptr, 0};
const auto* unversionedPacket = sceAgcDcbAtomicGds(&unversionedBuffer, 0x261u, 1, 3, 7, 0x100, 0x200, 0x00ff00ffu, 0x12345678u, 0x1122334455667788ull, 0x99aabbccddeeff00ull);
check(unversionedPacket != nullptr && unversionedPacket == unversionedWords.data(), "unversioned DCB atomic GDS returned unexpected packet pointer");
check(unversionedBuffer.cursor_up == unversionedWords.data() + 11, "unversioned DCB atomic GDS cursor advance does not match 11 dwords");
check(std::equal(words.begin(), words.begin() + 11, unversionedWords.begin()), "versioned and unversioned DCB atomic GDS emitted different packets");
}
}
int main() {
try {
std::array<std::uint32_t, 16> words{};
CommandBuffer buffer{words.data(), words.data() + words.size(), words.data(), words.data() + words.size(), nullptr, nullptr, 0};
const auto* packet = sceAgcCbReleaseMem(&buffer, 0x2f, 0, 0, 0, nullptr, 0, 0, 0, 0, 0, 0);
const auto releaseBytes = static_cast<std::uint32_t>((buffer.cursor_up - packet) * sizeof(std::uint32_t));
check(((packet[0] >> 8u) & 0xffu) == 0x49u && releaseBytes == 32, "end-of-shader RELEASE_MEM size mismatch");
check(sceAgcDcbQueueEndOfShaderActionGetSize() == releaseBytes, "DCB end-of-shader action size differs from its RELEASE_MEM");
check(sceAgcDcbQueueEndOfShaderActionGetSize() == sceAgcAcbQueueEndOfShaderActionGetSize(), "DCB and ACB end-of-shader action sizes differ");
testEndOfShaderActionSize();
testAtomicGds();
LibcRunShutdown_nid_postfix();
std::puts("AGC end-of-shader action size tests passed");
std::puts("AGC end-of-shader action and atomic GDS size tests passed");
return 0;
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());