chore: update template for PL changes

This commit is contained in:
Jonathan Hoffstadt 2025-04-11 21:55:51 -05:00
parent afc696a894
commit 11723827a6
2 changed files with 205 additions and 490 deletions

View File

@ -18,10 +18,10 @@ if len(sys.argv) > 1:
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/" + pilotlight_location)
import pl_build.core as pl
import pl_build.backend_win32 as win32
import pl_build.backend_linux as linux
import pl_build.backend_macos as apple
import build.core as pl
import build.backend_win32 as win32
import build.backend_linux as linux
import build.backend_macos as apple
#-----------------------------------------------------------------------------
# [SECTION] project

687
src/app.c
View File

@ -1,20 +1,64 @@
/*
app.c
- template app
- loads only stable APIs
example_basic_2.c
- demonstrates loading 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:
// [SECTION] quick notes
// [SECTION] includes
// [SECTION] structs
// [SECTION] apis
// [SECTION] helper functions forward declarations
// [SECTION] pl_app_load
// [SECTION] pl_app_shutdown
// [SECTION] pl_app_resize
// [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,21 @@ Index of this file:
#include <stdlib.h> // malloc, free
#include <string.h> // memset
#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"
// extensions
#include "pl_log_ext.h"
#include "pl_window_ext.h"
#include "pl_shader_ext.h"
#include "pl_draw_ext.h"
#include "pl_starter_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_tools_ext.h"
#include "pl_platform_ext.h"
#include "pl_profile_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"
//-----------------------------------------------------------------------------
@ -61,83 +94,34 @@ typedef struct _plAppData
// window
plWindow* ptWindow;
// drawing stuff
plDrawList2D* ptDrawlist;
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;
// log channel
uint64_t uExampleLogChannel;
// console variable
bool bShowHelpWindow;
} plAppData;
//-----------------------------------------------------------------------------
// [SECTION] apis
//-----------------------------------------------------------------------------
const plDataRegistryI* gptDataRegistry = NULL;
const plMemoryI* gptMemory = NULL;
const plIOI* gptIO = NULL;
const plWindowI* gptWindows = NULL;
const plGraphicsI* gptGfx = NULL;
const plDrawI* gptDraw = NULL;
const plUiI* gptUi = NULL;
const plShaderI* gptShader = 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 plIOI* gptIO = NULL;
const plWindowI* gptWindows = NULL;
const plDrawI* gptDraw = NULL;
const plStarterI* gptStarter = NULL;
const plUiI* gptUI = 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_REALLOC(x, y) gptMemory->tracked_realloc((x), (y), __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
//-----------------------------------------------------------------------------
@ -145,13 +129,9 @@ void pl__resize_graphics_extensions(plAppData*);
PL_EXPORT void*
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
// 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
// during a hot reload.
if(ptAppData)
@ -159,140 +139,86 @@ pl_app_load(plApiRegistryI* ptApiRegistry, plAppData* ptAppData)
// re-retrieve the apis since we are now in
// a different dll/so
gptMemory = pl_get_api_latest(ptApiRegistry, plMemoryI);
gptIO = pl_get_api_latest(ptApiRegistry, plIOI);
gptWindows = pl_get_api_latest(ptApiRegistry, plWindowI);
gptGfx = pl_get_api_latest(ptApiRegistry, plGraphicsI);
gptDraw = pl_get_api_latest(ptApiRegistry, plDrawI);
gptShader = pl_get_api_latest(ptApiRegistry, plShaderI);
gptDrawBackend = pl_get_api_latest(ptApiRegistry, plDrawBackendI);
gptUi = pl_get_api_latest(ptApiRegistry, plUiI);
gptProfile = pl_get_api_latest(ptApiRegistry, plProfileI);
gptStats = pl_get_api_latest(ptApiRegistry, plStatsI);
gptExample = pl_get_api_latest(ptApiRegistry, plExampleI);
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);
gptVirtualMemory = pl_get_api_latest(ptApiRegistry, plVirtualMemoryI);
gptConsole = pl_get_api_latest(ptApiRegistry, plConsoleI);
gptScreenLog = pl_get_api_latest(ptApiRegistry, plScreenLogI);
gptIO = pl_get_api_latest(ptApiRegistry, plIOI);
gptWindows = pl_get_api_latest(ptApiRegistry, plWindowI);
gptDraw = pl_get_api_latest(ptApiRegistry, plDrawI);
gptStarter = pl_get_api_latest(ptApiRegistry, plStarterI);
gptUI = pl_get_api_latest(ptApiRegistry, plUiI);
gptScreenLog = pl_get_api_latest(ptApiRegistry, plScreenLogI);
gptProfile = pl_get_api_latest(ptApiRegistry, plProfileI);
gptStats = pl_get_api_latest(ptApiRegistry, plStatsI);
gptMemory = pl_get_api_latest(ptApiRegistry, plMemoryI);
gptLog = pl_get_api_latest(ptApiRegistry, plLogI);
gptConsole = pl_get_api_latest(ptApiRegistry, plConsoleI);
gptExample = pl_get_api_latest(ptApiRegistry, plExampleI);
return ptAppData;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~apis & extensions~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// retrieve extension registry
const plExtensionRegistryI* ptExtensionRegistry = pl_get_api_latest(ptApiRegistry, plExtensionRegistryI);
// load extensions
ptExtensionRegistry->load("pl_unity_ext", NULL, NULL, false);
ptExtensionRegistry->load("pl_platform_ext", NULL, NULL, false);
// * first argument is the shared library name WITHOUT the extension
// * 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);
// load apis
gptMemory = pl_get_api_latest(ptApiRegistry, plMemoryI);
gptIO = pl_get_api_latest(ptApiRegistry, plIOI);
gptWindows = pl_get_api_latest(ptApiRegistry, plWindowI);
gptGfx = pl_get_api_latest(ptApiRegistry, plGraphicsI);
gptDraw = pl_get_api_latest(ptApiRegistry, plDrawI);
gptShader = pl_get_api_latest(ptApiRegistry, plShaderI);
gptDrawBackend = pl_get_api_latest(ptApiRegistry, plDrawBackendI);
gptUi = pl_get_api_latest(ptApiRegistry, plUiI);
gptProfile = pl_get_api_latest(ptApiRegistry, plProfileI);
gptStats = pl_get_api_latest(ptApiRegistry, plStatsI);
gptExample = pl_get_api_latest(ptApiRegistry, plExampleI);
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);
gptVirtualMemory = pl_get_api_latest(ptApiRegistry, plVirtualMemoryI);
gptConsole = pl_get_api_latest(ptApiRegistry, plConsoleI);
gptScreenLog = pl_get_api_latest(ptApiRegistry, plScreenLogI);
// load required apis
gptIO = pl_get_api_latest(ptApiRegistry, plIOI);
gptWindows = pl_get_api_latest(ptApiRegistry, plWindowI);
// load required apis (these are provided though extensions)
gptDraw = pl_get_api_latest(ptApiRegistry, plDrawI);
gptStarter = pl_get_api_latest(ptApiRegistry, plStarterI);
gptUI = pl_get_api_latest(ptApiRegistry, plUiI);
gptScreenLog = pl_get_api_latest(ptApiRegistry, plScreenLogI);
gptProfile = pl_get_api_latest(ptApiRegistry, plProfileI);
gptStats = pl_get_api_latest(ptApiRegistry, plStatsI);
gptMemory = pl_get_api_latest(ptApiRegistry, plMemoryI);
gptLog = pl_get_api_latest(ptApiRegistry, plLogI);
gptConsole = pl_get_api_latest(ptApiRegistry, plConsoleI);
// 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
// allocate app memory here
ptAppData = PL_ALLOC(sizeof(plAppData));
memset(ptAppData, 0, sizeof(plAppData));
// add console variables
gptConsole->initialize((plConsoleSettings){.tFlags = PL_CONSOLE_FLAGS_POPUP});
// default values
ptAppData->bShowHelpWindow = true;
// use window API to create a window
plWindowDesc tWindowDesc = {
.pcTitle = "App Template",
.pcTitle = "Template App",
.iXPos = 200,
.iYPos = 200,
.uWidth = 500,
.uHeight = 500,
.uWidth = 600,
.uHeight = 600,
};
gptWindows->create_window(tWindowDesc, &ptAppData->ptWindow);
gptWindows->create(tWindowDesc, &ptAppData->ptWindow);
gptWindows->show(ptAppData->ptWindow);
// setup graphics extension
pl__setup_graphics_extensions(ptAppData);
// initialize the starter API (handles alot of boilerplate)
plStarterInit tStarterInit = {
.tFlags = PL_STARTER_FLAGS_ALL_EXTENSIONS,
.ptWindow = ptAppData->ptWindow
};
gptStarter->initialize(tStarterInit);
gptStarter->finalize();
// initialize APIs that require it
gptTools->initialize((plToolsInit){.ptDevice = ptAppData->ptDevice});
// add a log channel
ptAppData->uExampleLogChannel = gptLog->add_channel("Example 2", (plLogExtChannelInit){.tType = PL_LOG_CHANNEL_TYPE_BUFFER});
// retrieve some console variables
ptAppData->pbShowLogging = (bool*)gptConsole->get_variable("t.LogTool", NULL, NULL);
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!");
// add a console variable
gptConsole->add_toggle_variable("a.HelpWindow", &ptAppData->bShowHelpWindow, "toggle help window", PL_CONSOLE_VARIABLE_FLAGS_NONE);
// return app memory
return ptAppData;
@ -305,23 +231,8 @@ pl_app_load(plApiRegistryI* ptApiRegistry, plAppData* ptAppData)
PL_EXPORT void
pl_app_shutdown(plAppData* ptAppData)
{
// ensure GPU is finished before 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);
gptStarter->cleanup();
gptWindows->destroy(ptAppData->ptWindow);
PL_FREE(ptAppData);
}
@ -332,7 +243,7 @@ pl_app_shutdown(plAppData* ptAppData)
PL_EXPORT void
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_app_update(plAppData* ptAppData)
{
gptProfile->begin_frame();
pl_begin_cpu_sample(gptProfile, 0, __FUNCTION__);
// 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();
// this needs to be the first call when using the starter
// extension. You must return if it returns false (usually a swapchain recreation).
if(!gptStarter->begin_frame())
return;
}
// just some drawing
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))
gptConsole->open();
gptConsole->update();
if(gptUi->begin_window("Pilot Light", NULL, false))
{
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->text("Pilot Light %s", PILOT_LIGHT_VERSION_STRING);
gptUi->end_collapsing_header();
}
if(gptUi->begin_collapsing_header("Tools", 0))
{
gptUi->checkbox("Device Memory Analyzer", ptAppData->pbShowDeviceMemoryAnalyzer);
gptUi->checkbox("Memory Allocations", ptAppData->pbShowMemoryAllocations);
gptUi->checkbox("Profiling", ptAppData->pbShowProfiling);
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);
gptUi->checkbox("UI Style", &ptAppData->bShowUiStyle);
gptUi->end_collapsing_header();
}
gptUi->end_window();
}
if(ptAppData->bShowUiStyle)
gptUi->show_style_editor_window(&ptAppData->bShowUiStyle);
if(ptAppData->bShowUiDebug)
gptUi->show_debug_window(&ptAppData->bShowUiDebug);
gptTools->update();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~graphics work~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const uint32_t uCurrentFrameIndex = gptGfx->get_current_frame_index();
plCommandBuffer* ptCommandBuffer = gptGfx->request_command_buffer(ptCmdPool);
const plBeginCommandInfo tBeginInfo = {
.uWaitSemaphoreCount = 1,
.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);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~stats API~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// submits UI layers
gptUi->end_frame();
// rather than have to lookup the counter every frame, its best to "store" it
// 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");
// 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);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~drawing & profile API~~~~~~~~~~~~~~~~~~~~~~~~~~~
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);
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)});
// end render pass
gptGfx->end_render_pass(ptEncoder);
gptProfile->end_sample(0);
// end recording
gptGfx->end_command_recording(ptCommandBuffer);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~UI & Screen Log API~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~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++)
// creating a window
if(ptAppData->bShowHelpWindow)
{
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,
},
if(gptUI->begin_window("Help", NULL, PL_UI_WINDOW_FLAGS_AUTO_SIZE | PL_UI_WINDOW_FLAGS_NO_COLLAPSE))
{
gptUI->layout_static(0.0f, 500.0f, 1);
gptUI->text("Press F1 to bring up console.");
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();
}
};
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++)
// creating another window
if(gptUI->begin_window("Pilot Light", NULL, PL_UI_WINDOW_FLAGS_NONE))
{
atMainAttachmentSets[i].atViewAttachments[0] = atSwapchainImages[i];
gptUI->text("Pilot Light %s", PILOT_LIGHT_VERSION_STRING);
if(gptUI->button("Log"))
{
gptLog->trace(ptAppData->uExampleLogChannel, "Log");
gptLog->debug(ptAppData->uExampleLogChannel, "Log");
gptLog->info(ptAppData->uExampleLogChannel, "Log");
gptLog->warn(ptAppData->uExampleLogChannel, "Log");
gptLog->error(ptAppData->uExampleLogChannel, "Log");
gptLog->fatal(ptAppData->uExampleLogChannel, "Log");
}
static int iCounter = 0;
gptUI->slider_int("Stat Counter Example", &iCounter, -10, 10, 0);
*pdExample2Counter = iCounter; // setting our stat variable
gptUI->layout_row_begin(PL_UI_LAYOUT_ROW_TYPE_DYNAMIC, 0.0f, 2); // got to pl_ui_ext.h to see layout systems
gptUI->layout_row_push(0.3f);
if(gptUI->button("Log To Screen"))
gptScreenLog->add_message(5.0, "Cool Message!");
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!");
gptUI->layout_row_end();
gptUI->end_window();
}
gptGfx->update_render_pass_attachments(ptAppData->ptDevice, ptAppData->tMainRenderPass, gptIO->get_io()->tMainViewportSize, atMainAttachmentSets);
// must be the last function called when using the starter extension
gptStarter->end_frame();
}