Overhaul gspgpu/gfx code, see details:
- GSPGPU changes: - gspInit now properly initializes the event queue, GSP shared memory and first-time initialization - previously this was done in gfxInit. - Removed gspInitEventHandler/gspExitEventHandler in line with above. - Added gspPresentBuffer for pushing a framebuffer to the internal GSP swap queue (previously this was an internal function in gfx.c). - Added defines for screen IDs and screen dimensions. - Removed sharedGspCmdBuf param from gspSubmitGxCommand (now uses the correct GSP shared memory address automatically). - Removed gxCmdBuf global variable (no longer needed as per above). - Removed GSPGPU_REBASE_REG (leftover from early 3DS homebrew). - GFX changes: - Documentation overhaul and code cleanup. - Simplified implementation using the enhanced GSPGPU service wrapper. - Top left/right framebuffers now form a single allocation instead of being split into two separate allocations. - Fixed LCD configuration mode when framebuffers are on VRAM. - Removed the ability to forcefully swap the screens: gspPresentBuffer is now always used (i.e. GSP shared mem). The 'immediate' parameter of gfxConfigScreen now does nothing, and gfxSwapBuffers/Gpu now do the same thing. - Removed gfx{TopLeft,TopRight,Bottom}Framebuffers global variables (please use gfxGetFramebuffer instead as originally intended...)
This commit is contained in:
parent
26f5f7eb33
commit
6ef91576ae
@ -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];
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
};
|
||||
|
||||
|
@ -1,367 +1,162 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user