diff --git a/Makefile.nx b/Makefile.nx index fc7005e..df58fca 100644 --- a/Makefile.nx +++ b/Makefile.nx @@ -63,7 +63,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions ASFLAGS := -g $(ARCH) LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -LIBS := -lminizip `freetype-config --libs` -lconfig -lturbojpeg -lpng +LIBS := -ldeko3d -lminizip `freetype-config --libs` -lconfig -lturbojpeg -lpng #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing diff --git a/nx_main/main.c b/nx_main/main.c index 523aa21..b32470f 100644 --- a/nx_main/main.c +++ b/nx_main/main.c @@ -3,14 +3,13 @@ #include #include "../common/common.h" +#include "nx_graphics.h" #include "nx_touch.h" // Define the desired framebuffer resolution (here we set it to 720p). #define FB_WIDTH 1280 #define FB_HEIGHT 720 -Framebuffer g_framebufObj; - uint8_t* g_framebuf; u32 g_framebuf_width; @@ -120,8 +119,7 @@ int main(int argc, char **argv) if (errormsg[0]) error_screen = 1; if (!error_screen) { - framebufferCreate(&g_framebufObj, nwindowGetDefault(), FB_WIDTH, FB_HEIGHT, PIXEL_FORMAT_RGBA_8888, 2); - framebufferMakeLinear(&g_framebufObj); + graphicsInit(FB_WIDTH, FB_HEIGHT); } else { consoleInit(NULL); @@ -138,7 +136,7 @@ int main(int argc, char **argv) if (!error_screen) { if (!uiUpdate()) break; - g_framebuf = framebufferBegin(&g_framebufObj, &g_framebuf_width); + g_framebuf = graphicsFrameBegin(&g_framebuf_width); #ifdef PERF_LOG start_tick = armGetSystemTick(); #endif @@ -150,7 +148,7 @@ int main(int argc, char **argv) } if (!error_screen) { - framebufferEnd(&g_framebufObj); + graphicsFrameEnd(); #ifdef PERF_LOG g_tickdiff_frame = armGetSystemTick() - start_tick; @@ -162,7 +160,7 @@ int main(int argc, char **argv) } if (!error_screen) { - framebufferClose(&g_framebufObj); + graphicsExit(); } else { consoleExit(NULL); diff --git a/nx_main/nx_graphics.c b/nx_main/nx_graphics.c new file mode 100644 index 0000000..ce7e5f8 --- /dev/null +++ b/nx_main/nx_graphics.c @@ -0,0 +1,141 @@ +#include +#include + +#include "nx_graphics.h" + +#define FB_NUM 2 +#define CMDMEMSIZE 0x1000 + +static u32 s_fbWidth, s_fbHeight; + +static DkDevice s_device; +static DkMemBlock s_fbMemBlock, s_workMemBlock, s_cmdMemBlock; +static DkSwapchain s_swapchain; +static DkCmdBuf s_cmdBuf; +static DkCmdList s_cmdLists[FB_NUM]; +static DkFence s_fence; +static DkQueue s_queue; + +void graphicsInit(u32 width, u32 height) +{ + DkImageLayoutMaker imgLayoutMaker; + DkMemBlockMaker memBlockMaker; + + // Create the device, which is the root object + DkDeviceMaker deviceMaker; + dkDeviceMakerDefaults(&deviceMaker); + s_device = dkDeviceCreate(&deviceMaker); + + // Calculate layout for the framebuffers + DkImageLayout fbLayout; + dkImageLayoutMakerDefaults(&imgLayoutMaker, s_device); + imgLayoutMaker.flags = DkImageFlags_UsagePresent; + imgLayoutMaker.format = DkImageFormat_RGBA8_Unorm; + imgLayoutMaker.dimensions[0] = s_fbWidth = width; + imgLayoutMaker.dimensions[1] = s_fbHeight = height; + dkImageLayoutInitialize(&fbLayout, &imgLayoutMaker); + + // Retrieve necessary size and alignment for the framebuffers + uint32_t fbSize = dkImageLayoutGetSize(&fbLayout); + uint32_t fbAlign = dkImageLayoutGetAlignment(&fbLayout); + fbSize = (fbSize + fbAlign - 1) &~ (fbAlign - 1); + + // Create a memory block that will host the framebuffers + dkMemBlockMakerDefaults(&memBlockMaker, s_device, FB_NUM*fbSize); + memBlockMaker.flags = DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image; + s_fbMemBlock = dkMemBlockCreate(&memBlockMaker); + + // Initialize the framebuffers with the layout and backing memory we've just created + DkImage fbImages[FB_NUM]; + DkImage const* swapchainImages[FB_NUM]; + for (unsigned i = 0; i < FB_NUM; i ++) + { + swapchainImages[i] = &fbImages[i]; + dkImageInitialize(&fbImages[i], &fbLayout, s_fbMemBlock, i*fbSize); + } + + // Create a swapchain out of the framebuffers we've just initialized + DkSwapchainMaker swapchainMaker; + dkSwapchainMakerDefaults(&swapchainMaker, s_device, nwindowGetDefault(), swapchainImages, FB_NUM); + s_swapchain = dkSwapchainCreate(&swapchainMaker); + + // Create a memory block for the linear framebuffer + dkMemBlockMakerDefaults(&memBlockMaker, s_device, width*height*4); + memBlockMaker.flags = DkMemBlockFlags_CpuCached | DkMemBlockFlags_GpuUncached; + s_workMemBlock = dkMemBlockCreate(&memBlockMaker); + + // Create a memory block for the command lists + dkMemBlockMakerDefaults(&memBlockMaker, s_device, CMDMEMSIZE); + memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached; + s_cmdMemBlock = dkMemBlockCreate(&memBlockMaker); + + // Create a command buffer + DkCmdBufMaker cmdBufMaker; + dkCmdBufMakerDefaults(&cmdBufMaker, s_device); + s_cmdBuf = dkCmdBufCreate(&cmdBufMaker); + dkCmdBufAddMemory(s_cmdBuf, s_cmdMemBlock, 0, CMDMEMSIZE); + + // Define source for linear framebuffer copies + const DkCopyBuf linearSrc = { + .addr = dkMemBlockGetGpuAddr(s_workMemBlock), + .rowLength = 0, + .imageHeight = 0, + }; + + // Define rectangle for the copies + const DkImageRect copyRect = { + .x = 0, .y = 0, .z = 0, + .width = width, .height = height, .depth = 1, + }; + + // Record command lists for the copies + for (unsigned i = 0; i < FB_NUM; i ++) { + DkImageView tiledDst; + dkImageViewDefaults(&tiledDst, &fbImages[i]); + dkCmdBufCopyBufferToImage(s_cmdBuf, &linearSrc, &tiledDst, ©Rect, 0); + dkCmdBufSignalFence(s_cmdBuf, &s_fence, false); + s_cmdLists[i] = dkCmdBufFinishList(s_cmdBuf); + } + + // Create a queue, to which we will submit our command lists + DkQueueMaker queueMaker; + dkQueueMakerDefaults(&queueMaker, s_device); + queueMaker.flags = 0; // we will only use this queue for transferring + s_queue = dkQueueCreate(&queueMaker); +} + +void graphicsExit(void) +{ + // Make sure the queue is idle before destroying anything + dkQueueWaitIdle(s_queue); + + // Destroy all the resources we've created + dkQueueDestroy(s_queue); + dkCmdBufDestroy(s_cmdBuf); + dkMemBlockDestroy(s_cmdMemBlock); + dkMemBlockDestroy(s_workMemBlock); + dkSwapchainDestroy(s_swapchain); + dkMemBlockDestroy(s_fbMemBlock); + dkDeviceDestroy(s_device); +} + +void* graphicsFrameBegin(u32* out_stride) +{ + // Ensure the GPU is not reading from the framebuffer + dkFenceWait(&s_fence, -1); + + // Return information + if (out_stride) *out_stride = s_fbWidth*4; + return dkMemBlockGetCpuAddr(s_workMemBlock); +} + +void graphicsFrameEnd(void) +{ + // Flush the linear framebuffer + dkMemBlockFlushCpuCache(s_workMemBlock, 0, s_fbWidth*s_fbHeight*4); + + // Present a frame + int slot = dkQueueAcquireImage(s_queue, s_swapchain); + dkQueueSubmitCommands(s_queue, s_cmdLists[slot]); + dkQueuePresentImage(s_queue, s_swapchain, slot); +} diff --git a/nx_main/nx_graphics.h b/nx_main/nx_graphics.h new file mode 100644 index 0000000..1ad5d4c --- /dev/null +++ b/nx_main/nx_graphics.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +void graphicsInit(u32 width, u32 height); +void graphicsExit(void); + +void* graphicsFrameBegin(u32* out_stride); +void graphicsFrameEnd(void);