diff --git a/libctru/include/3ds/gfx.h b/libctru/include/3ds/gfx.h index 3fea7fe..bc52f09 100644 --- a/libctru/include/3ds/gfx.h +++ b/libctru/include/3ds/gfx.h @@ -1,8 +1,9 @@ /** * @file gfx.h - * @brief LCD Screens manipulation + * @brief Simple framebuffer API * - * This header provides functions to configure and manipulate the two screens, including double buffering and 3D activation. + * This API provides basic functionality needed to bring up framebuffers for both screens, + * as well as managing display mode (stereoscopic 3D) and double buffering. * It is mainly an abstraction over the gsp service. */ #pragma once @@ -16,36 +17,29 @@ /// Converts packed RGB8 to packed RGB565. #define RGB8_to_565(r,g,b) (((b)>>3)&0x1f)|((((g)>>2)&0x3f)<<5)|((((r)>>3)&0x1f)<<11) -/// Available screens. -typedef enum -{ - GFX_TOP = 0, ///< Top screen - GFX_BOTTOM = 1 ///< Bottom screen -}gfxScreen_t; +/// Screen IDs. +typedef enum { + GFX_TOP = GSP_SCREEN_TOP, ///< Top screen + GFX_BOTTOM = GSP_SCREEN_BOTTOM, ///< Bottom screen +} gfxScreen_t; /** - * @brief Side of top screen framebuffer. + * @brief Top screen framebuffer side. * - * This is to be used only when the 3D is enabled. - * Use only GFX_LEFT if this concerns the bottom screen or if 3D is disabled. + * This is only meaningful when stereoscopic 3D is enabled on the top screen. + * In any other case, use \ref GFX_LEFT. */ -typedef enum -{ - GFX_LEFT = 0, ///< Left eye framebuffer - GFX_RIGHT = 1,///< Right eye framebuffer -}gfx3dSide_t; +typedef enum { + GFX_LEFT = 0, ///< Left eye framebuffer + GFX_RIGHT = 1, ///< Right eye framebuffer +} gfx3dSide_t; - -///@name System related +///@name Initialization and deinitialization ///@{ /** * @brief Initializes the LCD framebuffers with default parameters - * - * By default libctru will configure the LCD framebuffers with the @ref GSP_BGR8_OES format in linear memory. - * This is the same as calling : @code gfxInit(GSP_BGR8_OES,GSP_BGR8_OES,false); @endcode - * - * @note You should always call @ref gfxExit once done to free the memory and services + * This is equivalent to calling: @code gfxInit(GSP_BGR8_OES,GSP_BGR8_OES,false); @endcode */ void gfxInitDefault(void); @@ -55,117 +49,103 @@ void gfxInitDefault(void); * @param bottomFormat The format of the bottom screen framebuffers. * @param vramBuffers Whether to allocate the framebuffers in VRAM. * - * This function will allocate the memory for the framebuffers and open a gsp service session. - * It will also bind the newly allocated framebuffers to the LCD screen and setup the VBlank event. + * This function allocates memory for the framebuffers in the specified memory region. + * Initially, stereoscopic 3D is disabled and double buffering is enabled. * - * The 3D stereoscopic display is will be disabled. - * - * @note Even if the double buffering is disabled, it will allocate two buffer per screen. - * @note You should always call @ref gfxExit once done to free the memory and services + * @note This function internally calls \ref gspInit. */ void gfxInit(GSPGPU_FramebufferFormat topFormat, GSPGPU_FramebufferFormat bottomFormat, bool vrambuffers); /** - * @brief Closes the gsp service and frees the framebuffers. - * - * Just call it when you're done. + * @brief Deinitializes and frees the LCD framebuffers. + * @note This function internally calls \ref gspExit. */ void gfxExit(void); + ///@} ///@name Control ///@{ + /** - * @brief Enables the 3D stereoscopic effect. - * @param enable Enables the 3D effect if true, disables it if false. + * @brief Enables or disables the 3D stereoscopic effect on the top screen. + * @param enable Pass true to enable, false to disable. + * @note Stereoscopic 3D is disabled by default. */ void gfxSet3D(bool enable); /** - * @brief Retrieves the status of the 3D stereoscopic effect. + * @brief Retrieves the status of the 3D stereoscopic effect on the top screen. * @return true if 3D enabled, false otherwise. */ bool gfxIs3D(void); /** - * @brief Changes the color format of a screen - * @param screen The screen of which format should be changed - * @param format One of the gsp pixel formats. + * @brief Changes the pixel format of a screen. + * @param screen Screen ID (see \ref gfxScreen_t) + * @param format Pixel format (see \ref GSPGPU_FramebufferFormat) + * @note If the currently allocated framebuffers are too small for the specified format, + * they are freed and new ones are reallocated. */ void gfxSetScreenFormat(gfxScreen_t screen, GSPGPU_FramebufferFormat format); /** - * @brief Gets a screen pixel format. - * @param screen Screen to get the pixel format of. - * @return the pixel format of the chosen screen set by libctru. + * @brief Retrieves the current pixel format of a screen. + * @param screen Screen ID (see \ref gfxScreen_t) + * @return Pixel format (see \ref GSPGPU_FramebufferFormat) */ GSPGPU_FramebufferFormat gfxGetScreenFormat(gfxScreen_t screen); /** - * @brief Sets whether to use libctru's double buffering - * @param screen Screen to toggle double buffering for. - * @param doubleBuffering Whether to use double buffering. - * - * libctru is by default using a double buffering scheme. - * If you do not want to swap one of the screen framebuffers when @ref gfxSwapBuffers or @ref gfxSwapBuffers is called, - * then you have to disable double buffering. - * - * It is however recommended to call @ref gfxSwapBuffers even if double buffering is disabled - * for both screens if you want to keep the gsp configuration up to date. + * @brief Enables or disables double buffering on a screen. + * @param screen Screen ID (see \ref gfxScreen_t) + * @param enable Pass true to enable, false to disable. + * @note Double buffering is enabled by default. */ -void gfxSetDoubleBuffering(gfxScreen_t screen, bool doubleBuffering); +void gfxSetDoubleBuffering(gfxScreen_t screen, bool enable); + +///@} + +///@name Rendering and presentation +///@{ /** - * @brief Flushes the current framebuffers + * @brief Retrieves the framebuffer of the specified screen to which graphics should be rendered. + * @param screen Screen ID (see \ref gfxScreen_t) + * @param side Framebuffer side (see \ref gfx3dSide_t) (pass \ref GFX_LEFT if not using stereoscopic 3D) + * @param width Pointer that will hold the width of the framebuffer in pixels. + * @param height Pointer that will hold the height of the framebuffer in pixels. + * @return A pointer to the current framebuffer of the chosen screen. * - * Use this if the data within your framebuffers changes a lot and that you want to make sure everything was updated correctly. - * This shouldn't be needed and has a significant overhead. + * Please remember that the returned pointer will change every frame if double buffering is enabled. + */ +u8* gfxGetFramebuffer(gfxScreen_t screen, gfx3dSide_t side, u16* width, u16* height); + +/** + * @brief Flushes the data cache for the current framebuffers. + * @warning This is **only used during software rendering**. Since this function has significant overhead, + * it is preferred to call this only once per frame, after all software rendering is completed. */ void gfxFlushBuffers(void); /** - * @brief Updates the configuration of the specified screen (swapping the buffers if double-buffering is enabled). - * @param scr Screen to configure. - * @param immediate Whether to apply the updated configuration immediately or let GSPGPU apply it after the next GX transfer completes. + * @brief Updates the configuration of the specified screen, swapping the buffers if double buffering is enabled. + * @param scr Screen ID (see \ref gfxScreen_t) + * @param immediate This parameter no longer has any effect and is thus ignored. + * @note Previously rendered content will be displayed on the screen after the next VBlank. + * @note This function is still useful even if double buffering is disabled, as it must be used to commit configuration changes. + * @warning Only call this once per screen per frame, otherwise graphical glitches will occur + * since this API does not implement triple buffering. */ void gfxConfigScreen(gfxScreen_t scr, bool immediate); /** - * @brief Swaps the buffers and sets the gsp state - * - * This is to be called to update the gsp state and swap the framebuffers. - * LCD rendering should start as soon as the gsp state is set. - * When using the GPU, call @ref gfxSwapBuffers instead. + * @brief Updates the configuration of both screens. + * @note This function is equivalent to calling \ref gfxConfigScreen for both screens. */ void gfxSwapBuffers(void); -/** - * @brief Swaps the framebuffers - * - * This is the version to be used with the GPU since the GPU will use the gsp shared memory, - * so the gsp state mustn't be set directly by the user. - */ +/// Same as \ref gfxSwapBuffers (formerly different). void gfxSwapBuffersGpu(void); ///@} - - -///@name Helper -///@{ -/** - * @brief Retrieves a framebuffer information. - * @param screen Screen to retrieve framebuffer information for. - * @param side Side of the screen to retrieve framebuffer information for. - * @param width Pointer that will hold the width of the framebuffer in pixels. - * @param height Pointer that will hold the height of the framebuffer in pixels. - * @return A pointer to the current framebuffer of the choosen screen. - * - * Please remember that the returned pointer will change after each call to gfxSwapBuffers if double buffering is enabled. - */ -u8* gfxGetFramebuffer(gfxScreen_t screen, gfx3dSide_t side, u16* width, u16* height); -///@} - -//global variables -extern u8* gfxTopLeftFramebuffers[2]; -extern u8* gfxTopRightFramebuffers[2]; -extern u8* gfxBottomFramebuffers[2]; diff --git a/libctru/include/3ds/gpu/gx.h b/libctru/include/3ds/gpu/gx.h index 7187b1c..da32c98 100644 --- a/libctru/include/3ds/gpu/gx.h +++ b/libctru/include/3ds/gpu/gx.h @@ -65,8 +65,6 @@ typedef enum /// Flushes the command list. #define GX_CMDLIST_FLUSH BIT(1) -extern u32* gxCmdBuf; ///< GX command buffer. - /// GX command entry typedef union { diff --git a/libctru/include/3ds/services/gspgpu.h b/libctru/include/3ds/services/gspgpu.h index 4bde714..b03de3a 100644 --- a/libctru/include/3ds/services/gspgpu.h +++ b/libctru/include/3ds/services/gspgpu.h @@ -4,7 +4,12 @@ */ #pragma once -#define GSPGPU_REBASE_REG(r) ((r)-0x1EB00000) +#define GSP_SCREEN_TOP 0 ///< ID of the top screen. +#define GSP_SCREEN_BOTTOM 1 ///< ID of the bottom screen. +#define GSP_SCREEN_WIDTH 240 ///< Width of the top/bottom screens. +#define GSP_SCREEN_HEIGHT_TOP 400 ///< Height of the top screen. +#define GSP_SCREEN_HEIGHT_TOP_2X 800 ///< Height of the top screen (2x). +#define GSP_SCREEN_HEIGHT_BOTTOM 320 ///< Height of the bottom screen. /// Framebuffer information. typedef struct @@ -84,6 +89,18 @@ Result gspInit(void); /// Exits GSPGPU. void gspExit(void); +/** + * @brief Presents a buffer to the specified screen. + * @param screen Screen ID (see \ref GSP_SCREEN_TOP and \ref GSP_SCREEN_BOTTOM) + * @param swap Specifies which set of framebuffer registers to configure and activate (0 or 1) + * @param fb_a Pointer to the framebuffer (in stereo mode: left eye) + * @param fb_b Pointer to the secondary framebuffer (only used in stereo mode for the right eye, otherwise pass the same as fb_a) + * @param stride Stride in bytes between scanlines + * @param mode Mode configuration to be written to LCD register + * @note The most recently presented buffer is processed and configured during the specified screen's next VBlank event. + */ +void gspPresentBuffer(unsigned screen, unsigned swap, const void* fb_a, const void* fb_b, unsigned stride, u32 mode); + /** * @brief Configures a callback to run when a GSPGPU event occurs. * @param id ID of the event. @@ -93,17 +110,6 @@ void gspExit(void); */ void gspSetEventCallback(GSPGPU_Event id, ThreadFunc cb, void* data, bool oneShot); -/** - * @brief Initializes the GSPGPU event handler. - * @param gspEvent Event handle to use. - * @param gspSharedMem GSP shared memory. - * @param gspThreadId ID of the GSP thread. - */ -Result gspInitEventHandler(Handle gspEvent, vu8* gspSharedMem, u8 gspThreadId); - -/// Exits the GSPGPU event handler. -void gspExitEventHandler(void); - /** * @brief Waits for a GSPGPU event to occur. * @param id ID of the event. @@ -145,10 +151,9 @@ GSPGPU_Event gspWaitForAnyEvent(void); /** * @brief Submits a GX command. - * @param sharedGspCmdBuf Command buffer to use. * @param gxCommand GX command to execute. */ -Result gspSubmitGxCommand(u32* sharedGspCmdBuf, u32 gxCommand[0x8]); +Result gspSubmitGxCommand(u32 gxCommand[0x8]); /** * @brief Acquires GPU rights. diff --git a/libctru/include/3ds/services/gsplcd.h b/libctru/include/3ds/services/gsplcd.h index f044da9..8e52d8e 100644 --- a/libctru/include/3ds/services/gsplcd.h +++ b/libctru/include/3ds/services/gsplcd.h @@ -3,13 +3,14 @@ * @brief GSPLCD service. */ #pragma once -#include <3ds/gfx.h> // For gfxScreen_t +#include <3ds/types.h> +#include <3ds/services/gspgpu.h> /// LCD screens. enum { - GSPLCD_SCREEN_TOP = BIT(GFX_TOP), ///< Top screen. - GSPLCD_SCREEN_BOTTOM = BIT(GFX_BOTTOM), ///< Bottom screen. + GSPLCD_SCREEN_TOP = BIT(GSP_SCREEN_TOP), ///< Top screen. + GSPLCD_SCREEN_BOTTOM = BIT(GSP_SCREEN_BOTTOM), ///< Bottom screen. GSPLCD_SCREEN_BOTH = GSPLCD_SCREEN_TOP | GSPLCD_SCREEN_BOTTOM, ///< Both screens. }; diff --git a/libctru/source/gfx.c b/libctru/source/gfx.c index 56f827b..5482959 100644 --- a/libctru/source/gfx.c +++ b/libctru/source/gfx.c @@ -1,367 +1,162 @@ -#include -#include #include <3ds/types.h> -#include <3ds/gfx.h> -#include <3ds/svc.h> -#include <3ds/synchronization.h> #include <3ds/allocator/linear.h> -#include <3ds/allocator/mappable.h> #include <3ds/allocator/vram.h> -#include <3ds/gpu/gx.h> +#include <3ds/services/gspgpu.h> +#include <3ds/gfx.h> -GSPGPU_FramebufferInfo topFramebufferInfo, bottomFramebufferInfo; +static u8* gfxTopFramebuffers[2]; +static u8* gfxBottomFramebuffers[2]; +static u32 gfxTopFramebufferMaxSize; +static u32 gfxBottomFramebufferMaxSize; +static GSPGPU_FramebufferFormat gfxFramebufferFormats[2]; -u8 gfxThreadID; -u8* gfxSharedMemory; +static bool gfxEnable3D, gfxIsVram; +static u8 gfxCurBuf[2]; +static u8 gfxIsDoubleBuf[2]; -u8* gfxTopLeftFramebuffers[2]; -u8* gfxTopRightFramebuffers[2]; -u8* gfxBottomFramebuffers[2]; -u32 gfxTopFramebufferMaxSize; -u32 gfxBottomFramebufferMaxSize; - -static bool enable3d; -static u8 currentBuffer[2]; -static int doubleBuf[2] = {1,1}; - -Handle gspEvent, gspSharedMemHandle; - -static GSPGPU_FramebufferFormat topFormat = GSP_BGR8_OES; -static GSPGPU_FramebufferFormat botFormat = GSP_BGR8_OES; - -static GSPGPU_FramebufferInfo* const framebufferInfoSt[] = { &topFramebufferInfo, &bottomFramebufferInfo }; - -static void (*screenFree)(void *) = NULL; -static void *(*screenAlloc)(size_t) = NULL; +static void (*screenFree)(void *); +static void *(*screenAlloc)(size_t); void gfxSet3D(bool enable) { - enable3d=enable; + gfxEnable3D = enable; } bool gfxIs3D(void) { - return enable3d; + return gfxEnable3D; } -void gfxSetScreenFormat(gfxScreen_t screen, GSPGPU_FramebufferFormat format) { - if (screenAlloc == NULL || screenFree == NULL) return; - if(screen==GFX_TOP) +void gfxSetScreenFormat(gfxScreen_t screen, GSPGPU_FramebufferFormat format) +{ + u32 reqSize = GSP_SCREEN_WIDTH * gspGetBytesPerPixel(format); + u8** framebuffers; + u32* maxSize; + + if (screen == GFX_TOP) { - u32 topSize = 400 * 240 * gspGetBytesPerPixel(format); - if (gfxTopFramebufferMaxSize < topSize) - { - screenFree(gfxTopLeftFramebuffers[0]); - screenFree(gfxTopLeftFramebuffers[1]); - screenFree(gfxTopRightFramebuffers[0]); - screenFree(gfxTopRightFramebuffers[1]); - gfxTopLeftFramebuffers[0]=screenAlloc(topSize); - gfxTopLeftFramebuffers[1]=screenAlloc(topSize); - gfxTopRightFramebuffers[0]=screenAlloc(topSize); - gfxTopRightFramebuffers[1]=screenAlloc(topSize); - gfxTopFramebufferMaxSize = topSize; - } - topFormat = format; - }else{ - u32 bottomSize = 320 * 240 * gspGetBytesPerPixel(format); - if (gfxBottomFramebufferMaxSize < bottomSize) - { - screenFree(gfxBottomFramebuffers[0]); - screenFree(gfxBottomFramebuffers[1]); - gfxBottomFramebuffers[0]=screenAlloc(bottomSize); - gfxBottomFramebuffers[1]=screenAlloc(bottomSize); - gfxBottomFramebufferMaxSize = bottomSize; - } - botFormat = format; + reqSize *= GSP_SCREEN_HEIGHT_TOP_2X; + framebuffers = gfxTopFramebuffers; + maxSize = &gfxTopFramebufferMaxSize; } + else // GFX_BOTTOM + { + reqSize *= GSP_SCREEN_HEIGHT_BOTTOM; + framebuffers = gfxBottomFramebuffers; + maxSize = &gfxBottomFramebufferMaxSize; + } + + if (*maxSize < reqSize) + { + if (framebuffers[0]) screenFree(framebuffers[0]); + if (framebuffers[1]) screenFree(framebuffers[1]); + framebuffers[0] = (u8*)screenAlloc(reqSize); + framebuffers[1] = (u8*)screenAlloc(reqSize); + *maxSize = reqSize; + } + + gfxFramebufferFormats[screen] = format; } -GSPGPU_FramebufferFormat gfxGetScreenFormat(gfxScreen_t screen) { - if(screen==GFX_TOP) - return topFormat; +GSPGPU_FramebufferFormat gfxGetScreenFormat(gfxScreen_t screen) +{ + return gfxFramebufferFormats[screen]; +} + +void gfxSetDoubleBuffering(gfxScreen_t screen, bool enable) +{ + gfxIsDoubleBuf[screen] = enable ? 1 : 0; // make sure they're the integer values '1' and '0' +} + +static void gfxPresentFramebuffer(gfxScreen_t screen, u8 id) +{ + u32 stride = GSP_SCREEN_WIDTH*gspGetBytesPerPixel(gfxFramebufferFormats[screen]); + u32 mode = gfxFramebufferFormats[screen]; + + const u8 *fb_a, *fb_b; + if (screen == GFX_TOP) + { + fb_a = gfxTopFramebuffers[id]; + if (gfxEnable3D) + { + mode |= BIT(5); + fb_b = fb_a + gfxTopFramebufferMaxSize/2; + } + else + { + mode |= BIT(6); + fb_b = fb_a; + } + } else - return botFormat; -} - -void gfxSetDoubleBuffering(gfxScreen_t screen, bool doubleBuffering) { - doubleBuf[screen] = doubleBuffering ? 1 : 0; // make sure they're the integer values '1' and '0' -} - -static void gfxSetFramebufferInfo(gfxScreen_t screen, u8 id) -{ - if(screen==GFX_TOP) { - topFramebufferInfo.active_framebuf=id; - topFramebufferInfo.framebuf0_vaddr=(u32*)gfxTopLeftFramebuffers[id]; - if(enable3d)topFramebufferInfo.framebuf1_vaddr=(u32*)gfxTopRightFramebuffers[id]; - else topFramebufferInfo.framebuf1_vaddr=topFramebufferInfo.framebuf0_vaddr; - topFramebufferInfo.framebuf_widthbytesize=240*gspGetBytesPerPixel(topFormat); - u8 bit5=(enable3d!=0); - topFramebufferInfo.format=((1)<<8)|((1^bit5)<<6)|((bit5)<<5)|topFormat; - topFramebufferInfo.framebuf_dispselect=id; - topFramebufferInfo.unk=0x00000000; - }else{ - bottomFramebufferInfo.active_framebuf=id; - bottomFramebufferInfo.framebuf0_vaddr=(u32*)gfxBottomFramebuffers[id]; - bottomFramebufferInfo.framebuf1_vaddr=0x00000000; - bottomFramebufferInfo.framebuf_widthbytesize=240*gspGetBytesPerPixel(botFormat); - bottomFramebufferInfo.format=botFormat; - bottomFramebufferInfo.framebuf_dispselect=id; - bottomFramebufferInfo.unk=0x00000000; + fb_a = gfxBottomFramebuffers[id]; + fb_b = fb_a; } -} -static void gfxWriteFramebufferInfo(gfxScreen_t screen) -{ - s32* framebufferInfoHeader=(s32*)(gfxSharedMemory+0x200+gfxThreadID*0x80); - if(screen==GFX_BOTTOM)framebufferInfoHeader+=0x10; - GSPGPU_FramebufferInfo* framebufferInfo=(GSPGPU_FramebufferInfo*)&framebufferInfoHeader[1]; + if (!gfxIsVram) + mode |= 1<<8; + else + mode |= 3<<8; - u8 pos = 1 - *(u8*)framebufferInfoHeader; - framebufferInfo[pos]=*framebufferInfoSt[screen]; - __dsb(); - - union - { - s32 header; - struct { u8 swap, update; }; - } info; - - do - { - info.header = __ldrex(framebufferInfoHeader); - info.swap = pos; - info.update = 1; - } while (__strex(framebufferInfoHeader, info.header)); -} - -static inline void gfxWriteGxReg(u32 offset, u32 data) -{ - GSPGPU_WriteHWRegs(0x400000 + offset, &data, 4); -} -static inline void gfxWriteGxRegMasked(u32 offset, u32 data, u32 mask) -{ - GSPGPU_WriteHWRegsWithMask(0x400000 + offset, &data, 4, &mask, 4); -} - -static void gfxGxHwInit(void) -{ - // SDK apps have this exact sequence (except for GPUREG_START_DRAW_FUNC0) - - // Some GPU-internal init registers - gfxWriteGxReg(0x1000, 0); - gfxWriteGxReg(0x1080, 0x12345678); - gfxWriteGxReg(0x10C0, 0xFFFFFFF0); - gfxWriteGxReg(0x10D0, 1); - // Ensure GPUREG_START_DRAW_FUNC0 starts off in configuration mode - gfxWriteGxReg(0x1914, 1); - - // Top screen LCD configuration, see https://www.3dbrew.org/wiki/GPU/External_Registers#LCD_Source_Framebuffer_Setup - - // Top screen sync registers: - gfxWriteGxReg(0x0400, 0x1C2); - gfxWriteGxReg(0x0404, 0xD1); - gfxWriteGxReg(0x0408, 0x1C1); - gfxWriteGxReg(0x040C, 0x1C1); - gfxWriteGxReg(0x0410, 0); - gfxWriteGxReg(0x0414, 0xCF); - gfxWriteGxReg(0x0418, 0xD1); - gfxWriteGxReg(0x041C, (0x1C5 << 16) | 0x1C1); - gfxWriteGxReg(0x0420, 0x10000); - gfxWriteGxReg(0x0424, 0x19D); - gfxWriteGxReg(0x0428, 2); - gfxWriteGxReg(0x042C, 0x192); - gfxWriteGxReg(0x0430, 0x192); - gfxWriteGxReg(0x0434, 0x192); - gfxWriteGxReg(0x0438, 1); - gfxWriteGxReg(0x043C, 2); - gfxWriteGxReg(0x0440, (0x196 << 16) | 0x192); - gfxWriteGxReg(0x0444, 0); - gfxWriteGxReg(0x0448, 0); - - // Top screen fb geometry - gfxWriteGxReg(0x045C, (400 << 16) | 240); // dimensions - gfxWriteGxReg(0x0460, (0x1C1 << 16) | 0xD1); - gfxWriteGxReg(0x0464, (0x192 << 16) | 2); - - // Top screen framebuffer format (initial) - gfxWriteGxReg(0x0470, 0x80340); - - // Top screen unknown reg @ 0x9C - gfxWriteGxReg(0x049C, 0); - - // Bottom screen LCD configuration - - // Bottom screen sync registers: - gfxWriteGxReg(0x0500, 0x1C2); - gfxWriteGxReg(0x0504, 0xD1); - gfxWriteGxReg(0x0508, 0x1C1); - gfxWriteGxReg(0x050C, 0x1C1); - gfxWriteGxReg(0x0510, 0xCD); - gfxWriteGxReg(0x0514, 0xCF); - gfxWriteGxReg(0x0518, 0xD1); - gfxWriteGxReg(0x051C, (0x1C5 << 16) | 0x1C1); - gfxWriteGxReg(0x0520, 0x10000); - gfxWriteGxReg(0x0524, 0x19D); - gfxWriteGxReg(0x0528, 0x52); - gfxWriteGxReg(0x052C, 0x192); - gfxWriteGxReg(0x0530, 0x192); - gfxWriteGxReg(0x0534, 0x4F); - gfxWriteGxReg(0x0538, 0x50); - gfxWriteGxReg(0x053C, 0x52); - gfxWriteGxReg(0x0540, (0x198 << 16) | 0x194); - gfxWriteGxReg(0x0544, 0); - gfxWriteGxReg(0x0548, 0x11); - - // Bottom screen fb geometry - gfxWriteGxReg(0x055C, (320 << 16) | 240); // dimensions - gfxWriteGxReg(0x0560, (0x1C1 << 16) | 0xD1); - gfxWriteGxReg(0x0564, (0x192 << 16) | 0x52); - - // Bottom screen framebuffer format (initial) - gfxWriteGxReg(0x0570, 0x80300); - - // Bottom screen unknown reg @ 0x9C - gfxWriteGxReg(0x059C, 0); - - // Initial, blank framebuffer (top left A/B, bottom A/B, top right A/B) - gfxWriteGxReg(0x0468, 0x18300000); - gfxWriteGxReg(0x046C, 0x18300000); - gfxWriteGxReg(0x0568, 0x18300000); - gfxWriteGxReg(0x056C, 0x18300000); - gfxWriteGxReg(0x0494, 0x18300000); - gfxWriteGxReg(0x0498, 0x18300000); - - // Framebuffer select: A - gfxWriteGxReg(0x0478, 1); - gfxWriteGxReg(0x0578, 1); - - // Clear DMA transfer (PPF) "transfer finished" bit - gfxWriteGxRegMasked(0x0C18, 0, 0xFF00); - - // GX_GPU_CLK |= 0x70000 (value is 0x100 when gsp starts, enough to at least display framebuffers & have memory fill work) - // This enables the clock to some GPU components - gfxWriteGxReg(0x0004, 0x70100); - - // Clear Memory Fill (PSC0 and PSC1) "busy" and "finished" bits - gfxWriteGxRegMasked(0x001C, 0, 0xFF); - gfxWriteGxRegMasked(0x002C, 0, 0xFF); - - // More init registers - gfxWriteGxReg(0x0050, 0x22221200); - gfxWriteGxRegMasked(0x0054, 0xFF2, 0xFFFF); - - // Enable some LCD clocks (?) (unsure) - gfxWriteGxReg(0x0474, 0x10501); - gfxWriteGxReg(0x0574, 0x10501); + gspPresentBuffer(screen, id, fb_a, fb_b, stride, mode); } void gfxInit(GSPGPU_FramebufferFormat topFormat, GSPGPU_FramebufferFormat bottomFormat, bool vrambuffers) { if (vrambuffers) { - screenAlloc=vramAlloc; - screenFree=vramFree; - - } else { - - screenAlloc=linearAlloc; - screenFree=linearFree; + screenAlloc = vramAlloc; + screenFree = vramFree; + gfxIsVram = true; + } + else + { + screenAlloc = linearAlloc; + screenFree = linearFree; + gfxIsVram = false; } + // Initialize GSP gspInit(); - gfxSharedMemory=(u8*)mappableAlloc(0x1000); + // Initialize configuration + gfxSet3D(false); + gfxSetScreenFormat(GFX_TOP, topFormat); + gfxSetScreenFormat(GFX_BOTTOM, bottomFormat); + gfxSetDoubleBuffering(GFX_TOP, true); + gfxSetDoubleBuffering(GFX_BOTTOM, true); - GSPGPU_AcquireRight(0); + // Present the framebuffers + gfxCurBuf[0] = gfxCurBuf[1] = 0; + gfxPresentFramebuffer(GFX_TOP, 0); + gfxPresentFramebuffer(GFX_BOTTOM, 0); - //setup our gsp shared mem section - svcCreateEvent(&gspEvent, RESET_ONESHOT); - - // The 0x2A07 success code is returned only for the very first call to that function (globally) - if (GSPGPU_RegisterInterruptRelayQueue(gspEvent, 0x1, &gspSharedMemHandle, &gfxThreadID) == 0x2A07) - gfxGxHwInit(); - - svcMapMemoryBlock(gspSharedMemHandle, (u32)gfxSharedMemory, 0x3, 0x10000000); - - // default gspHeap configuration : - // topleft1 0x00000000-0x00046500 - // topleft2 0x00046500-0x0008CA00 - // bottom1 0x0008CA00-0x000C4E00 - // bottom2 0x000C4E00-0x000FD200 - // if 3d enabled : - // topright1 0x000FD200-0x00143700 - // topright2 0x00143700-0x00189C00 - u32 topSize = 400 * 240 * gspGetBytesPerPixel(topFormat); - u32 bottomSize = 320 * 240 * gspGetBytesPerPixel(bottomFormat); - - gfxTopLeftFramebuffers[0]=screenAlloc(topSize); - gfxTopLeftFramebuffers[1]=screenAlloc(topSize); - gfxBottomFramebuffers[0]=screenAlloc(bottomSize); - gfxBottomFramebuffers[1]=screenAlloc(bottomSize); - gfxTopRightFramebuffers[0]=screenAlloc(topSize); - gfxTopRightFramebuffers[1]=screenAlloc(topSize); - gfxTopFramebufferMaxSize = topSize; - gfxBottomFramebufferMaxSize = bottomSize; - - enable3d=false; - - //set requested modes - gfxSetScreenFormat(GFX_TOP,topFormat); - gfxSetScreenFormat(GFX_BOTTOM,bottomFormat); - - //initialize framebuffer info structures - gfxSetFramebufferInfo(GFX_TOP, 0); - gfxSetFramebufferInfo(GFX_BOTTOM, 0); - - //GSP shared mem : 0x2779F000 - gxCmdBuf=(u32*)(gfxSharedMemory+0x800+gfxThreadID*0x200); - - currentBuffer[0]=0; - currentBuffer[1]=0; - - // Initialize event handler and wait for VBlank - gspInitEventHandler(gspEvent, (vu8*) gfxSharedMemory, gfxThreadID); + // Wait for VBlank and turn the LCD on gspWaitForVBlank(); - GSPGPU_SetLcdForceBlack(0x0); } -void gfxInitDefault(void) { +void gfxInitDefault(void) +{ gfxInit(GSP_BGR8_OES,GSP_BGR8_OES,false); } void gfxExit(void) { - if (screenFree == NULL) return; - - // Exit event handler - gspExitEventHandler(); + if (screenFree == NULL) + return; // Free framebuffers - screenFree(gfxTopRightFramebuffers[1]); - screenFree(gfxTopRightFramebuffers[0]); - screenFree(gfxBottomFramebuffers[1]); + screenFree(gfxTopFramebuffers[0]); + screenFree(gfxTopFramebuffers[1]); screenFree(gfxBottomFramebuffers[0]); - screenFree(gfxTopLeftFramebuffers[1]); - screenFree(gfxTopLeftFramebuffers[0]); - - //unmap GSP shared mem - svcUnmapMemoryBlock(gspSharedMemHandle, (u32)gfxSharedMemory); - - GSPGPU_UnregisterInterruptRelayQueue(); - - svcCloseHandle(gspSharedMemHandle); - if(gfxSharedMemory != NULL) - { - mappableFree(gfxSharedMemory); - gfxSharedMemory = NULL; - } - - svcCloseHandle(gspEvent); - - GSPGPU_ReleaseRight(); + screenFree(gfxBottomFramebuffers[1]); + gfxTopFramebuffers[0] = gfxTopFramebuffers[1] = NULL; + gfxBottomFramebuffers[0] = gfxBottomFramebuffers[1] = NULL; + gfxTopFramebufferMaxSize = gfxBottomFramebufferMaxSize = 0; + // Deinitialize GSP gspExit(); screenFree = NULL; @@ -369,36 +164,47 @@ void gfxExit(void) u8* gfxGetFramebuffer(gfxScreen_t screen, gfx3dSide_t side, u16* width, u16* height) { - if(width)*width=240; + unsigned id = gfxCurBuf[screen]^gfxIsDoubleBuf[screen]; + unsigned scr_width = GSP_SCREEN_WIDTH; + unsigned scr_height; + u8* fb; - if(screen==GFX_TOP) + if (screen == GFX_TOP) { - if(height)*height=400; - return (side==GFX_LEFT || !enable3d)?(gfxTopLeftFramebuffers[currentBuffer[0]^doubleBuf[0]]):(gfxTopRightFramebuffers[currentBuffer[0]^doubleBuf[0]]); - }else{ - if(height)*height=320; - return gfxBottomFramebuffers[currentBuffer[1]^doubleBuf[1]]; + fb = gfxTopFramebuffers[id]; + scr_height = GSP_SCREEN_HEIGHT_TOP; + if (gfxEnable3D && side != GFX_LEFT) + fb += gfxTopFramebufferMaxSize/2; } + else // GFX_BOTTOM + { + fb = gfxBottomFramebuffers[id]; + scr_height = GSP_SCREEN_HEIGHT_BOTTOM; + } + + if (width) + *width = scr_width; + if (height) + *height = scr_height; + + return fb; } void gfxFlushBuffers(void) { - u32 topSize = 400 * 240 * gspGetBytesPerPixel(gfxGetScreenFormat(GFX_TOP)); - u32 bottomSize = 320 * 240 * gspGetBytesPerPixel(gfxGetScreenFormat(GFX_BOTTOM)); + const u32 baseSize = GSP_SCREEN_WIDTH * gspGetBytesPerPixel(gfxGetScreenFormat(GFX_TOP)); + const u32 topSize = GSP_SCREEN_HEIGHT_TOP * baseSize; + const u32 bottomSize = GSP_SCREEN_HEIGHT_BOTTOM * baseSize; GSPGPU_FlushDataCache(gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL), topSize); - if(enable3d)GSPGPU_FlushDataCache(gfxGetFramebuffer(GFX_TOP, GFX_RIGHT, NULL, NULL), topSize); + if(gfxEnable3D)GSPGPU_FlushDataCache(gfxGetFramebuffer(GFX_TOP, GFX_RIGHT, NULL, NULL), topSize); GSPGPU_FlushDataCache(gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), bottomSize); } void gfxConfigScreen(gfxScreen_t scr, bool immediate) { - currentBuffer[scr]^=doubleBuf[scr]; - gfxSetFramebufferInfo(scr, currentBuffer[scr]); - if (immediate) - GSPGPU_SetBufferSwap(scr, framebufferInfoSt[scr]); - else - gfxWriteFramebufferInfo(scr); + gfxCurBuf[scr] ^= gfxIsDoubleBuf[scr]; + gfxPresentFramebuffer(scr, gfxCurBuf[scr]); } void gfxSwapBuffers(void) diff --git a/libctru/source/gpu/gx.c b/libctru/source/gpu/gx.c index 7e836e0..26e1ff4 100644 --- a/libctru/source/gpu/gx.c +++ b/libctru/source/gpu/gx.c @@ -9,7 +9,6 @@ #include <3ds/gpu/gx.h> #include <3ds/services/gspgpu.h> -u32* gxCmdBuf; static gxCmdQueue_s* boundQueue; // Dummy version to avoid linking in gxqueue.c if not actually used @@ -30,7 +29,7 @@ static Result submitGxCommand(u32 gxCommand[0x8]) return 0; } else - return gspSubmitGxCommand(gxCmdBuf, gxCommand); + return gspSubmitGxCommand(gxCommand); } Result GX_RequestDma(u32* src, u32* dst, u32 length) @@ -115,5 +114,5 @@ Result GX_FlushCacheRegions(u32* buf0a, u32 buf0s, u32* buf1a, u32 buf1s, u32* b gxCommand[6]=(u32)buf2s; //buf2 size gxCommand[7]=0x0; - return gspSubmitGxCommand(gxCmdBuf, gxCommand); + return gspSubmitGxCommand(gxCommand); } diff --git a/libctru/source/gpu/gxqueue.c b/libctru/source/gpu/gxqueue.c index cc79f83..89e1a9d 100644 --- a/libctru/source/gpu/gxqueue.c +++ b/libctru/source/gpu/gxqueue.c @@ -8,7 +8,6 @@ #define MAX_PARALLEL_CMDS 3 -extern u32* gxCmdBuf; static gxCmdQueue_s* curQueue; static bool isActive, isRunning, shouldStop; static LightLock queueLock = 1; @@ -21,7 +20,7 @@ static void gxCmdQueueDoCommands(void) while (curQueue->curEntry < curQueue->numEntries && batchSize--) { gxCmdEntry_s* entry = &curQueue->entries[curQueue->curEntry++]; - gspSubmitGxCommand(gxCmdBuf, entry->data); + gspSubmitGxCommand(entry->data); } } diff --git a/libctru/source/services/gspgpu.c b/libctru/source/services/gspgpu.c index 7e42894..30dd1c4 100644 --- a/libctru/source/services/gspgpu.c +++ b/libctru/source/services/gspgpu.c @@ -5,44 +5,251 @@ #include <3ds/svc.h> #include <3ds/srv.h> #include <3ds/synchronization.h> +#include <3ds/allocator/mappable.h> #include <3ds/services/gspgpu.h> #include <3ds/ipc.h> #include <3ds/thread.h> #define GSP_EVENT_STACK_SIZE 0x1000 -Handle gspGpuHandle; +static Handle gspGpuHandle; static int gspRefCount; -static s32 gspLastEvent = -1; +static Handle gspSharedMemHandle; +static void* gspSharedMem; +static u8 gspThreadId; + +static Handle gspEvent; +static Thread gspEventThread; +static volatile bool gspRunEvents; + +static s32 gspLastEvent; static LightEvent gspEvents[GSPGPU_EVENT_MAX]; -static vu32 gspEventCounts[GSPGPU_EVENT_MAX]; static ThreadFunc gspEventCb[GSPGPU_EVENT_MAX]; static void* gspEventCbData[GSPGPU_EVENT_MAX]; static bool gspEventCbOneShot[GSPGPU_EVENT_MAX]; -static volatile bool gspRunEvents; -static Thread gspEventThread; - -static Handle gspEvent; -static vu8* gspEventData; static void gspEventThreadMain(void *arg); +static inline void gspWriteGxReg(u32 offset, u32 data) +{ + GSPGPU_WriteHWRegs(0x400000 + offset, &data, 4); +} + +static inline void gspWriteGxRegMasked(u32 offset, u32 data, u32 mask) +{ + GSPGPU_WriteHWRegsWithMask(0x400000 + offset, &data, 4, &mask, 4); +} + +// Hardware initialization for first-time GSP users (matching official software). +static void gspHardwareInit(void) +{ + // Some GPU-internal init registers + gspWriteGxReg(0x1000, 0); + gspWriteGxReg(0x1080, 0x12345678); + gspWriteGxReg(0x10C0, 0xFFFFFFF0); + gspWriteGxReg(0x10D0, 1); + gspWriteGxReg(0x1914, 1); // homebrew addition: make sure GPUREG_START_DRAW_FUNC0 starts off in configuration mode + + // Top screen LCD configuration, see https://www.3dbrew.org/wiki/GPU/External_Registers#LCD_Source_Framebuffer_Setup + + // Top screen sync registers: + gspWriteGxReg(0x0400, 0x1C2); + gspWriteGxReg(0x0404, 0xD1); + gspWriteGxReg(0x0408, 0x1C1); + gspWriteGxReg(0x040C, 0x1C1); + gspWriteGxReg(0x0410, 0); + gspWriteGxReg(0x0414, 0xCF); + gspWriteGxReg(0x0418, 0xD1); + gspWriteGxReg(0x041C, (0x1C5 << 16) | 0x1C1); + gspWriteGxReg(0x0420, 0x10000); + gspWriteGxReg(0x0424, 0x19D); + gspWriteGxReg(0x0428, 2); + gspWriteGxReg(0x042C, 0x192); + gspWriteGxReg(0x0430, 0x192); + gspWriteGxReg(0x0434, 0x192); + gspWriteGxReg(0x0438, 1); + gspWriteGxReg(0x043C, 2); + gspWriteGxReg(0x0440, (0x196 << 16) | 0x192); + gspWriteGxReg(0x0444, 0); + gspWriteGxReg(0x0448, 0); + + // Top screen fb geometry + gspWriteGxReg(0x045C, (400 << 16) | 240); // dimensions + gspWriteGxReg(0x0460, (0x1C1 << 16) | 0xD1); + gspWriteGxReg(0x0464, (0x192 << 16) | 2); + + // Top screen framebuffer format (initial) + gspWriteGxReg(0x0470, 0x80340); + + // Top screen unknown reg @ 0x9C + gspWriteGxReg(0x049C, 0); + + // Bottom screen LCD configuration + + // Bottom screen sync registers: + gspWriteGxReg(0x0500, 0x1C2); + gspWriteGxReg(0x0504, 0xD1); + gspWriteGxReg(0x0508, 0x1C1); + gspWriteGxReg(0x050C, 0x1C1); + gspWriteGxReg(0x0510, 0xCD); + gspWriteGxReg(0x0514, 0xCF); + gspWriteGxReg(0x0518, 0xD1); + gspWriteGxReg(0x051C, (0x1C5 << 16) | 0x1C1); + gspWriteGxReg(0x0520, 0x10000); + gspWriteGxReg(0x0524, 0x19D); + gspWriteGxReg(0x0528, 0x52); + gspWriteGxReg(0x052C, 0x192); + gspWriteGxReg(0x0530, 0x192); + gspWriteGxReg(0x0534, 0x4F); + gspWriteGxReg(0x0538, 0x50); + gspWriteGxReg(0x053C, 0x52); + gspWriteGxReg(0x0540, (0x198 << 16) | 0x194); + gspWriteGxReg(0x0544, 0); + gspWriteGxReg(0x0548, 0x11); + + // Bottom screen fb geometry + gspWriteGxReg(0x055C, (320 << 16) | 240); // dimensions + gspWriteGxReg(0x0560, (0x1C1 << 16) | 0xD1); + gspWriteGxReg(0x0564, (0x192 << 16) | 0x52); + + // Bottom screen framebuffer format (initial) + gspWriteGxReg(0x0570, 0x80300); + + // Bottom screen unknown reg @ 0x9C + gspWriteGxReg(0x059C, 0); + + // Initial, blank framebuffer (top left A/B, bottom A/B, top right A/B) + gspWriteGxReg(0x0468, 0x18300000); + gspWriteGxReg(0x046C, 0x18300000); + gspWriteGxReg(0x0568, 0x18300000); + gspWriteGxReg(0x056C, 0x18300000); + gspWriteGxReg(0x0494, 0x18300000); + gspWriteGxReg(0x0498, 0x18300000); + + // Framebuffer select: A + gspWriteGxReg(0x0478, 1); + gspWriteGxReg(0x0578, 1); + + // Clear DMA transfer (PPF) "transfer finished" bit + gspWriteGxRegMasked(0x0C18, 0, 0xFF00); + + // GX_GPU_CLK |= 0x70000 (value is 0x100 when gsp starts, enough to at least display framebuffers & have memory fill work) + // This enables the clock to some GPU components + gspWriteGxReg(0x0004, 0x70100); + + // Clear Memory Fill (PSC0 and PSC1) "busy" and "finished" bits + gspWriteGxRegMasked(0x001C, 0, 0xFF); + gspWriteGxRegMasked(0x002C, 0, 0xFF); + + // More init registers + gspWriteGxReg(0x0050, 0x22221200); + gspWriteGxRegMasked(0x0054, 0xFF2, 0xFFFF); + + // Enable some LCD clocks (?) (unsure) + gspWriteGxReg(0x0474, 0x10501); + gspWriteGxReg(0x0574, 0x10501); +} + Result gspInit(void) { - Result res=0; + Result ret=0; if (AtomicPostIncrement(&gspRefCount)) return 0; - res = srvGetServiceHandle(&gspGpuHandle, "gsp::Gpu"); - if (R_FAILED(res)) AtomicDecrement(&gspRefCount); - return res; + + // Initialize events + for (int i = 0; i < GSPGPU_EVENT_MAX; i ++) + LightEvent_Init(&gspEvents[i], RESET_STICKY); + + // Retrieve a GSP service session handle + ret = srvGetServiceHandle(&gspGpuHandle, "gsp::Gpu"); + if (R_FAILED(ret)) goto _fail0; + + // Acquire GPU rights + ret = GSPGPU_AcquireRight(0); + if (R_FAILED(ret)) goto _fail1; + + // Register ourselves as a user of graphics hardware + svcCreateEvent(&gspEvent, RESET_ONESHOT); + ret = GSPGPU_RegisterInterruptRelayQueue(gspEvent, 0x1, &gspSharedMemHandle, &gspThreadId); + if (R_FAILED(ret)) + goto _fail2; + + // Initialize the hardware if we are the first process to register + if (ret == 0x2A07) + gspHardwareInit(); + + // Map GSP shared memory + gspSharedMem = mappableAlloc(0x1000); + svcMapMemoryBlock(gspSharedMemHandle, (u32)gspSharedMem, MEMPERM_READWRITE, MEMPERM_DONTCARE); + + // Start event handling thread + gspRunEvents = true; + gspLastEvent = -1; + gspEventThread = threadCreate(gspEventThreadMain, 0x0, GSP_EVENT_STACK_SIZE, 0x1A, -2, true); + return 0; + +_fail2: + GSPGPU_ReleaseRight(); +_fail1: + svcCloseHandle(gspGpuHandle); +_fail0: + AtomicDecrement(&gspRefCount); + return ret; } void gspExit(void) { if (AtomicDecrement(&gspRefCount)) return; + + // Stop event handling thread + gspRunEvents = false; + svcSignalEvent(gspEvent); + threadJoin(gspEventThread, U64_MAX); + + // Unmap and close GSP shared memory + svcUnmapMemoryBlock(gspSharedMemHandle, (u32)gspSharedMem); + svcCloseHandle(gspSharedMemHandle); + mappableFree(gspSharedMem); + + // Unregister ourselves + GSPGPU_UnregisterInterruptRelayQueue(); + + // Release GPU rights and close the service handle + GSPGPU_ReleaseRight(); svcCloseHandle(gspGpuHandle); } +void gspPresentBuffer(unsigned screen, unsigned swap, const void* fb_a, const void* fb_b, unsigned stride, u32 mode) +{ + GSPGPU_FramebufferInfo info; + info.active_framebuf = swap; + info.framebuf0_vaddr = (u32*)fb_a; + info.framebuf1_vaddr = (u32*)fb_b; + info.framebuf_widthbytesize = stride; + info.format = mode; + info.framebuf_dispselect = swap; + info.unk = 0; + + s32* fbInfoHeader = (s32*)((u8*)gspSharedMem + 0x200 + gspThreadId*0x80 + screen*0x40); + GSPGPU_FramebufferInfo* fbInfos = (GSPGPU_FramebufferInfo*)&fbInfoHeader[1]; + unsigned pos = 1 - (*fbInfoHeader & 0xff); + fbInfos[pos] = info; + __dsb(); + + union + { + s32 header; + struct { u8 swap, update; }; + } u; + + do + { + u.header = __ldrex(fbInfoHeader); + u.swap = pos; + u.update = 1; + } while (__strex(fbInfoHeader, u.header)); +} + void gspSetEventCallback(GSPGPU_Event id, ThreadFunc cb, void* data, bool oneShot) { if(id>= GSPGPU_EVENT_MAX)return; @@ -52,29 +259,6 @@ void gspSetEventCallback(GSPGPU_Event id, ThreadFunc cb, void* data, bool oneSho gspEventCbOneShot[id] = oneShot; } -Result gspInitEventHandler(Handle _gspEvent, vu8* _gspSharedMem, u8 gspThreadId) -{ - // Initialize events - int i; - for (i = 0; i < GSPGPU_EVENT_MAX; i ++) - LightEvent_Init(&gspEvents[i], RESET_STICKY); - - // Start event thread - gspEvent = _gspEvent; - gspEventData = _gspSharedMem + gspThreadId*0x40; - gspRunEvents = true; - gspEventThread = threadCreate(gspEventThreadMain, 0x0, GSP_EVENT_STACK_SIZE, 0x1A, -2, true); - return 0; -} - -void gspExitEventHandler(void) -{ - // Stop event thread - gspRunEvents = false; - svcSignalEvent(gspEvent); - threadJoin(gspEventThread, U64_MAX); -} - void gspWaitForEvent(GSPGPU_Event id, bool nextEvent) { if(id>= GSPGPU_EVENT_MAX)return; @@ -110,6 +294,7 @@ static int popInterrupt() { int curEvt; bool strexFailed; + u8* gspEventData = (u8*)gspSharedMem + gspThreadId*0x40; do { union { struct { @@ -154,6 +339,9 @@ void gspEventThreadMain(void *arg) svcWaitSynchronization(gspEvent, U64_MAX); svcClearEvent(gspEvent); + if (!gspRunEvents) + break; + while (true) { int curEvt = popInterrupt(); @@ -176,7 +364,6 @@ void gspEventThreadMain(void *arg) __ldrex(&gspLastEvent); while (__strex(&gspLastEvent, curEvt)); syncArbitrateAddress(&gspLastEvent, ARBITRATION_SIGNAL, 1); - gspEventCounts[curEvt]++; } } } @@ -185,10 +372,9 @@ void gspEventThreadMain(void *arg) //essentially : get commandIndex and totalCommands, calculate offset of new command, copy command and update totalCommands //use LDREX/STREX because this data may also be accessed by the GSP module and we don't want to break stuff //(mostly, we could overwrite the buffer header with wrong data and make the GSP module reexecute old commands) -Result gspSubmitGxCommand(u32* sharedGspCmdBuf, u32 gxCommand[0x8]) +Result gspSubmitGxCommand(u32 gxCommand[0x8]) { - if(!sharedGspCmdBuf || !gxCommand)return -1; - + u32* sharedGspCmdBuf = (u32*)((u8*)gspSharedMem + 0x800 + gspThreadId*0x200); u32 cmdBufHeader = __ldrex((s32*)sharedGspCmdBuf); u8 commandIndex=cmdBufHeader&0xFF;