chore: update template for PL changes

This commit is contained in:
Jonathan Hoffstadt 2025-04-11 21:55:51 -05:00
parent afc696a894
commit 5aa4be8b9c

639
src/app.c
View File

@ -1,20 +1,64 @@
/* /*
app.c example_basic_2.c
- template app - demonstrates loading APIs
- loads only stable APIs - demonstrates loading extensions
- demonstrates hot reloading
- demonstrates starter extension
- demonstrates basic drawing extension (2D)
- demonstrates basic screen log extension
- demonstrates basic console extension
- demonstrates basic UI extension
*/ */
/* /*
Index of this file: Index of this file:
// [SECTION] quick notes
// [SECTION] includes // [SECTION] includes
// [SECTION] structs // [SECTION] structs
// [SECTION] apis // [SECTION] apis
// [SECTION] helper functions forward declarations
// [SECTION] pl_app_load // [SECTION] pl_app_load
// [SECTION] pl_app_shutdown // [SECTION] pl_app_shutdown
// [SECTION] pl_app_resize // [SECTION] pl_app_resize
// [SECTION] pl_app_update // [SECTION] pl_app_update
// [SECTION] helper functions implementations */
//-----------------------------------------------------------------------------
// [SECTION] quick notes
//-----------------------------------------------------------------------------
/*
This example is the first to introduce extensions. Extensions are just
shared libraries that export a "load" and "unload" function. The default
being:
* void pl_load_ext (plApiRegistryI*, bool reload)
* void pl_unload_ext(plApiRegistryI*, bool reload)
Later examples will explain more about the details of creating an extension
but the important thing to understand now is that an extension just provides
an implementation of an API. The "load" function allows an extension to
request APIs it depends on and to register any API it provides. The unload
function just allows an extension the opportunity to unregister and perform
any required cleanup.
This example is also the first to introduce the "starter" extension. This
extension acts a bit as a helper extension to remove some common boilerplate
but is also just useful in general for most applications only needing to use
UI, plotting, drawing, etc. Or even to just experiment with the lower level
graphics extension in an isolated manner. Later examples will gradually peel
away at this extension and others. For this example, we will just demonstrate
some of the smaller helpful extensions. These include:
* log
* profile
* stat
* console
* screen log
* ui
This will be very light introductions with later examples going into more
detail. Feel free to open the header file for the extension for more
information and functionality.
*/ */
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -24,32 +68,22 @@ Index of this file:
#include <stdlib.h> // malloc, free #include <stdlib.h> // malloc, free
#include <string.h> // memset #include <string.h> // memset
#include "pl.h" #include "pl.h"
#include "pl_memory.h"
#define PL_MATH_INCLUDE_FUNCTIONS #define PL_MATH_INCLUDE_FUNCTIONS // required to expose some of the color helpers
#include "pl_math.h" #include "pl_math.h"
// extensions // extensions
#include "pl_log_ext.h"
#include "pl_window_ext.h" #include "pl_window_ext.h"
#include "pl_shader_ext.h"
#include "pl_draw_ext.h" #include "pl_draw_ext.h"
#include "pl_starter_ext.h"
#include "pl_ui_ext.h" #include "pl_ui_ext.h"
#include "pl_graphics_ext.h"
#include "pl_draw_backend_ext.h"
#include "pl_profile_ext.h"
#include "pl_stats_ext.h"
#include "pl_job_ext.h"
#include "pl_string_intern_ext.h"
#include "pl_library_ext.h"
#include "pl_rect_pack_ext.h"
#include "pl_gpu_allocators_ext.h"
#include "pl_image_ext.h"
#include "pl_console_ext.h"
#include "pl_screen_log_ext.h" #include "pl_screen_log_ext.h"
#include "pl_tools_ext.h" #include "pl_profile_ext.h"
#include "pl_platform_ext.h" #include "pl_log_ext.h"
#include "pl_stats_ext.h"
#include "pl_console_ext.h"
// example extensions // out extension
#include "pl_example_ext.h" #include "pl_example_ext.h"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -61,83 +95,34 @@ typedef struct _plAppData
// window // window
plWindow* ptWindow; plWindow* ptWindow;
// drawing stuff // log channel
plDrawList2D* ptDrawlist; uint64_t uExampleLogChannel;
plDrawLayer2D* ptFGLayer;
plDrawLayer2D* ptBGLayer;
plFont* ptDefaultFont;
// ui options
bool bShowUiDebug;
bool bShowUiStyle;
bool* pbShowDeviceMemoryAnalyzer;
bool* pbShowMemoryAllocations;
bool* pbShowProfiling;
bool* pbShowStats;
bool* pbShowLogging;
// graphics & sync objects
plDevice* ptDevice;
plSurface* ptSurface;
plSwapchain* ptSwapchain;
plTimelineSemaphore* aptSemaphores[PL_MAX_FRAMES_IN_FLIGHT];
uint64_t aulNextTimelineValue[PL_MAX_FRAMES_IN_FLIGHT];
plCommandPool* atCmdPools[PL_MAX_FRAMES_IN_FLIGHT];
plRenderPassHandle tMainRenderPass;
plRenderPassLayoutHandle tMainRenderPassLayout;
// console variable
bool bShowHelpWindow;
} plAppData; } plAppData;
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] apis // [SECTION] apis
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
const plDataRegistryI* gptDataRegistry = NULL;
const plMemoryI* gptMemory = NULL;
const plIOI* gptIO = NULL; const plIOI* gptIO = NULL;
const plWindowI* gptWindows = NULL; const plWindowI* gptWindows = NULL;
const plGraphicsI* gptGfx = NULL;
const plDrawI* gptDraw = NULL; const plDrawI* gptDraw = NULL;
const plUiI* gptUi = NULL; const plStarterI* gptStarter = NULL;
const plShaderI* gptShader = NULL; const plUiI* gptUI = NULL;
const plDrawBackendI* gptDrawBackend = NULL;
const plProfileI* gptProfile = NULL;
const plExampleI* gptExample = NULL;
const plStatsI* gptStats = NULL;
const plToolsI* gptTools = NULL;
const plImageI* gptImage = NULL;
const plGPUAllocatorsI* gptGpuAllocators = NULL;
const plJobI* gptJob = NULL;
const plThreadsI* gptThreads = NULL;
const plAtomicsI* gptAtomics = NULL;
const plRectPackI* gptRect = NULL;
const plFileI* gptFile = NULL;
const plNetworkI* gptNetwork = NULL;
const plStringInternI* gptString = NULL;
const plLibraryI* gptLibrary = NULL;
const plLogI* gptLog = NULL;
const plVirtualMemoryI* gptVirtualMemory = NULL;
const plConsoleI* gptConsole = NULL;
const plScreenLogI* gptScreenLog = NULL; const plScreenLogI* gptScreenLog = NULL;
const plProfileI* gptProfile = NULL;
const plStatsI* gptStats = NULL;
const plMemoryI* gptMemory = NULL;
const plLogI* gptLog = NULL;
const plConsoleI* gptConsole = NULL;
const plExampleI* gptExample = NULL;
// helpers
#define PL_ALLOC(x) gptMemory->tracked_realloc(NULL, (x), __FILE__, __LINE__) #define PL_ALLOC(x) gptMemory->tracked_realloc(NULL, (x), __FILE__, __LINE__)
#define PL_REALLOC(x, y) gptMemory->tracked_realloc((x), (y), __FILE__, __LINE__) #define PL_REALLOC(x, y) gptMemory->tracked_realloc((x), (y), __FILE__, __LINE__)
#define PL_FREE(x) gptMemory->tracked_realloc((x), 0, __FILE__, __LINE__) #define PL_FREE(x) gptMemory->tracked_realloc((x), 0, __FILE__, __LINE__)
#define PL_DS_ALLOC(x) gptMemory->tracked_realloc(NULL, (x), __FILE__, __LINE__)
#define PL_DS_ALLOC_INDIRECT(x, FILE, LINE) gptMemory->tracked_realloc(NULL, (x), FILE, LINE)
#define PL_DS_FREE(x) gptMemory->tracked_realloc((x), 0, __FILE__, __LINE__)
#include "pl_ds.h"
//-----------------------------------------------------------------------------
// [SECTION] helper functions forward declarations
//-----------------------------------------------------------------------------
void pl__setup_graphics_extensions (plAppData*);
void pl__resize_graphics_extensions(plAppData*);
void pl__resize_graphics_extensions(plAppData*);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] pl_app_load // [SECTION] pl_app_load
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -145,13 +130,9 @@ void pl__resize_graphics_extensions(plAppData*);
PL_EXPORT void* PL_EXPORT void*
pl_app_load(plApiRegistryI* ptApiRegistry, plAppData* ptAppData) pl_app_load(plApiRegistryI* ptApiRegistry, plAppData* ptAppData)
{ {
// NOTE: on first load, "ptAppData" will be NULL but on reloads // NOTE: on first load, "pAppData" will be NULL but on reloads
// it will be the value returned from this function // it will be the value returned from this function
// retrieve the data registry API, this is the API used for sharing data
// between extensions & the runtime
gptDataRegistry = pl_get_api_latest(ptApiRegistry, plDataRegistryI);
// if "ptAppData" is a valid pointer, then this function is being called // if "ptAppData" is a valid pointer, then this function is being called
// during a hot reload. // during a hot reload.
if(ptAppData) if(ptAppData)
@ -159,140 +140,85 @@ pl_app_load(plApiRegistryI* ptApiRegistry, plAppData* ptAppData)
// re-retrieve the apis since we are now in // re-retrieve the apis since we are now in
// a different dll/so // a different dll/so
gptMemory = pl_get_api_latest(ptApiRegistry, plMemoryI);
gptIO = pl_get_api_latest(ptApiRegistry, plIOI); gptIO = pl_get_api_latest(ptApiRegistry, plIOI);
gptWindows = pl_get_api_latest(ptApiRegistry, plWindowI); gptWindows = pl_get_api_latest(ptApiRegistry, plWindowI);
gptGfx = pl_get_api_latest(ptApiRegistry, plGraphicsI);
gptDraw = pl_get_api_latest(ptApiRegistry, plDrawI); gptDraw = pl_get_api_latest(ptApiRegistry, plDrawI);
gptShader = pl_get_api_latest(ptApiRegistry, plShaderI); gptStarter = pl_get_api_latest(ptApiRegistry, plStarterI);
gptDrawBackend = pl_get_api_latest(ptApiRegistry, plDrawBackendI); gptUI = pl_get_api_latest(ptApiRegistry, plUiI);
gptUi = pl_get_api_latest(ptApiRegistry, plUiI); gptScreenLog = pl_get_api_latest(ptApiRegistry, plScreenLogI);
gptProfile = pl_get_api_latest(ptApiRegistry, plProfileI); gptProfile = pl_get_api_latest(ptApiRegistry, plProfileI);
gptStats = pl_get_api_latest(ptApiRegistry, plStatsI); gptStats = pl_get_api_latest(ptApiRegistry, plStatsI);
gptExample = pl_get_api_latest(ptApiRegistry, plExampleI); gptMemory = pl_get_api_latest(ptApiRegistry, plMemoryI);
gptTools = pl_get_api_latest(ptApiRegistry, plToolsI);
gptImage = pl_get_api_latest(ptApiRegistry, plImageI);
gptGpuAllocators = pl_get_api_latest(ptApiRegistry, plGPUAllocatorsI);
gptJob = pl_get_api_latest(ptApiRegistry, plJobI);
gptThreads = pl_get_api_latest(ptApiRegistry, plThreadsI);
gptAtomics = pl_get_api_latest(ptApiRegistry, plAtomicsI);
gptRect = pl_get_api_latest(ptApiRegistry, plRectPackI);
gptFile = pl_get_api_latest(ptApiRegistry, plFileI);
gptNetwork = pl_get_api_latest(ptApiRegistry, plNetworkI);
gptString = pl_get_api_latest(ptApiRegistry, plStringInternI);
gptLibrary = pl_get_api_latest(ptApiRegistry, plLibraryI);
gptLog = pl_get_api_latest(ptApiRegistry, plLogI); gptLog = pl_get_api_latest(ptApiRegistry, plLogI);
gptVirtualMemory = pl_get_api_latest(ptApiRegistry, plVirtualMemoryI);
gptConsole = pl_get_api_latest(ptApiRegistry, plConsoleI); gptConsole = pl_get_api_latest(ptApiRegistry, plConsoleI);
gptScreenLog = pl_get_api_latest(ptApiRegistry, plScreenLogI); gptExample = pl_get_api_latest(ptApiRegistry, plExampleI);
return ptAppData; return ptAppData;
} }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~apis & extensions~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// retrieve extension registry // retrieve extension registry
const plExtensionRegistryI* ptExtensionRegistry = pl_get_api_latest(ptApiRegistry, plExtensionRegistryI); const plExtensionRegistryI* ptExtensionRegistry = pl_get_api_latest(ptApiRegistry, plExtensionRegistryI);
// load extensions // load extensions
ptExtensionRegistry->load("pl_unity_ext", NULL, NULL, false); // * first argument is the shared library name WITHOUT the extension
ptExtensionRegistry->load("pl_platform_ext", NULL, NULL, false); // * second & third argument is the load/unload functions names (use NULL for the default of "pl_load_ext" &
// "pl_unload_ext")
// * fourth argument indicates if the extension is reloadable (should we check for changes and reload if changed)
ptExtensionRegistry->load("pl_unity_ext", NULL, NULL, true);
ptExtensionRegistry->load("pl_platform_ext", NULL, NULL, false); // provides the file API used by the drawing ext
ptExtensionRegistry->load("pl_example_ext", NULL, NULL, true); ptExtensionRegistry->load("pl_example_ext", NULL, NULL, true);
// load apis // load required apis
gptMemory = pl_get_api_latest(ptApiRegistry, plMemoryI);
gptIO = pl_get_api_latest(ptApiRegistry, plIOI); gptIO = pl_get_api_latest(ptApiRegistry, plIOI);
gptWindows = pl_get_api_latest(ptApiRegistry, plWindowI); gptWindows = pl_get_api_latest(ptApiRegistry, plWindowI);
gptGfx = pl_get_api_latest(ptApiRegistry, plGraphicsI);
// load required apis (these are provided though extensions)
gptDraw = pl_get_api_latest(ptApiRegistry, plDrawI); gptDraw = pl_get_api_latest(ptApiRegistry, plDrawI);
gptShader = pl_get_api_latest(ptApiRegistry, plShaderI); gptStarter = pl_get_api_latest(ptApiRegistry, plStarterI);
gptDrawBackend = pl_get_api_latest(ptApiRegistry, plDrawBackendI); gptUI = pl_get_api_latest(ptApiRegistry, plUiI);
gptUi = pl_get_api_latest(ptApiRegistry, plUiI); gptScreenLog = pl_get_api_latest(ptApiRegistry, plScreenLogI);
gptProfile = pl_get_api_latest(ptApiRegistry, plProfileI); gptProfile = pl_get_api_latest(ptApiRegistry, plProfileI);
gptStats = pl_get_api_latest(ptApiRegistry, plStatsI); gptStats = pl_get_api_latest(ptApiRegistry, plStatsI);
gptExample = pl_get_api_latest(ptApiRegistry, plExampleI); gptMemory = pl_get_api_latest(ptApiRegistry, plMemoryI);
gptTools = pl_get_api_latest(ptApiRegistry, plToolsI);
gptImage = pl_get_api_latest(ptApiRegistry, plImageI);
gptGpuAllocators = pl_get_api_latest(ptApiRegistry, plGPUAllocatorsI);
gptJob = pl_get_api_latest(ptApiRegistry, plJobI);
gptThreads = pl_get_api_latest(ptApiRegistry, plThreadsI);
gptAtomics = pl_get_api_latest(ptApiRegistry, plAtomicsI);
gptRect = pl_get_api_latest(ptApiRegistry, plRectPackI);
gptFile = pl_get_api_latest(ptApiRegistry, plFileI);
gptNetwork = pl_get_api_latest(ptApiRegistry, plNetworkI);
gptString = pl_get_api_latest(ptApiRegistry, plStringInternI);
gptLibrary = pl_get_api_latest(ptApiRegistry, plLibraryI);
gptLog = pl_get_api_latest(ptApiRegistry, plLogI); gptLog = pl_get_api_latest(ptApiRegistry, plLogI);
gptVirtualMemory = pl_get_api_latest(ptApiRegistry, plVirtualMemoryI);
gptConsole = pl_get_api_latest(ptApiRegistry, plConsoleI); gptConsole = pl_get_api_latest(ptApiRegistry, plConsoleI);
gptScreenLog = pl_get_api_latest(ptApiRegistry, plScreenLogI);
// out extension
gptExample = pl_get_api_latest(ptApiRegistry, plExampleI);
gptExample->print_to_console("Hello from example extension");
// this path is taken only during first load, so we // this path is taken only during first load, so we
// allocate app memory here // allocate app memory here
ptAppData = PL_ALLOC(sizeof(plAppData)); ptAppData = PL_ALLOC(sizeof(plAppData));
memset(ptAppData, 0, sizeof(plAppData)); memset(ptAppData, 0, sizeof(plAppData));
// add console variables // default values
gptConsole->initialize((plConsoleSettings){.tFlags = PL_CONSOLE_FLAGS_POPUP}); ptAppData->bShowHelpWindow = true;
// use window API to create a window // use window API to create a window
plWindowDesc tWindowDesc = { plWindowDesc tWindowDesc = {
.pcTitle = "App Template", .pcTitle = "Template App",
.iXPos = 200, .iXPos = 200,
.iYPos = 200, .iYPos = 200,
.uWidth = 500, .uWidth = 600,
.uHeight = 500, .uHeight = 600,
}; };
gptWindows->create_window(tWindowDesc, &ptAppData->ptWindow); gptWindows->create_window(tWindowDesc, &ptAppData->ptWindow);
// setup graphics extension // initialize the starter API (handles alot of boilerplate)
pl__setup_graphics_extensions(ptAppData); plStarterInit tStarterInit = {
.tFlags = PL_STARTER_FLAGS_ALL_EXTENSIONS,
.ptWindow = ptAppData->ptWindow
};
gptStarter->initialize(tStarterInit);
gptStarter->finalize();
// initialize APIs that require it // add a log channel
gptTools->initialize((plToolsInit){.ptDevice = ptAppData->ptDevice}); ptAppData->uExampleLogChannel = gptLog->add_channel("Example 2", (plLogExtChannelInit){.tType = PL_LOG_CHANNEL_TYPE_BUFFER});
// retrieve some console variables // add a console variable
ptAppData->pbShowLogging = (bool*)gptConsole->get_variable("t.LogTool", NULL, NULL); gptConsole->add_toggle_variable("a.HelpWindow", &ptAppData->bShowHelpWindow, "toggle help window", PL_CONSOLE_VARIABLE_FLAGS_NONE);
ptAppData->pbShowStats = (bool*)gptConsole->get_variable("t.StatTool", NULL, NULL);
ptAppData->pbShowProfiling = (bool*)gptConsole->get_variable("t.ProfileTool", NULL, NULL);
ptAppData->pbShowMemoryAllocations = (bool*)gptConsole->get_variable("t.MemoryAllocationTool", NULL, NULL);
ptAppData->pbShowDeviceMemoryAnalyzer = (bool*)gptConsole->get_variable("t.DeviceMemoryAnalyzerTool", NULL, NULL);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~setup draw extensions~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// initialize
gptDraw->initialize(NULL);
gptDrawBackend->initialize(ptAppData->ptDevice);
// create font atlas
plFontAtlas* ptAtlas = gptDraw->create_font_atlas();
gptDraw->set_font_atlas(ptAtlas);
ptAppData->ptDefaultFont = gptDraw->add_default_font(ptAtlas);
// build font atlas
plCommandPool* ptCmdPool = ptAppData->atCmdPools[gptGfx->get_current_frame_index()];
plCommandBuffer* ptCmdBuffer = gptGfx->request_command_buffer(ptCmdPool);
gptDrawBackend->build_font_atlas(ptCmdBuffer, ptAtlas);
gptGfx->return_command_buffer(ptCmdBuffer);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~message extension~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gptScreenLog->initialize((plScreenLogSettings){.ptFont = ptAppData->ptDefaultFont});
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ui extension~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gptUi->initialize();
gptUi->set_default_font(ptAppData->ptDefaultFont);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~app stuff~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// create drawlist and some layers to draw to
ptAppData->ptDrawlist = gptDraw->request_2d_drawlist();
ptAppData->ptFGLayer = gptDraw->request_2d_layer(ptAppData->ptDrawlist);
ptAppData->ptBGLayer = gptDraw->request_2d_layer(ptAppData->ptDrawlist);
// demonstrate example extension
gptExample->print_to_console("From example extension!");
// return app memory // return app memory
return ptAppData; return ptAppData;
@ -305,22 +231,7 @@ pl_app_load(plApiRegistryI* ptApiRegistry, plAppData* ptAppData)
PL_EXPORT void PL_EXPORT void
pl_app_shutdown(plAppData* ptAppData) pl_app_shutdown(plAppData* ptAppData)
{ {
// ensure GPU is finished before cleanup gptStarter->cleanup();
gptGfx->flush_device(ptAppData->ptDevice);
for(uint32_t i = 0; i < gptGfx->get_frames_in_flight(); i++)
{
gptGfx->cleanup_command_pool(ptAppData->atCmdPools[i]);
gptGfx->cleanup_semaphore(ptAppData->aptSemaphores[i]);
}
gptDrawBackend->cleanup_font_atlas(NULL);
gptUi->cleanup();
gptDrawBackend->cleanup();
gptScreenLog->cleanup();
gptConsole->cleanup();
gptGfx->cleanup_swapchain(ptAppData->ptSwapchain);
gptGfx->cleanup_surface(ptAppData->ptSurface);
gptGfx->cleanup_device(ptAppData->ptDevice);
gptGfx->cleanup();
gptWindows->destroy_window(ptAppData->ptWindow); gptWindows->destroy_window(ptAppData->ptWindow);
PL_FREE(ptAppData); PL_FREE(ptAppData);
} }
@ -332,7 +243,7 @@ pl_app_shutdown(plAppData* ptAppData)
PL_EXPORT void PL_EXPORT void
pl_app_resize(plAppData* ptAppData) pl_app_resize(plAppData* ptAppData)
{ {
pl__resize_graphics_extensions(ptAppData); gptStarter->resize();
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -342,284 +253,88 @@ pl_app_resize(plAppData* ptAppData)
PL_EXPORT void PL_EXPORT void
pl_app_update(plAppData* ptAppData) pl_app_update(plAppData* ptAppData)
{ {
gptProfile->begin_frame(); // this needs to be the first call when using the starter
pl_begin_cpu_sample(gptProfile, 0, __FUNCTION__); // extension. You must return if it returns false (usually a swapchain recreation).
if(!gptStarter->begin_frame())
// for convience
plIO* ptIO = gptIO->get_io();
// new frame stuff
gptIO->new_frame();
gptDrawBackend->new_frame();
gptUi->new_frame();
gptStats->new_frame();
gptGfx->begin_frame(ptAppData->ptDevice);
plCommandPool* ptCmdPool = ptAppData->atCmdPools[gptGfx->get_current_frame_index()];
gptGfx->reset_command_pool(ptCmdPool, 0);
// update statistics
static double* pdFrameTimeCounter = NULL;
if(!pdFrameTimeCounter)
pdFrameTimeCounter = gptStats->get_counter("frametime (ms)");
*pdFrameTimeCounter = (double)ptIO->fDeltaTime * 1000.0;
// acquire swapchain image
if(!gptGfx->acquire_swapchain_image(ptAppData->ptSwapchain))
{
pl_app_resize(ptAppData);
pl_end_cpu_sample(gptProfile, 0);
gptProfile->end_frame();
return; return;
}
// just some drawing //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~stats API~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gptDraw->add_circle(ptAppData->ptFGLayer, (plVec2){100.0f, 100.0f}, 50.0f, 12, (plDrawLineOptions){.fThickness = 2.0f, .uColor = PL_COLOR_32_RGBA(1.0f, 0.0f, 1.0f, 1.0f)});
if(gptIO->is_key_pressed(PL_KEY_F1, false)) // rather than have to lookup the counter every frame, its best to "store" it
gptConsole->open(); // like this. To update it, just deference it and set the value.
static double* pdExample2Counter = NULL;
if(!pdExample2Counter)
pdExample2Counter = gptStats->get_counter("example 2 counter");
gptConsole->update(); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~drawing & profile API~~~~~~~~~~~~~~~~~~~~~~~~~~~
if(gptUi->begin_window("Pilot Light", NULL, false)) gptProfile->begin_sample(0, "example drawing");
plDrawLayer2D* ptFGLayer = gptStarter->get_foreground_layer();
gptDraw->add_line(ptFGLayer,
(plVec2){0.0f, 0.0f},
(plVec2){500.0f, 500.0f}, (plDrawLineOptions){ .fThickness = 1.0f, .uColor = PL_COLOR_32_MAGENTA});
plDrawLayer2D* ptBGLayer = gptStarter->get_background_layer();
gptDraw->add_triangle_filled(ptBGLayer,
(plVec2){50.0f, 100.0f},
(plVec2){200.0f},
(plVec2){100.0f, 200.0f}, (plDrawSolidOptions){.uColor = PL_COLOR_32_RGBA(0.0f, 0.5f, 1.0f, 0.5f)});
gptProfile->end_sample(0);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~UI & Screen Log API~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// creating a window
if(ptAppData->bShowHelpWindow)
{ {
if(gptUI->begin_window("Help", NULL, PL_UI_WINDOW_FLAGS_AUTO_SIZE | PL_UI_WINDOW_FLAGS_NO_COLLAPSE))
const float pfRatios[] = {1.0f};
gptUi->layout_row(PL_UI_LAYOUT_ROW_TYPE_DYNAMIC, 0.0f, 1, pfRatios);
if(gptUi->begin_collapsing_header("Information", 0))
{ {
gptUI->layout_static(0.0f, 500.0f, 1);
gptUi->text("Pilot Light %s", PILOT_LIGHT_VERSION_STRING); gptUI->text("Press F1 to bring up console.");
gptUi->end_collapsing_header(); gptUI->text("Look for t.StatsTool (we added a stat)");
gptUI->text("Look for t.LogTool (we added a log channel)");
gptUI->text("Look for t.ProfileTool");
gptUI->text("Look for t.MemoryAllocationTool and look for example 2!");
gptUI->text("Look for a.HelpWindow (console variable we added)");
gptUI->end_window();
} }
if(gptUi->begin_collapsing_header("Tools", 0)) }
// creating another window
if(gptUI->begin_window("Pilot Light", NULL, PL_UI_WINDOW_FLAGS_NONE))
{ {
gptUi->checkbox("Device Memory Analyzer", ptAppData->pbShowDeviceMemoryAnalyzer); gptUI->text("Pilot Light %s", PILOT_LIGHT_VERSION_STRING);
gptUi->checkbox("Memory Allocations", ptAppData->pbShowMemoryAllocations);
gptUi->checkbox("Profiling", ptAppData->pbShowProfiling); if(gptUI->button("Log"))
gptUi->checkbox("Statistics", ptAppData->pbShowStats);
gptUi->checkbox("Logging", ptAppData->pbShowLogging);
gptUi->end_collapsing_header();
}
if(gptUi->begin_collapsing_header("User Interface", 0))
{ {
gptUi->checkbox("UI Debug", &ptAppData->bShowUiDebug); gptLog->trace(ptAppData->uExampleLogChannel, "Log");
gptUi->checkbox("UI Style", &ptAppData->bShowUiStyle); gptLog->debug(ptAppData->uExampleLogChannel, "Log");
gptUi->end_collapsing_header(); gptLog->info(ptAppData->uExampleLogChannel, "Log");
} gptLog->warn(ptAppData->uExampleLogChannel, "Log");
gptUi->end_window(); gptLog->error(ptAppData->uExampleLogChannel, "Log");
gptLog->fatal(ptAppData->uExampleLogChannel, "Log");
} }
if(ptAppData->bShowUiStyle) static int iCounter = 0;
gptUi->show_style_editor_window(&ptAppData->bShowUiStyle); gptUI->slider_int("Stat Counter Example", &iCounter, -10, 10, 0);
*pdExample2Counter = iCounter; // setting our stat variable
if(ptAppData->bShowUiDebug) gptUI->layout_row_begin(PL_UI_LAYOUT_ROW_TYPE_DYNAMIC, 0.0f, 2); // got to pl_ui_ext.h to see layout systems
gptUi->show_debug_window(&ptAppData->bShowUiDebug);
gptTools->update(); gptUI->layout_row_push(0.3f);
if(gptUI->button("Log To Screen"))
gptScreenLog->add_message(5.0, "Cool Message!");
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~graphics work~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gptUI->layout_row_push(0.3f);
if(gptUI->button("Big Log To Screen"))
gptScreenLog->add_message_ex(0, 5, PL_COLOR_32_GREEN, 3.0f, "%s", "Bigger & Greener!");
const uint32_t uCurrentFrameIndex = gptGfx->get_current_frame_index(); gptUI->layout_row_end();
plCommandBuffer* ptCommandBuffer = gptGfx->request_command_buffer(ptCmdPool); gptUI->end_window();
}
const plBeginCommandInfo tBeginInfo = { // must be the last function called when using the starter extension
.uWaitSemaphoreCount = 1, gptStarter->end_frame();
.atWaitSempahores = {ptAppData->aptSemaphores[uCurrentFrameIndex]},
.auWaitSemaphoreValues = {ptAppData->aulNextTimelineValue[uCurrentFrameIndex]},
};
gptGfx->begin_command_recording(ptCommandBuffer, &tBeginInfo);
// begin main renderpass (directly to swapchain)
plRenderEncoder* ptEncoder = gptGfx->begin_render_pass(ptCommandBuffer, ptAppData->tMainRenderPass, NULL);
// submit our layers & drawlist
gptDraw->submit_2d_layer(ptAppData->ptBGLayer);
gptDraw->submit_2d_layer(ptAppData->ptFGLayer);
gptDrawBackend->submit_2d_drawlist(ptAppData->ptDrawlist, ptEncoder, ptIO->tMainViewportSize.x, ptIO->tMainViewportSize.y, gptGfx->get_swapchain_info(ptAppData->ptSwapchain).tSampleCount);
// submits UI layers
gptUi->end_frame();
// submit UI drawlists
gptDrawBackend->submit_2d_drawlist(gptUi->get_draw_list(), ptEncoder, ptIO->tMainViewportSize.x, ptIO->tMainViewportSize.y, gptGfx->get_swapchain_info(ptAppData->ptSwapchain).tSampleCount);
gptDrawBackend->submit_2d_drawlist(gptUi->get_debug_draw_list(), ptEncoder, ptIO->tMainViewportSize.x, ptIO->tMainViewportSize.y, gptGfx->get_swapchain_info(ptAppData->ptSwapchain).tSampleCount);
plDrawList2D* ptMessageDrawlist = gptScreenLog->get_drawlist(ptIO->tMainViewportSize.x, ptIO->tMainViewportSize.y);
gptDrawBackend->submit_2d_drawlist(ptMessageDrawlist, ptEncoder, ptIO->tMainViewportSize.x, ptIO->tMainViewportSize.y, gptGfx->get_swapchain_info(ptAppData->ptSwapchain).tSampleCount);
// end render pass
gptGfx->end_render_pass(ptEncoder);
// end recording
gptGfx->end_command_recording(ptCommandBuffer);
//~~~~~~~~~~~~~~~~~~~~~~~~~~submit work to GPU & present~~~~~~~~~~~~~~~~~~~~~~~
const plSubmitInfo tSubmitInfo = {
.uSignalSemaphoreCount = 1,
.atSignalSempahores = {ptAppData->aptSemaphores[uCurrentFrameIndex]},
.auSignalSemaphoreValues = {++ptAppData->aulNextTimelineValue[uCurrentFrameIndex]},
};
if(!gptGfx->present(ptCommandBuffer, &tSubmitInfo, &ptAppData->ptSwapchain, 1))
pl_app_resize(ptAppData);
gptGfx->return_command_buffer(ptCommandBuffer);
pl_end_cpu_sample(gptProfile, 0);
gptProfile->end_frame();
}
//-----------------------------------------------------------------------------
// [SECTION] helper functions implementations
//-----------------------------------------------------------------------------
void
pl__setup_graphics_extensions(plAppData* ptAppData)
{
// initialize shader extension (shader compiler)
static const plShaderOptions tDefaultShaderOptions = {
.apcIncludeDirectories = {
"../shaders/"
},
.apcDirectories = {
"../shaders/"
},
.tFlags = PL_SHADER_FLAGS_AUTO_OUTPUT
};
gptShader->initialize(&tDefaultShaderOptions);
// initialize graphics system
const plGraphicsInit tGraphicsInit = {
.tFlags = PL_GRAPHICS_INIT_FLAGS_VALIDATION_ENABLED | PL_GRAPHICS_INIT_FLAGS_SWAPCHAIN_ENABLED
};
gptGfx->initialize(&tGraphicsInit);
ptAppData->ptSurface = gptGfx->create_surface(ptAppData->ptWindow);
// find suitable device
uint32_t uDeviceCount = 16;
plDeviceInfo atDeviceInfos[16] = {0};
gptGfx->enumerate_devices(atDeviceInfos, &uDeviceCount);
// we will prefer discrete, then integrated
int iBestDvcIdx = 0;
int iDiscreteGPUIdx = -1;
int iIntegratedGPUIdx = -1;
for(uint32_t i = 0; i < uDeviceCount; i++)
{
if(atDeviceInfos[i].tType == PL_DEVICE_TYPE_DISCRETE)
iDiscreteGPUIdx = i;
else if(atDeviceInfos[i].tType == PL_DEVICE_TYPE_INTEGRATED)
iIntegratedGPUIdx = i;
}
if(iDiscreteGPUIdx > -1)
iBestDvcIdx = iDiscreteGPUIdx;
else if(iIntegratedGPUIdx > -1)
iBestDvcIdx = iIntegratedGPUIdx;
// create device
const plDeviceInit tDeviceInit = {
.uDeviceIdx = iBestDvcIdx,
.ptSurface = ptAppData->ptSurface
};
ptAppData->ptDevice = gptGfx->create_device(&tDeviceInit);
// create command pools
for(uint32_t i = 0; i < gptGfx->get_frames_in_flight(); i++)
ptAppData->atCmdPools[i] = gptGfx->create_command_pool(ptAppData->ptDevice, NULL);
// create swapchain
plSwapchainInit tSwapInit = {
.tSampleCount = PL_SAMPLE_COUNT_1
};
ptAppData->ptSwapchain = gptGfx->create_swapchain(ptAppData->ptDevice, ptAppData->ptSurface, &tSwapInit);
uint32_t uImageCount = 0;
plTextureHandle* atSwapchainImages = gptGfx->get_swapchain_images(ptAppData->ptSwapchain, &uImageCount);
// create main render pass layout
const plRenderPassLayoutDesc tMainRenderPassLayoutDesc = {
.atRenderTargets = {
{ .tFormat = gptGfx->get_swapchain_info(ptAppData->ptSwapchain).tFormat }, // swapchain
},
.atSubpasses = {
{
.uRenderTargetCount = 1,
.auRenderTargets = {0}
}
},
.atSubpassDependencies = {
{
.uSourceSubpass = UINT32_MAX,
.uDestinationSubpass = 0,
.tSourceStageMask = PL_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT | PL_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS | PL_PIPELINE_STAGE_LATE_FRAGMENT_TESTS | PL_PIPELINE_STAGE_COMPUTE_SHADER,
.tDestinationStageMask = PL_PIPELINE_STAGE_FRAGMENT_SHADER | PL_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT | PL_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS | PL_PIPELINE_STAGE_LATE_FRAGMENT_TESTS,
.tSourceAccessMask = PL_ACCESS_COLOR_ATTACHMENT_WRITE | PL_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE | PL_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ,
.tDestinationAccessMask = PL_ACCESS_SHADER_READ | PL_ACCESS_COLOR_ATTACHMENT_WRITE | PL_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE | PL_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ,
},
{
.uSourceSubpass = 0,
.uDestinationSubpass = UINT32_MAX,
.tSourceStageMask = PL_PIPELINE_STAGE_FRAGMENT_SHADER | PL_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT | PL_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS | PL_PIPELINE_STAGE_LATE_FRAGMENT_TESTS,
.tDestinationStageMask = PL_PIPELINE_STAGE_FRAGMENT_SHADER | PL_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT | PL_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS | PL_PIPELINE_STAGE_LATE_FRAGMENT_TESTS | PL_PIPELINE_STAGE_COMPUTE_SHADER,
.tSourceAccessMask = PL_ACCESS_SHADER_READ | PL_ACCESS_COLOR_ATTACHMENT_WRITE | PL_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE | PL_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ,
.tDestinationAccessMask = PL_ACCESS_SHADER_READ | PL_ACCESS_COLOR_ATTACHMENT_WRITE | PL_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE | PL_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ,
},
}
};
ptAppData->tMainRenderPassLayout = gptGfx->create_render_pass_layout(ptAppData->ptDevice, &tMainRenderPassLayoutDesc);
// create main render pass
const plRenderPassDesc tMainRenderPassDesc = {
.tLayout = ptAppData->tMainRenderPassLayout,
.atColorTargets = {
{
.tLoadOp = PL_LOAD_OP_CLEAR,
.tStoreOp = PL_STORE_OP_STORE,
.tCurrentUsage = PL_TEXTURE_USAGE_UNSPECIFIED,
.tNextUsage = PL_TEXTURE_USAGE_PRESENT,
.tClearColor = {0.0f, 0.0f, 0.0f, 1.0f}
}
},
.tDimensions = {(float)gptGfx->get_swapchain_info(ptAppData->ptSwapchain).uWidth, (float)gptGfx->get_swapchain_info(ptAppData->ptSwapchain).uHeight},
.ptSwapchain = ptAppData->ptSwapchain
};
plRenderPassAttachments atMainAttachmentSets[16] = {0};
for(uint32_t i = 0; i < uImageCount; i++)
{
atMainAttachmentSets[i].atViewAttachments[0] = atSwapchainImages[i];
}
ptAppData->tMainRenderPass = gptGfx->create_render_pass(ptAppData->ptDevice, &tMainRenderPassDesc, atMainAttachmentSets);
// create timeline semaphores to syncronize GPU work submission
for(uint32_t i = 0; i < gptGfx->get_frames_in_flight(); i++)
ptAppData->aptSemaphores[i] = gptGfx->create_semaphore(ptAppData->ptDevice, false);
}
void
pl__resize_graphics_extensions(plAppData* ptAppData)
{
plIO* ptIO = gptIO->get_io();
plSwapchainInit tDesc = {
.bVSync = true,
.uWidth = (uint32_t)ptIO->tMainViewportSize.x,
.uHeight = (uint32_t)ptIO->tMainViewportSize.y,
.tSampleCount = gptGfx->get_swapchain_info(ptAppData->ptSwapchain).tSampleCount,
};
gptGfx->recreate_swapchain(ptAppData->ptSwapchain, &tDesc);
uint32_t uImageCount = 0;
plTextureHandle* atSwapchainImages = gptGfx->get_swapchain_images(ptAppData->ptSwapchain, &uImageCount);
plRenderPassAttachments atMainAttachmentSets[16] = {0};
for(uint32_t i = 0; i < uImageCount; i++)
{
atMainAttachmentSets[i].atViewAttachments[0] = atSwapchainImages[i];
}
gptGfx->update_render_pass_attachments(ptAppData->ptDevice, ptAppData->tMainRenderPass, gptIO->get_io()->tMainViewportSize, atMainAttachmentSets);
} }