mirror of
https://github.com/switchbrew/switch-examples.git
synced 2025-06-20 21:12:38 +02:00
227 lines
8.4 KiB
C
227 lines
8.4 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <switch.h>
|
|
#include <deko3d.h>
|
|
|
|
// Define the desired number of framebuffers
|
|
#define FB_NUM 2
|
|
|
|
// Define the desired framebuffer resolution (here we set it to 720p).
|
|
#define FB_WIDTH 1280
|
|
#define FB_HEIGHT 720
|
|
|
|
// Remove above and uncomment below for 1080p
|
|
//#define FB_WIDTH 1920
|
|
//#define FB_HEIGHT 1080
|
|
|
|
// Define the size of the memory block that will hold code
|
|
#define CODEMEMSIZE (64*1024)
|
|
|
|
// Define the size of the memory block that will hold command lists
|
|
#define CMDMEMSIZE (16*1024)
|
|
|
|
static DkDevice g_device;
|
|
static DkMemBlock g_framebufferMemBlock;
|
|
static DkImage g_framebuffers[FB_NUM];
|
|
static DkSwapchain g_swapchain;
|
|
|
|
static DkMemBlock g_codeMemBlock;
|
|
static uint32_t g_codeMemOffset;
|
|
static DkShader g_vertexShader;
|
|
static DkShader g_fragmentShader;
|
|
|
|
static DkMemBlock g_cmdbufMemBlock;
|
|
static DkCmdBuf g_cmdbuf;
|
|
static DkCmdList g_cmdsBindFramebuffer[FB_NUM];
|
|
static DkCmdList g_cmdsRender;
|
|
|
|
static DkQueue g_renderQueue;
|
|
|
|
// Simple function for loading a shader from the filesystem
|
|
static void loadShader(DkShader* pShader, const char* path)
|
|
{
|
|
// Open the file, and retrieve its size
|
|
FILE* f = fopen(path, "rb");
|
|
fseek(f, 0, SEEK_END);
|
|
uint32_t size = ftell(f);
|
|
rewind(f);
|
|
|
|
// Look for a spot in the code memory block for loading this shader. Note that
|
|
// we are just using a simple incremental offset; this isn't a general purpose
|
|
// allocation algorithm.
|
|
uint32_t codeOffset = g_codeMemOffset;
|
|
g_codeMemOffset += (size + DK_SHADER_CODE_ALIGNMENT - 1) &~ (DK_SHADER_CODE_ALIGNMENT - 1);
|
|
|
|
// Read the file into memory, and close the file
|
|
fread((uint8_t*)dkMemBlockGetCpuAddr(g_codeMemBlock) + codeOffset, size, 1, f);
|
|
fclose(f);
|
|
|
|
// Initialize the user provided shader object with the code we've just loaded
|
|
DkShaderMaker shaderMaker;
|
|
dkShaderMakerDefaults(&shaderMaker, g_codeMemBlock, codeOffset);
|
|
dkShaderInitialize(pShader, &shaderMaker);
|
|
}
|
|
|
|
// This function creates all the necessary graphical resources.
|
|
static void graphicsInitialize(void)
|
|
{
|
|
// Create the device, which is the root object
|
|
DkDeviceMaker deviceMaker;
|
|
dkDeviceMakerDefaults(&deviceMaker);
|
|
g_device = dkDeviceCreate(&deviceMaker);
|
|
|
|
// Calculate layout for the framebuffers
|
|
DkImageLayoutMaker imageLayoutMaker;
|
|
dkImageLayoutMakerDefaults(&imageLayoutMaker, g_device);
|
|
imageLayoutMaker.flags = DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression;
|
|
imageLayoutMaker.format = DkImageFormat_RGBA8_Unorm;
|
|
imageLayoutMaker.dimensions[0] = FB_WIDTH;
|
|
imageLayoutMaker.dimensions[1] = FB_HEIGHT;
|
|
|
|
// Calculate layout for the framebuffers
|
|
DkImageLayout framebufferLayout;
|
|
dkImageLayoutInitialize(&framebufferLayout, &imageLayoutMaker);
|
|
|
|
// Retrieve necessary size and alignment for the framebuffers
|
|
uint32_t framebufferSize = dkImageLayoutGetSize(&framebufferLayout);
|
|
uint32_t framebufferAlign = dkImageLayoutGetAlignment(&framebufferLayout);
|
|
framebufferSize = (framebufferSize + framebufferAlign - 1) &~ (framebufferAlign - 1);
|
|
|
|
// Create a memory block that will host the framebuffers
|
|
DkMemBlockMaker memBlockMaker;
|
|
dkMemBlockMakerDefaults(&memBlockMaker, g_device, FB_NUM*framebufferSize);
|
|
memBlockMaker.flags = DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image;
|
|
g_framebufferMemBlock = dkMemBlockCreate(&memBlockMaker);
|
|
|
|
// Initialize the framebuffers with the layout and backing memory we've just created
|
|
DkImage const* swapchainImages[FB_NUM];
|
|
for (unsigned i = 0; i < FB_NUM; i ++)
|
|
{
|
|
swapchainImages[i] = &g_framebuffers[i];
|
|
dkImageInitialize(&g_framebuffers[i], &framebufferLayout, g_framebufferMemBlock, i*framebufferSize);
|
|
}
|
|
|
|
// Create a swapchain out of the framebuffers we've just initialized
|
|
DkSwapchainMaker swapchainMaker;
|
|
dkSwapchainMakerDefaults(&swapchainMaker, g_device, nwindowGetDefault(), swapchainImages, FB_NUM);
|
|
g_swapchain = dkSwapchainCreate(&swapchainMaker);
|
|
|
|
// Create a memory block onto which we will load shader code
|
|
dkMemBlockMakerDefaults(&memBlockMaker, g_device, CODEMEMSIZE);
|
|
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code;
|
|
g_codeMemBlock = dkMemBlockCreate(&memBlockMaker);
|
|
g_codeMemOffset = 0;
|
|
|
|
// Load our shaders (both vertex and fragment)
|
|
loadShader(&g_vertexShader, "romfs:/shaders/triangle_vsh.dksh");
|
|
loadShader(&g_fragmentShader, "romfs:/shaders/color_fsh.dksh");
|
|
|
|
// Create a memory block which will be used for recording command lists using a command buffer
|
|
dkMemBlockMakerDefaults(&memBlockMaker, g_device, CMDMEMSIZE);
|
|
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached;
|
|
g_cmdbufMemBlock = dkMemBlockCreate(&memBlockMaker);
|
|
|
|
// Create a command buffer object
|
|
DkCmdBufMaker cmdbufMaker;
|
|
dkCmdBufMakerDefaults(&cmdbufMaker, g_device);
|
|
g_cmdbuf = dkCmdBufCreate(&cmdbufMaker);
|
|
|
|
// Feed our memory to the command buffer so that we can start recording commands
|
|
dkCmdBufAddMemory(g_cmdbuf, g_cmdbufMemBlock, 0, CMDMEMSIZE);
|
|
|
|
// Generate a command list for each framebuffer, which will bind each of them as a render target
|
|
for (unsigned i = 0; i < FB_NUM; i ++)
|
|
{
|
|
DkImageView imageView;
|
|
dkImageViewDefaults(&imageView, &g_framebuffers[i]);
|
|
dkCmdBufBindRenderTarget(g_cmdbuf, &imageView, NULL);
|
|
g_cmdsBindFramebuffer[i] = dkCmdBufFinishList(g_cmdbuf);
|
|
}
|
|
|
|
// Declare structs that will be used for binding state
|
|
DkViewport viewport = { 0.0f, 0.0f, (float)FB_WIDTH, (float)FB_HEIGHT, 0.0f, 1.0f };
|
|
DkScissor scissor = { 0, 0, FB_WIDTH, FB_HEIGHT };
|
|
DkShader const* shaders[] = { &g_vertexShader, &g_fragmentShader };
|
|
DkRasterizerState rasterizerState;
|
|
DkColorState colorState;
|
|
DkColorWriteState colorWriteState;
|
|
|
|
// Initialize state structs with the deko3d defaults
|
|
dkRasterizerStateDefaults(&rasterizerState);
|
|
dkColorStateDefaults(&colorState);
|
|
dkColorWriteStateDefaults(&colorWriteState);
|
|
|
|
// Generate the main rendering command list
|
|
dkCmdBufSetViewports(g_cmdbuf, 0, &viewport, 1);
|
|
dkCmdBufSetScissors(g_cmdbuf, 0, &scissor, 1);
|
|
dkCmdBufClearColorFloat(g_cmdbuf, 0, DkColorMask_RGBA, 0.125f, 0.294f, 0.478f, 1.0f);
|
|
dkCmdBufBindShaders(g_cmdbuf, DkStageFlag_GraphicsMask, shaders, sizeof(shaders)/sizeof(shaders[0]));
|
|
dkCmdBufBindRasterizerState(g_cmdbuf, &rasterizerState);
|
|
dkCmdBufBindColorState(g_cmdbuf, &colorState);
|
|
dkCmdBufBindColorWriteState(g_cmdbuf, &colorWriteState);
|
|
dkCmdBufDraw(g_cmdbuf, DkPrimitive_Triangles, 3, 1, 0, 0);
|
|
g_cmdsRender = dkCmdBufFinishList(g_cmdbuf);
|
|
|
|
// Create a queue, to which we will submit our command lists
|
|
DkQueueMaker queueMaker;
|
|
dkQueueMakerDefaults(&queueMaker, g_device);
|
|
queueMaker.flags = DkQueueFlags_Graphics;
|
|
g_renderQueue = dkQueueCreate(&queueMaker);
|
|
}
|
|
|
|
// This function is to be called at each frame, and it is in charge of rendering.
|
|
static void graphicsUpdate(void)
|
|
{
|
|
// Acquire a framebuffer from the swapchain (and wait for it to be available)
|
|
int slot = dkQueueAcquireImage(g_renderQueue, g_swapchain);
|
|
|
|
// Run the command list that binds said framebuffer as a render target
|
|
dkQueueSubmitCommands(g_renderQueue, g_cmdsBindFramebuffer[slot]);
|
|
|
|
// Run the main rendering command list
|
|
dkQueueSubmitCommands(g_renderQueue, g_cmdsRender);
|
|
|
|
// Now that we are done rendering, present it to the screen
|
|
dkQueuePresentImage(g_renderQueue, g_swapchain, slot);
|
|
}
|
|
|
|
// This function destroys the graphical resources created by graphicsInitialize.
|
|
static void graphicsExit(void)
|
|
{
|
|
// Make sure the rendering queue is idle before destroying anything
|
|
dkQueueWaitIdle(g_renderQueue);
|
|
|
|
// Destroy all the resources we've created
|
|
dkQueueDestroy(g_renderQueue);
|
|
dkCmdBufDestroy(g_cmdbuf);
|
|
dkMemBlockDestroy(g_cmdbufMemBlock);
|
|
dkMemBlockDestroy(g_codeMemBlock);
|
|
dkSwapchainDestroy(g_swapchain);
|
|
dkMemBlockDestroy(g_framebufferMemBlock);
|
|
dkDeviceDestroy(g_device);
|
|
}
|
|
|
|
// Main entrypoint
|
|
int main(int argc, char* argv[])
|
|
{
|
|
romfsInit();
|
|
graphicsInitialize();
|
|
|
|
while (appletMainLoop())
|
|
{
|
|
hidScanInput();
|
|
|
|
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
|
if (kDown & KEY_PLUS)
|
|
break; // break in order to return to hbmenu
|
|
|
|
graphicsUpdate();
|
|
}
|
|
|
|
graphicsExit();
|
|
romfsExit();
|
|
return 0;
|
|
}
|