diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 111fca5..a93a837 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,6 +48,16 @@ externalproject_add(libgfd -DCMAKE_INSTALL_PREFIX:string= -DCMAKE_TOOLCHAIN_FILE:string=${CMAKE_SOURCE_DIR}/cmake/wut-toolchain.cmake) +externalproject_add(libwhb + SOURCE_DIR "${CMAKE_SOURCE_DIR}/src/libwhb" + CMAKE_GENERATOR "Unix Makefiles" + INSTALL_DIR "${CMAKE_BINARY_DIR}/staging" + CMAKE_CACHE_ARGS + -DDEVKITPPC:string=${DEVKITPPC} + -DWUT_ROOT:string=${CMAKE_SOURCE_DIR} + -DCMAKE_INSTALL_PREFIX:string= + -DCMAKE_TOOLCHAIN_FILE:string=${CMAKE_SOURCE_DIR}/cmake/wut-toolchain.cmake) + # We must force build because changes are not detected with ExternalProject... externalproject_add_step(crt forcebuild COMMAND ${CMAKE_COMMAND} -E echo "Force build of crt" @@ -78,3 +88,9 @@ externalproject_add_step(libgfd forcebuild DEPENDEES "configure" DEPENDERS "build" ALWAYS 1) + +externalproject_add_step(libwhb forcebuild + COMMAND ${CMAKE_COMMAND} -E echo "Force build of libwhb" + DEPENDEES "configure" + DEPENDERS "build" + ALWAYS 1) diff --git a/src/libwhb/CMakeLists.txt b/src/libwhb/CMakeLists.txt new file mode 100644 index 0000000..d89472f --- /dev/null +++ b/src/libwhb/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.2) +project(libwhb) + +file(GLOB_RECURSE SOURCE_FILES *.c) +file(GLOB_RECURSE HEADER_FILES *.h) + +add_library(whb STATIC ${SOURCE_FILES} ${HEADER_FILES}) + +target_include_directories(whb PRIVATE "../../include") +target_include_directories(whb PRIVATE "../libdefaultheap/include") +target_include_directories(whb PRIVATE "../libgfd/include") +target_include_directories(whb PUBLIC "include") + +install(TARGETS whb + ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib") +install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ + DESTINATION "${CMAKE_INSTALL_PREFIX}/include" + FILES_MATCHING PATTERN "*.h*") + diff --git a/src/libwhb/include/whb/align.h b/src/libwhb/include/whb/align.h new file mode 100644 index 0000000..221e33a --- /dev/null +++ b/src/libwhb/include/whb/align.h @@ -0,0 +1,20 @@ +#pragma once +#include + +/** + * \defgroup whb_align Align + * \ingroup whb + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define WHBAlignUp(val, align) ((val + align - 1) & ~(align - 1)) + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/src/libwhb/include/whb/file.h b/src/libwhb/include/whb/file.h new file mode 100644 index 0000000..914ccd2 --- /dev/null +++ b/src/libwhb/include/whb/file.h @@ -0,0 +1,47 @@ +#pragma once +#include + +/** + * \defgroup whb_file Filesystem + * \ingroup whb + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum WHBFileError +{ + WHB_FILE_OK = 0, + WHB_FILE_FATAL_ERROR = -1, +} WHBFileError; + +int32_t +WHBOpenFile(const char *path, + const char *mode); + +uint32_t +WHBGetFileSize(int32_t handle); + +uint32_t +WHBReadFile(int32_t handle, + void *buf, + uint32_t size, + uint32_t count); + +int32_t +WHBCloseFile(int32_t handle); + +char * +WHBReadWholeFile(const char *path, + uint32_t *outSize); + +void +WHBFreeWholeFile(char *file); + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/src/libwhb/include/whb/gfx.h b/src/libwhb/include/whb/gfx.h new file mode 100644 index 0000000..095af9b --- /dev/null +++ b/src/libwhb/include/whb/gfx.h @@ -0,0 +1,87 @@ +#pragma once +#include +#include + +/** + * \defgroup whb_gfx Graphics + * \ingroup whb + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct WHBGfxShaderGroup WHBGfxShaderGroup; + +struct WHBGfxShaderGroup +{ + GX2FetchShader fetchShader; + void *fetchShaderProgram; + GX2PixelShader *pixelShader; + GX2VertexShader *vertexShader; + uint32_t numAttributes; + GX2AttribStream attributes[16]; +}; + +BOOL +WHBGfxInit(); + +void +WHBGfxShutdown(); + +void +WHBGfxBeginRender(); + +void +WHBGfxFinishRender(); + +void +WHBGfxBeginRenderDRC(); + +void +WHBGfxFinishRenderDRC(); + +void +WHBGfxBeginRenderTV(); + +void +WHBGfxFinishRenderTV(); + +GX2PixelShader * +WHBGfxLoadGFDPixelShader(uint32_t index, + const void *file); + +BOOL +WHBGfxFreePixelShader(GX2PixelShader *shader); + +GX2VertexShader * +WHBGfxLoadGFDVertexShader(uint32_t index, + const void *file); + +BOOL +WHBGfxFreeVertexShader(GX2VertexShader *shader); + +BOOL +WHBGfxLoadGFDShaderGroup(WHBGfxShaderGroup *group, + uint32_t index, + const void *file); + +BOOL +WHBGfxInitShaderAttribute(WHBGfxShaderGroup *group, + const char *name, + uint32_t buffer, + uint32_t offset, + GX2AttribFormat format); + +BOOL +WHBGfxInitFetchShader(WHBGfxShaderGroup *group); + +BOOL +WHBGfxFreeShaderGroup(WHBGfxShaderGroup *group); + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/src/libwhb/include/whb/proc.h b/src/libwhb/include/whb/proc.h new file mode 100644 index 0000000..fdc61b6 --- /dev/null +++ b/src/libwhb/include/whb/proc.h @@ -0,0 +1,14 @@ +#pragma once +#include + +void +WHBProcInit(); + +void +WHBProcShutdown(); + +void +WHBProcStopRunning(); + +BOOL +WHBProcIsRunning(); diff --git a/src/libwhb/src/file.c b/src/libwhb/src/file.c new file mode 100644 index 0000000..d120b01 --- /dev/null +++ b/src/libwhb/src/file.c @@ -0,0 +1,150 @@ +#include +#include +#include +#include + +static BOOL +sInitialised = FALSE; + +static FSClient +sClient; + +static BOOL +InitFileSystem() +{ + if (!sInitialised) { + FSInit(); + + if (FSAddClient(&sClient, -1) != FS_STATUS_OK) { + return FALSE; + } + + sInitialised = TRUE; + } + + return TRUE; +} + +int32_t +WHBOpenFile(const char *path, + const char *mode) +{ + FSCmdBlock cmd; + FSStatus result; + FSFileHandle handle; + char tmp[256]; + tmp[0] = 0; + + if (!InitFileSystem()) { + return WHB_FILE_FATAL_ERROR; + } + + if (path[0] != '/') { + strcat(tmp, "/vol/content/"); + strcat(tmp, path); + } else { + strcat(tmp, path); + } + + FSInitCmdBlock(&cmd); + result = FSOpenFile(&sClient, &cmd, tmp, mode, &handle, -1); + if (result < 0) { + return WHB_FILE_FATAL_ERROR; + } + + return (int32_t)handle; +} + +uint32_t +WHBGetFileSize(int32_t handle) +{ + FSCmdBlock cmd; + FSStatus result; + FSStat stat; + FSInitCmdBlock(&cmd); + result = FSGetStatFile(&sClient, &cmd, (FSFileHandle)handle, &stat, -1); + if (result < 0) { + return 0; + } + + return stat.size; +} + +uint32_t +WHBReadFile(int32_t handle, + void *buf, + uint32_t size, + uint32_t count) +{ + FSCmdBlock cmd; + FSStatus result; + FSInitCmdBlock(&cmd); + result = FSReadFile(&sClient, &cmd, buf, size, count, (FSFileHandle)handle, 0, -1); + if (result < 0) { + return 0; + } + + return (uint32_t)result; +} + +int32_t +WHBCloseFile(int32_t handle) +{ + FSCmdBlock cmd; + FSStatus result; + FSInitCmdBlock(&cmd); + result = FSCloseFile(&sClient, &cmd, (FSFileHandle)handle, -1); + if (result != FS_STATUS_OK) { + return WHB_FILE_FATAL_ERROR; + } + + return WHB_FILE_OK; +} + +char * +WHBReadWholeFile(const char *path, + uint32_t *outSize) +{ + int32_t handle; + uint32_t size; + char *buf = NULL; + handle = WHBOpenFile(path, "r"); + if (handle == WHB_FILE_FATAL_ERROR) { + return NULL; + } + + size = WHBGetFileSize(handle); + if (size == 0) { + goto error; + } + + buf = MEMAllocFromDefaultHeapEx(size, 64); + if (!buf) { + goto error; + } + + if (WHBReadFile(handle, buf, 1, size) != size) { + goto error; + } + + if (outSize) { + *outSize = size; + } + + WHBCloseFile(handle); + return buf; + +error: + if (buf) { + MEMFreeToDefaultHeap(buf); + } + + WHBCloseFile(handle); + return NULL; +} + +void +WHBFreeWholeFile(char *file) +{ + MEMFreeToDefaultHeap(file); +} diff --git a/src/libwhb/src/gfx.c b/src/libwhb/src/gfx.c new file mode 100644 index 0000000..ff794e0 --- /dev/null +++ b/src/libwhb/src/gfx.c @@ -0,0 +1,465 @@ +#include "gfx_heap.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WHB_GFX_COMMAND_BUFFER_POOL_SIZE (0x400000) + +static void * +sCommandBufferPool = NULL; + +static GX2DrcRenderMode +sDrcRenderMode; + +static void * +sDrcScanBuffer = NULL; + +static uint32_t +sDrcScanBufferSize = 0; + +static GX2SurfaceFormat +sDrcSurfaceFormat; + +static GX2TVRenderMode +sTvRenderMode; + +static void * +sTvScanBuffer = NULL; + +static uint32_t +sTvScanBufferSize = 0; + +static GX2SurfaceFormat +sTvSurfaceFormat; + +static GX2ColorBuffer +sTvColourBuffer = { 0 }; + +static GX2DepthBuffer +sTvDepthBuffer = { 0 }; + +static GX2ColorBuffer +sDrcColourBuffer = { 0 }; + +static GX2DepthBuffer +sDrcDepthBuffer = { 0 }; + +static GX2ContextState * +sTvContextState = NULL; + +static GX2ContextState * +sDrcContextState = NULL; + +static void * +GfxGX2RAlloc(GX2RResourceFlags flags, + uint32_t size, + uint32_t alignment) +{ + // Color, depth, scan buffers all belong in MEM1 + if ((flags & (GX2R_RESOURCE_BIND_COLOR_BUFFER + | GX2R_RESOURCE_BIND_DEPTH_BUFFER + | GX2R_RESOURCE_BIND_SCAN_BUFFER + | GX2R_RESOURCE_USAGE_FORCE_MEM1)) + && !(flags & GX2R_RESOURCE_USAGE_FORCE_MEM2)) { + return GfxHeapAllocMEM1(size, alignment); + } else { + return GfxHeapAllocMEM2(size, alignment); + } +} + +static void +GfxGX2RFree(GX2RResourceFlags flags, void *block) +{ + if ((flags & (GX2R_RESOURCE_BIND_COLOR_BUFFER + | GX2R_RESOURCE_BIND_DEPTH_BUFFER + | GX2R_RESOURCE_BIND_SCAN_BUFFER + | GX2R_RESOURCE_USAGE_FORCE_MEM1)) + && !(flags & GX2R_RESOURCE_USAGE_FORCE_MEM2)) { + return GfxHeapFreeMEM1(block); + } else { + return GfxHeapFreeMEM2(block); + } +} + +static void +GfxInitTvColourBuffer(GX2ColorBuffer *cb, + uint32_t width, + uint32_t height, + GX2SurfaceFormat format, + GX2AAMode aa) +{ + memset(cb, 0, sizeof(GX2ColorBuffer)); + cb->surface.use = GX2_SURFACE_USE_TEXTURE_COLOR_BUFFER_TV; + cb->surface.dim = GX2_SURFACE_DIM_TEXTURE_2D; + cb->surface.width = width; + cb->surface.height = height; + cb->surface.depth = 1; + cb->surface.mipLevels = 1; + cb->surface.format = format; + cb->surface.aa = aa; + cb->surface.tileMode = GX2_TILE_MODE_DEFAULT; + cb->viewNumSlices = 1; + GX2CalcSurfaceSizeAndAlignment(&cb->surface); + GX2InitColorBufferRegs(cb); +} + +static void +GfxInitDepthBuffer(GX2DepthBuffer *db, + uint32_t width, + uint32_t height, + GX2SurfaceFormat format, + GX2AAMode aa) +{ + memset(db, 0, sizeof(GX2DepthBuffer)); + + if (format == GX2_SURFACE_FORMAT_UNORM_R24_X8 || format == GX2_SURFACE_FORMAT_FLOAT_D24_S8) { + db->surface.use = GX2_SURFACE_USE_DEPTH_BUFFER; + } else { + db->surface.use = GX2_SURFACE_USE_DEPTH_BUFFER | GX2_SURFACE_USE_TEXTURE; + } + + db->surface.dim = GX2_SURFACE_DIM_TEXTURE_2D; + db->surface.width = width; + db->surface.height = height; + db->surface.depth = 1; + db->surface.mipLevels = 1; + db->surface.format = format; + db->surface.aa = aa; + db->surface.tileMode = GX2_TILE_MODE_DEFAULT; + db->viewNumSlices = 1; + db->depthClear = 1.0f; + GX2CalcSurfaceSizeAndAlignment(&db->surface); + GX2InitDepthBufferRegs(db); +} + +static uint32_t +GfxProcCallbackAcquired(void *context) +{ + GfxHeapInitMEM1(); + GfxHeapInitForeground(); + + // Allocate TV scan buffer. + sTvScanBuffer = GfxHeapAllocForeground(sTvScanBufferSize, GX2_SCAN_BUFFER_ALIGNMENT); + if (!sTvScanBuffer) { + goto error; + } + GX2Invalidate(GX2_INVALIDATE_MODE_CPU, sTvScanBuffer, sTvScanBufferSize); + GX2SetTVBuffer(sTvScanBuffer, sTvScanBufferSize, sTvRenderMode, sTvSurfaceFormat, GX2_BUFFERING_MODE_DOUBLE); + + // Allocate TV colour buffer. + sTvColourBuffer.surface.image = GfxHeapAllocMEM1(sTvColourBuffer.surface.imageSize, sTvColourBuffer.surface.alignment); + if (!sTvColourBuffer.surface.image) { + goto error; + } + GX2Invalidate(GX2_INVALIDATE_MODE_CPU, sTvColourBuffer.surface.image, sTvColourBuffer.surface.imageSize); + + // Allocate TV depth buffer. + sTvDepthBuffer.surface.image = GfxHeapAllocMEM1(sTvDepthBuffer.surface.imageSize, sTvDepthBuffer.surface.alignment); + if (!sTvDepthBuffer.surface.image) { + goto error; + } + GX2Invalidate(GX2_INVALIDATE_MODE_CPU, sTvDepthBuffer.surface.image, sTvDepthBuffer.surface.imageSize); + + // Allocate DRC scan buffer. + sDrcScanBuffer = GfxHeapAllocForeground(sDrcScanBufferSize, GX2_SCAN_BUFFER_ALIGNMENT); + if (!sDrcScanBuffer) { + goto error; + } + GX2Invalidate(GX2_INVALIDATE_MODE_CPU, sDrcScanBuffer, sDrcScanBufferSize); + GX2SetDRCBuffer(sDrcScanBuffer, sDrcScanBufferSize, sDrcRenderMode, sDrcSurfaceFormat, GX2_BUFFERING_MODE_DOUBLE); + + // Allocate DRC colour buffer. + sDrcColourBuffer.surface.image = GfxHeapAllocMEM1(sDrcColourBuffer.surface.imageSize, sDrcColourBuffer.surface.alignment); + if (!sDrcColourBuffer.surface.image) { + goto error; + } + GX2Invalidate(GX2_INVALIDATE_MODE_CPU, sDrcColourBuffer.surface.image, sDrcColourBuffer.surface.imageSize); + + // Allocate DRC depth buffer. + sDrcDepthBuffer.surface.image = GfxHeapAllocMEM1(sDrcDepthBuffer.surface.imageSize, sDrcDepthBuffer.surface.alignment); + if (!sDrcDepthBuffer.surface.image) { + goto error; + } + GX2Invalidate(GX2_INVALIDATE_MODE_CPU, sDrcDepthBuffer.surface.image, sDrcDepthBuffer.surface.imageSize); + return 0; + +error: + return 1; +} + +static uint32_t +GfxProcCallbackReleased(void *context) +{ + GX2DrawDone(); + + if (sTvScanBuffer) { + GfxHeapFreeForeground(sTvScanBuffer); + sTvScanBuffer = NULL; + } + + if (sTvColourBuffer.surface.image) { + GfxHeapFreeMEM1(sTvColourBuffer.surface.image); + sTvColourBuffer.surface.image = NULL; + } + + if (sTvDepthBuffer.surface.image) { + GfxHeapFreeMEM1(sTvDepthBuffer.surface.image); + sTvDepthBuffer.surface.image = NULL; + } + + if (sDrcScanBuffer) { + GfxHeapFreeForeground(sDrcScanBuffer); + sDrcScanBuffer = NULL; + } + + if (sDrcColourBuffer.surface.image) { + GfxHeapFreeMEM1(sDrcColourBuffer.surface.image); + sDrcColourBuffer.surface.image = NULL; + } + + if (sDrcDepthBuffer.surface.image) { + GfxHeapFreeMEM1(sDrcDepthBuffer.surface.image); + sDrcDepthBuffer.surface.image = NULL; + } + + GfxHeapDestroyMEM1(); + GfxHeapDestroyForeground(); + return 0; +} + +BOOL +WHBGfxInit() +{ + uint32_t drcWidth, drcHeight; + uint32_t tvWidth, tvHeight; + uint32_t unk; + + sCommandBufferPool = GfxHeapAllocMEM2(WHB_GFX_COMMAND_BUFFER_POOL_SIZE, + GX2_COMMAND_BUFFER_ALIGNMENT); + if (!sCommandBufferPool) { + goto error; + } + + uint32_t initAttribs[] = { + GX2_INIT_CMD_BUF_BASE, (uintptr_t)sCommandBufferPool, + GX2_INIT_CMD_BUF_POOL_SIZE, WHB_GFX_COMMAND_BUFFER_POOL_SIZE, + GX2_INIT_ARGC, 0, + GX2_INIT_ARGV, 0, + GX2_INIT_END + }; + GX2Init(initAttribs); + + sDrcRenderMode = GX2GetSystemDRCScanMode(); + sTvSurfaceFormat = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8; + sDrcSurfaceFormat = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8; + + switch(GX2GetSystemTVScanMode()) + { + case GX2_TV_SCAN_MODE_480I: + case GX2_TV_SCAN_MODE_480P: + sTvRenderMode = GX2_TV_RENDER_MODE_WIDE_480P; + tvWidth = 854; + tvHeight = 480; + break; + case GX2_TV_SCAN_MODE_1080I: + case GX2_TV_SCAN_MODE_1080P: + sTvRenderMode = GX2_TV_RENDER_MODE_WIDE_1080P; + tvWidth = 1920; + tvHeight = 1080; + break; + case GX2_TV_SCAN_MODE_720P: + default: + sTvRenderMode = GX2_TV_RENDER_MODE_WIDE_720P; + tvWidth = 1280; + tvHeight = 720; + break; + } + + drcWidth = 854; + drcHeight = 480; + + // Setup TV and DRC buffers - they will be allocated in GfxProcCallbackAcquired. + GX2CalcTVSize(sTvRenderMode, sTvSurfaceFormat, GX2_BUFFERING_MODE_DOUBLE, &sTvScanBufferSize, &unk); + GfxInitTvColourBuffer(&sTvColourBuffer, tvWidth, tvHeight, sTvSurfaceFormat, GX2_AA_MODE1X); + GfxInitDepthBuffer(&sTvDepthBuffer, sTvColourBuffer.surface.width, sTvColourBuffer.surface.height, GX2_SURFACE_FORMAT_FLOAT_R32, sTvColourBuffer.surface.aa); + + GX2CalcDRCSize(sDrcRenderMode, sDrcSurfaceFormat, GX2_BUFFERING_MODE_DOUBLE, &sDrcScanBufferSize, &unk); + GfxInitTvColourBuffer(&sDrcColourBuffer, drcWidth, drcHeight, sDrcSurfaceFormat, GX2_AA_MODE1X); + GfxInitDepthBuffer(&sDrcDepthBuffer, sDrcColourBuffer.surface.width, sDrcColourBuffer.surface.height, GX2_SURFACE_FORMAT_FLOAT_R32, sDrcColourBuffer.surface.aa); + + if (GfxProcCallbackAcquired(NULL) != 0) { + goto error; + } + + GX2RSetAllocator(&GfxGX2RAlloc, &GfxGX2RFree); + ProcUIRegisterCallback(PROCUI_CALLBACK_ACQUIRE, GfxProcCallbackAcquired, NULL, 100); + ProcUIRegisterCallback(PROCUI_CALLBACK_RELEASE, GfxProcCallbackReleased, NULL, 100); + + // Initialise TV context state. + sTvContextState = GfxHeapAllocMEM2(sizeof(GX2ContextState), GX2_CONTEXT_STATE_ALIGNMENT); + if (!sTvContextState) { + goto error; + } + GX2SetupContextStateEx(sTvContextState, TRUE); + GX2SetContextState(sTvContextState); + GX2SetColorBuffer(&sTvColourBuffer, GX2_RENDER_TARGET_0); + GX2SetDepthBuffer(&sTvDepthBuffer); + GX2SetViewport(0, 0, (float)sTvColourBuffer.surface.width, (float)sTvColourBuffer.surface.height, 0.0f, 1.0f); + GX2SetScissor(0, 0, (float)sTvColourBuffer.surface.width, (float)sTvColourBuffer.surface.height); + GX2SetTVScale((float)sTvColourBuffer.surface.width, (float)sTvColourBuffer.surface.height); + + // Initialise DRC context state. + sDrcContextState = GfxHeapAllocMEM2(sizeof(GX2ContextState), GX2_CONTEXT_STATE_ALIGNMENT); + if (!sDrcContextState) { + goto error; + } + GX2SetupContextStateEx(sDrcContextState, TRUE); + GX2SetContextState(sDrcContextState); + GX2SetColorBuffer(&sDrcColourBuffer, GX2_RENDER_TARGET_0); + GX2SetDepthBuffer(&sDrcDepthBuffer); + GX2SetViewport(0, 0, (float)sDrcColourBuffer.surface.width, (float)sDrcColourBuffer.surface.height, 0.0f, 1.0f); + GX2SetScissor(0, 0, (float)sDrcColourBuffer.surface.width, (float)sDrcColourBuffer.surface.height); + GX2SetDRCScale((float)sDrcColourBuffer.surface.width, (float)sDrcColourBuffer.surface.height); + + // Set 60fps VSync + GX2SetSwapInterval(1); + GX2SetTVEnable(TRUE); + GX2SetDRCEnable(TRUE); + + return TRUE; + +error: + if (sCommandBufferPool) { + GfxHeapFreeMEM2(sCommandBufferPool); + sCommandBufferPool = NULL; + } + + if (sTvScanBuffer) { + GfxHeapFreeForeground(sTvScanBuffer); + sTvScanBuffer = NULL; + } + + if (sTvColourBuffer.surface.image) { + GfxHeapFreeMEM1(sTvColourBuffer.surface.image); + sTvColourBuffer.surface.image = NULL; + } + + if (sTvDepthBuffer.surface.image) { + GfxHeapFreeMEM1(sTvDepthBuffer.surface.image); + sTvDepthBuffer.surface.image = NULL; + } + + if (sTvContextState) { + GfxHeapFreeMEM2(sTvContextState); + sTvContextState = NULL; + } + + if (sDrcScanBuffer) { + GfxHeapFreeForeground(sDrcScanBuffer); + sDrcScanBuffer = NULL; + } + + if (sDrcColourBuffer.surface.image) { + GfxHeapFreeMEM1(sDrcColourBuffer.surface.image); + sDrcColourBuffer.surface.image = NULL; + } + + if (sDrcDepthBuffer.surface.image) { + GfxHeapFreeMEM1(sDrcDepthBuffer.surface.image); + sDrcDepthBuffer.surface.image = NULL; + } + + if (sDrcContextState) { + GfxHeapFreeMEM2(sDrcContextState); + sDrcContextState = NULL; + } + + return FALSE; +} + +void +WHBGfxShutdown() +{ + GfxProcCallbackReleased(NULL); + GX2Shutdown(); + + if (sTvContextState) { + GfxHeapFreeMEM2(sTvContextState); + sTvContextState = NULL; + } + + if (sDrcContextState) { + GfxHeapFreeMEM2(sDrcContextState); + sDrcContextState = NULL; + } + + if (sCommandBufferPool) { + GfxHeapFreeMEM2(sCommandBufferPool); + sCommandBufferPool = NULL; + } + + GX2RSetAllocator(NULL, NULL); +} + +void +WHBGfxBeginRender() +{ + uint32_t swapCount, flipCount; + OSTime lastFlip, lastVsync; + + while (true) { + GX2WaitForVsync(); + GX2GetSwapStatus(&swapCount, &flipCount, &lastFlip, &lastVsync); + + if (flipCount >= swapCount) { + break; + } + } +} + +void +WHBGfxFinishRender() +{ + GX2Flush(); + GX2SwapScanBuffers(); + GX2Flush(); +} + +void +WHBGfxBeginRenderDRC() +{ + GX2SetContextState(sDrcContextState); + GX2ClearColor(&sDrcColourBuffer, 0.0f, 1.0f, 0.0f, 1.0f); + GX2ClearDepthStencilEx(&sDrcDepthBuffer, sDrcDepthBuffer.depthClear, sDrcDepthBuffer.stencilClear, GX2_CLEAR_FLAGS_DEPTH | GX2_CLEAR_FLAGS_STENCIL); + GX2SetContextState(sDrcContextState); +} + +void +WHBGfxFinishRenderDRC() +{ + GX2CopyColorBufferToScanBuffer(&sDrcColourBuffer, GX2_SCAN_TARGET_DRC); +} + +void +WHBGfxBeginRenderTV() +{ + GX2SetContextState(sTvContextState); + GX2ClearColor(&sTvColourBuffer, 1.0f, 0.0f, 0.0f, 1.0f); + GX2ClearDepthStencilEx(&sTvDepthBuffer, sTvDepthBuffer.depthClear, sTvDepthBuffer.stencilClear, GX2_CLEAR_FLAGS_DEPTH | GX2_CLEAR_FLAGS_STENCIL); + GX2SetContextState(sTvContextState); +} + +void +WHBGfxFinishRenderTV() +{ + GX2CopyColorBufferToScanBuffer(&sTvColourBuffer, GX2_SCAN_TARGET_TV); +} diff --git a/src/libwhb/src/gfx_heap.c b/src/libwhb/src/gfx_heap.c new file mode 100644 index 0000000..5f735dd --- /dev/null +++ b/src/libwhb/src/gfx_heap.c @@ -0,0 +1,156 @@ +#include "gfx_heap.h" +#include +#include +#include +#include + +static void * +sGfxHeapMEM1 = NULL; + +static void * +sGfxHeapForeground = NULL; + +BOOL +GfxHeapInitMEM1() +{ + MEMHeapHandle mem1 = MEMGetBaseHeapHandle(MEM_BASE_HEAP_MEM1); + uint32_t size = MEMGetAllocatableSizeForFrmHeapEx(mem1, 4); + if (!size) { + return FALSE; + } + + void *base = MEMAllocFromFrmHeapEx(mem1, size, 4); + if (!base) { + return FALSE; + } + + sGfxHeapMEM1 = MEMCreateExpHeapEx(base, size, 0); + if (!sGfxHeapMEM1) { + return FALSE; + } + + return TRUE; +} + +BOOL +GfxHeapDestroyMEM1() +{ + MEMHeapHandle mem1 = MEMGetBaseHeapHandle(MEM_BASE_HEAP_MEM1); + + if (sGfxHeapMEM1) { + MEMDestroyExpHeap(sGfxHeapMEM1); + sGfxHeapMEM1 = NULL; + } + + MEMFreeToFrmHeap(mem1, MEM_FRAME_HEAP_FREE_ALL); + return TRUE; +} + +BOOL +GfxHeapInitForeground() +{ + MEMHeapHandle mem1 = MEMGetBaseHeapHandle(MEM_BASE_HEAP_FG); + uint32_t size = MEMGetAllocatableSizeForFrmHeapEx(mem1, 4); + if (!size) { + return FALSE; + } + + void *base = MEMAllocFromFrmHeapEx(mem1, size, 4); + if (!base) { + return FALSE; + } + + sGfxHeapForeground = MEMCreateExpHeapEx(base, size, 0); + if (!sGfxHeapForeground) { + return FALSE; + } + + return TRUE; +} + +BOOL +GfxHeapDestroyForeground() +{ + MEMHeapHandle foreground = MEMGetBaseHeapHandle(MEM_BASE_HEAP_FG); + + if (sGfxHeapForeground) { + MEMDestroyExpHeap(sGfxHeapForeground); + sGfxHeapForeground = NULL; + } + + MEMFreeToFrmHeap(foreground, MEM_FRAME_HEAP_FREE_ALL); + return TRUE; +} + +void * +GfxHeapAllocMEM1(uint32_t size, + uint32_t alignment) +{ + void *block; + + if (!sGfxHeapMEM1) { + return NULL; + } + + if (alignment < 4) { + alignment = 4; + } + + block = MEMAllocFromExpHeapEx(sGfxHeapMEM1, size, alignment); + return block; +} + +void +GfxHeapFreeMEM1(void *block) +{ + if (!sGfxHeapMEM1) { + return; + } + + MEMFreeToExpHeap(sGfxHeapMEM1, block); +} + +void * +GfxHeapAllocForeground(uint32_t size, + uint32_t alignment) +{ + void *block; + + if (!sGfxHeapForeground) { + return NULL; + } + + if (alignment < 4) { + alignment = 4; + } + + block = MEMAllocFromExpHeapEx(sGfxHeapForeground, size, alignment); + return block; +} + +void +GfxHeapFreeForeground(void *block) +{ + if (!sGfxHeapForeground) { + return; + } + + MEMFreeToExpHeap(sGfxHeapForeground, block); +} + +void * +GfxHeapAllocMEM2(uint32_t size, + uint32_t alignment) +{ + if (alignment < 4) { + alignment = 4; + } + + return MEMAllocFromDefaultHeapEx(size, alignment); +} + +void +GfxHeapFreeMEM2(void *block) +{ + MEMFreeToDefaultHeap(block); +} diff --git a/src/libwhb/src/gfx_heap.h b/src/libwhb/src/gfx_heap.h new file mode 100644 index 0000000..4d4debb --- /dev/null +++ b/src/libwhb/src/gfx_heap.h @@ -0,0 +1,35 @@ +#pragma once +#include + +BOOL +GfxHeapInitMEM1(); + +BOOL +GfxHeapDestroyMEM1(); + +BOOL +GfxHeapInitForeground(); + +BOOL +GfxHeapDestroyForeground(); + +void * +GfxHeapAllocMEM1(uint32_t size, + uint32_t alignment); + +void +GfxHeapFreeMEM1(void *block); + +void * +GfxHeapAllocForeground(uint32_t size, + uint32_t alignment); + +void +GfxHeapFreeForeground(void *block); + +void * +GfxHeapAllocMEM2(uint32_t size, + uint32_t alignment); + +void +GfxHeapFreeMEM2(void *block); diff --git a/src/libwhb/src/gfx_shader.c b/src/libwhb/src/gfx_shader.c new file mode 100644 index 0000000..8910558 --- /dev/null +++ b/src/libwhb/src/gfx_shader.c @@ -0,0 +1,265 @@ +#include "gfx_heap.h" +#include +#include +#include +#include +#include +#include + +GX2PixelShader * +WHBGfxLoadGFDPixelShader(uint32_t index, + const void *file) +{ + uint32_t headerSize, programSize; + GX2PixelShader *shader = NULL; + void *program = NULL; + + if (index >= GFDGetPixelShaderCount(file)) { + goto error; + } + + headerSize = GFDGetPixelShaderHeaderSize(index, file); + programSize = GFDGetPixelShaderProgramSize(index, file); + + if (!headerSize || !programSize) { + goto error; + } + + shader = (GX2PixelShader *)GfxHeapAllocMEM2(headerSize, 64); + if (!shader) { + goto error; + } + + shader->gx2rBuffer.flags = GX2R_RESOURCE_BIND_SHADER_PROGRAM | GX2R_RESOURCE_USAGE_CPU_WRITE | GX2R_RESOURCE_USAGE_GPU_READ; + shader->gx2rBuffer.elemSize = programSize; + shader->gx2rBuffer.elemCount = 1; + shader->gx2rBuffer.buffer = NULL; + if (!GX2RCreateBuffer(&shader->gx2rBuffer)) { + goto error; + } + + program = GX2RLockBufferEx(&shader->gx2rBuffer, 0); + if (!GFDGetPixelShader(shader, program, index, file)) { + GX2RUnlockBufferEx(&shader->gx2rBuffer, GX2R_RESOURCE_DISABLE_CPU_INVALIDATE | GX2R_RESOURCE_DISABLE_GPU_INVALIDATE); + goto error; + } + + GX2RUnlockBufferEx(&shader->gx2rBuffer, 0); + return shader; + +error: + if (shader) { + if (shader->gx2rBuffer.buffer) { + GX2RDestroyBufferEx(&shader->gx2rBuffer, 0); + } + + GfxHeapFreeMEM2(shader); + } + + return NULL; +} + +BOOL +WHBGfxFreePixelShader(GX2PixelShader *shader) +{ + if (shader->gx2rBuffer.buffer) { + GX2RDestroyBufferEx(&shader->gx2rBuffer, 0); + } + + GfxHeapFreeMEM2(shader); +} + +GX2VertexShader * +WHBGfxLoadGFDVertexShader(uint32_t index, + const void *file) +{ + uint32_t headerSize, programSize; + GX2VertexShader *shader = NULL; + void *program = NULL; + + if (index >= GFDGetVertexShaderCount(file)) { + goto error; + } + + headerSize = GFDGetVertexShaderHeaderSize(index, file); + programSize = GFDGetVertexShaderProgramSize(index, file); + + if (!headerSize || !programSize) { + goto error; + } + + shader = (GX2VertexShader *)GfxHeapAllocMEM2(headerSize, 64); + if (!shader) { + goto error; + } + + shader->gx2rBuffer.flags = GX2R_RESOURCE_BIND_SHADER_PROGRAM | GX2R_RESOURCE_USAGE_CPU_WRITE | GX2R_RESOURCE_USAGE_GPU_READ; + shader->gx2rBuffer.elemSize = programSize; + shader->gx2rBuffer.elemCount = 1; + shader->gx2rBuffer.buffer = NULL; + if (!GX2RCreateBuffer(&shader->gx2rBuffer)) { + goto error; + } + + program = GX2RLockBufferEx(&shader->gx2rBuffer, 0); + if (!GFDGetVertexShader(shader, program, index, file)) { + GX2RUnlockBufferEx(&shader->gx2rBuffer, GX2R_RESOURCE_DISABLE_CPU_INVALIDATE | GX2R_RESOURCE_DISABLE_GPU_INVALIDATE); + goto error; + } + + GX2RUnlockBufferEx(&shader->gx2rBuffer, 0); + return shader; + +error: + if (shader) { + if (shader->gx2rBuffer.buffer) { + GX2RDestroyBufferEx(&shader->gx2rBuffer, 0); + } + + GfxHeapFreeMEM2(shader); + } + + return NULL; +} + +BOOL +WHBGfxFreeVertexShader(GX2VertexShader *shader) +{ + if (shader->gx2rBuffer.buffer) { + GX2RDestroyBufferEx(&shader->gx2rBuffer, 0); + } + + GfxHeapFreeMEM2(shader); +} + +BOOL +WHBGfxLoadGFDShaderGroup(WHBGfxShaderGroup *group, + uint32_t index, + const void *file) +{ + memset(group, 0, sizeof(WHBGfxShaderGroup)); + group->vertexShader = WHBGfxLoadGFDVertexShader(index, file); + group->pixelShader = WHBGfxLoadGFDPixelShader(index, file); + + if (!group->vertexShader || !group->pixelShader) { + // A shader group requires at least a vertex shader and a pixel shader. + WHBGfxFreeShaderGroup(group); + return FALSE; + } + + return TRUE; +} + +#define SQ_SEL_MASK(x, y, z, w) (((x) << 24) | ((y) << 16) | ((z) << 8) | (w)) + +static uint32_t +GfxGetAttribFormatSel(GX2AttribFormat format) +{ + switch (format) { + case GX2_ATTRIB_FORMAT_UNORM_8: + case GX2_ATTRIB_FORMAT_UINT_8: + case GX2_ATTRIB_FORMAT_SNORM_8: + case GX2_ATTRIB_FORMAT_SINT_8: + case GX2_ATTRIB_FORMAT_FLOAT_32: + return SQ_SEL_MASK(LATTE_SQ_SEL_X, LATTE_SQ_SEL_0, LATTE_SQ_SEL_0, LATTE_SQ_SEL_1); + case GX2_ATTRIB_FORMAT_UNORM_8_8: + case GX2_ATTRIB_FORMAT_UINT_8_8: + case GX2_ATTRIB_FORMAT_SNORM_8_8: + case GX2_ATTRIB_FORMAT_SINT_8_8: + case GX2_ATTRIB_FORMAT_FLOAT_32_32: + return SQ_SEL_MASK(LATTE_SQ_SEL_X, LATTE_SQ_SEL_Y, LATTE_SQ_SEL_0, LATTE_SQ_SEL_1); + case GX2_ATTRIB_FORMAT_FLOAT_32_32_32: + return SQ_SEL_MASK(LATTE_SQ_SEL_X, LATTE_SQ_SEL_Y, LATTE_SQ_SEL_Z, LATTE_SQ_SEL_1); + case GX2_ATTRIB_FORMAT_UNORM_8_8_8_8: + case GX2_ATTRIB_FORMAT_UINT_8_8_8_8: + case GX2_ATTRIB_FORMAT_SNORM_8_8_8_8: + case GX2_ATTRIB_FORMAT_SINT_8_8_8_8: + case GX2_ATTRIB_FORMAT_FLOAT_32_32_32_32: + return SQ_SEL_MASK(LATTE_SQ_SEL_X, LATTE_SQ_SEL_Y, LATTE_SQ_SEL_Z, LATTE_SQ_SEL_W); + break; + default: + return SQ_SEL_MASK(LATTE_SQ_SEL_0, LATTE_SQ_SEL_0, LATTE_SQ_SEL_0, LATTE_SQ_SEL_1); + } +} + +static int32_t +GfxGetVertexAttribVarLocation(const GX2VertexShader* shader, + const char *name) +{ + uint32_t i; + + for (i = 0; i < shader->attribVarCount; ++i) { + if (strcmp(shader->attribVars[i].name, name) == 0) { + return shader->attribVars[i].location; + } + } + + return -1; +} + +BOOL +WHBGfxInitShaderAttribute(WHBGfxShaderGroup *group, + const char *name, + uint32_t buffer, + uint32_t offset, + GX2AttribFormat format) +{ + GX2AttribStream *attrib; + int32_t location; + + location = GfxGetVertexAttribVarLocation(group->vertexShader, name); + if (location == -1) { + return FALSE; + } + + attrib = &group->attributes[group->numAttributes++]; + attrib->location = location; + attrib->buffer = buffer; + attrib->offset = offset; + attrib->format = format; + attrib->type = GX2_ATTRIB_INDEX_PER_VERTEX; + attrib->aluDivisor = 0; + attrib->mask = GfxGetAttribFormatSel(format); + attrib->endianSwap = GX2_ENDIAN_SWAP_DEFAULT; + return TRUE; +} + +BOOL +WHBGfxInitFetchShader(WHBGfxShaderGroup *group) +{ + uint32_t size = GX2CalcFetchShaderSizeEx(group->numAttributes, + GX2_FETCH_SHADER_TESSELLATION_NONE, + GX2_TESSELLATION_MODE_DISCRETE); + group->fetchShaderProgram = GfxHeapAllocMEM2(size, GX2_SHADER_PROGRAM_ALIGNMENT); + + GX2InitFetchShaderEx(&group->fetchShader, + group->fetchShaderProgram, + group->numAttributes, + group->attributes, + GX2_FETCH_SHADER_TESSELLATION_NONE, + GX2_TESSELLATION_MODE_DISCRETE); + + GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, group->fetchShaderProgram, size); + return TRUE; +} + +BOOL +WHBGfxFreeShaderGroup(WHBGfxShaderGroup *group) +{ + if (group->fetchShaderProgram) { + GfxHeapFreeMEM2(group->fetchShaderProgram); + group->fetchShaderProgram = NULL; + } + + if (group->pixelShader) { + WHBGfxFreePixelShader(group->pixelShader); + group->pixelShader = NULL; + } + + if (group->vertexShader) { + WHBGfxFreeVertexShader(group->vertexShader); + group->vertexShader = NULL; + } + + return TRUE; +} diff --git a/src/libwhb/src/proc.c b/src/libwhb/src/proc.c new file mode 100644 index 0000000..16ce7d6 --- /dev/null +++ b/src/libwhb/src/proc.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include + +static uint32_t +sMainCore; + +static BOOL +sRunning = FALSE; + +static ProcUICallback +sAcquireCallback = NULL; + +static uint32_t +ProcSaveCallback(void *context) +{ + OSSavesDone_ReadyToRelease(); + return 0; +} + +void +WHBProcInit() +{ + sMainCore = OSGetCoreId(); + sRunning = TRUE; + ProcUIInitEx(&ProcSaveCallback, NULL); +} + +void +WHBProcShutdown() +{ + sRunning = FALSE; +} + +void +WHBProcStopRunning() +{ + sRunning = FALSE; +} + +BOOL +WHBProcIsRunning() +{ + ProcUIStatus status; + + if (sMainCore != OSGetCoreId()) { + ProcUISubProcessMessages(TRUE); + return sRunning; + } + + status = ProcUIProcessMessages(TRUE); + if (status == PROCUI_STATUS_EXITING) { + WHBProcStopRunning(); + } else if (status == PROCUI_STATUS_RELEASE_FOREGROUND) { + ProcUIDrawDoneRelease(); + } + + if (!sRunning) { + GX2DrawDone(); + ProcUIShutdown(); + } + + return sRunning; +}