#pragma once #include #include #include "PICA/gpu.hpp" #include "helpers.hpp" #include "kernel_types.hpp" #include "logger.hpp" #include "memory.hpp" #include "result/result.hpp" enum class GPUInterrupt : u8 { PSC0 = 0, // Memory fill completed PSC1 = 1, // ? VBlank0 = 2, // ? VBlank1 = 3, // ? PPF = 4, // Display transfer finished P3D = 5, // Command list processing finished DMA = 6 }; // More circular dependencies class Kernel; class GPUService { using Handle = HorizonHandle; Handle handle = KernelHandles::GPU; Memory& mem; GPU& gpu; Kernel& kernel; u32& currentPID; // Process ID of the current process u8* sharedMem; // Pointer to GSP shared memory // At any point in time only 1 process has privileges to use rendering functions // This is the PID of that process u32 privilegedProcess; std::optional interruptEvent; // Number of threads registered via RegisterInterruptRelayQueue u32 gspThreadCount = 0; MAKE_LOG_FUNCTION(log, gspGPULogger) void processCommandBuffer(); struct FramebufferInfo { u32 activeFb; u32 leftFramebufferVaddr; u32 rightFramebufferVaddr; u32 stride; u32 format; u32 displayFb; u32 attribute; }; static_assert(sizeof(FramebufferInfo) == 28, "GSP::GPU::FramebufferInfo has the wrong size"); struct FramebufferUpdate { u8 index; u8 dirtyFlag; u16 pad0; std::array framebufferInfo; u32 pad1; }; static_assert(sizeof(FramebufferUpdate) == 64, "GSP::GPU::FramebufferUpdate has the wrong size"); // Used for saving and restoring GPU state via ImportDisplayCaptureInfo struct CaptureInfo { u32 leftFramebuffer; // Left framebuffer VA u32 rightFramebuffer; // Right framebuffer VA (Top screen only) u32 format; u32 stride; }; static_assert(sizeof(CaptureInfo) == 16, "GSP::GPU::CaptureInfo has the wrong size"); // Service commands void acquireRight(u32 messagePointer); void flushDataCache(u32 messagePointer); void importDisplayCaptureInfo(u32 messagePointer); void readHwRegs(u32 messagePointer); void registerInterruptRelayQueue(u32 messagePointer); void releaseRight(u32 messagePointer); void restoreVramSysArea(u32 messagePointer); void saveVramSysArea(u32 messagePointer); void setAxiConfigQoSMode(u32 messagePointer); void setBufferSwap(u32 messagePointer); void setInternalPriorities(u32 messagePointer); void setLCDForceBlack(u32 messagePointer); void storeDataCache(u32 messagePointer); void triggerCmdReqQueue(u32 messagePointer); void writeHwRegs(u32 messagePointer); void writeHwRegsWithMask(u32 messagePointer); // GSP commands processed via TriggerCmdReqQueue void processCommandList(u32* cmd); void memoryFill(u32* cmd); void triggerDisplayTransfer(u32* cmd); void triggerDMARequest(u32* cmd); void triggerTextureCopy(u32* cmd); void flushCacheRegions(u32* cmd); void setBufferSwapImpl(u32 screen_id, const FramebufferInfo& info); // Get the framebuffer info in shared memory for a given screen FramebufferUpdate* getFramebufferInfo(int screen) { // TODO: Offset depends on GSP thread being triggered return reinterpret_cast(&sharedMem[0x200 + screen * sizeof(FramebufferUpdate)]); } FramebufferUpdate* getTopFramebufferInfo() { return getFramebufferInfo(0); } FramebufferUpdate* getBottomFramebufferInfo() { return getFramebufferInfo(1); } public: GPUService(Memory& mem, GPU& gpu, Kernel& kernel, u32& currentPID) : mem(mem), gpu(gpu), kernel(kernel), currentPID(currentPID) {} void reset(); void handleSyncRequest(u32 messagePointer); void requestInterrupt(GPUInterrupt type); void setSharedMem(u8* ptr) { sharedMem = ptr; if (ptr != nullptr) { // Zero-fill shared memory in case the process tries to read stale service data or vice versa std::memset(ptr, 0, 0x1000); } } };