142 lines
4.8 KiB
C
142 lines
4.8 KiB
C
#include <switch.h>
|
|
#include <deko3d.h>
|
|
|
|
#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);
|
|
}
|