initial commit

This commit is contained in:
Jonathan Hoffstadt 2025-02-12 16:03:57 -06:00
commit 96c01ca737
7 changed files with 1089 additions and 0 deletions

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
# directories
.vs/
.vscode/
.idea/
# python
scripts/__pycache__/
# random files
src/*.pdb
src/*.bat
src/*.sh
# misc
.DS_Store

84
README.md Normal file
View File

@ -0,0 +1,84 @@
# Pilot Light Template
## Information
This repository acts as a template for creating a [Pilot Light](https://github.com/PilotLightTech/pilotlight) application using the engine source. In the future, a
binary template will be created.
## Build
As a first step, clone and build **Pilot Light**. Here we are assuming it is adjacent to this repo. If not, pass the
correct relative location to **gen_build.py**.
### Windows
From within a local directory, enter the following commands in your terminal:
```bash
# clone & build pilot light
git clone https://github.com/PilotLightTech/pilotlight
cd pilotlight/src
build_win32.bat
# clone & build example
cd ..
git clone https://git.pilotlight.tech/pilotlight/pilotlight-template
cd pilotlight-template/scripts
python setup.py
python gen_build.py ../../pilotlight
cd ../src
build.bat
```
### Linux
From within a local directory, enter the following commands in your terminal:
```bash
# clone & build pilot light
git clone https://github.com/PilotLightTech/pilotlight
cd pilotlight/src
chmod +x build_linux.sh
./build_linux.sh
# clone & build example
cd ..
git clone https://git.pilotlight.tech/pilotlight/pilotlight-template
cd pilotlight-template/scripts
python3 setup.py
python3 gen_build.py ../../pilotlight
cd ../src
chmod +x build.sh
./build.sh
```
### MacOS
From within a local directory, enter the following commands in your terminal:
```bash
# clone & build pilot light
git clone https://github.com/PilotLightTech/pilotlight
cd pilotlight/src
chmod +x build_linux.sh
./build_linux.sh
# clone & build example
cd ..
git clone https://git.pilotlight.tech/pilotlight/pilotlight-template
cd pilotlight-template/scripts
python3 setup.py
python3 gen_build.py ../../pilotlight
cd ../src
chmod +x build.sh
./build.sh
```
Binaries will be in _pilotlight/out/_.
Run the application by pressing F5 if using VSCode or manually like so:
```bash
# windows
pilot_light.exe -a template_app
# linux/macos
./pilot_light -a template_app
```

View File

@ -0,0 +1,52 @@
/*
pl_example_ext.c
*/
/*
Index of this file:
// [SECTION] includes
// [SECTION] public api implementation
// [SECTION] extension loading
*/
//-----------------------------------------------------------------------------
// [SECTION] includes
//-----------------------------------------------------------------------------
#include <stdio.h>
#include "pl.h"
#include "pl_example_ext.h"
//-----------------------------------------------------------------------------
// [SECTION] public api implementation
//-----------------------------------------------------------------------------
static void
pl__example_print_to_console(const char* pcText)
{
printf("%s\n", pcText);
}
//-----------------------------------------------------------------------------
// [SECTION] extension loading
//-----------------------------------------------------------------------------
PL_EXPORT void
pl_load_ext(plApiRegistryI* ptApiRegistry, bool bReload)
{
const plExampleI tApi = {
.print_to_console = pl__example_print_to_console
};
pl_set_api(ptApiRegistry, plExampleI, &tApi);
}
PL_EXPORT void
pl_unload_ext(plApiRegistryI* ptApiRegistry, bool bReload)
{
if(bReload)
return;
const plExampleI* ptApi = pl_get_api_latest(ptApiRegistry, plExampleI);
ptApiRegistry->remove_api(ptApi);
}

View File

@ -0,0 +1,35 @@
/*
pl_example_ext.h
- example extension
*/
/*
Index of this file:
// [SECTION] header mess
// [SECTION] apis
// [SECTION] public api
*/
//-----------------------------------------------------------------------------
// [SECTION] header mess
//-----------------------------------------------------------------------------
#ifndef PL_EXAMPLE_EXT_H
#define PL_EXAMPLE_EXT_H
//-----------------------------------------------------------------------------
// [SECTION] apis
//-----------------------------------------------------------------------------
#define plExampleI_version (plVersion){1, 0, 0}
//-----------------------------------------------------------------------------
// [SECTION] public api
//-----------------------------------------------------------------------------
typedef struct _plExampleI
{
void (*print_to_console)(const char* text);
} plExampleI;
#endif // PL_EXAMPLE_EXT_H

198
scripts/gen_build.py Normal file
View File

@ -0,0 +1,198 @@
# gen_build.py
# Index of this file:
# [SECTION] imports
#-----------------------------------------------------------------------------
# [SECTION] imports
#-----------------------------------------------------------------------------
import os
import sys
import platform as plat
pilotlight_location = "../../pilotlight"
if len(sys.argv) > 1:
pilotlight_location = 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
#-----------------------------------------------------------------------------
# [SECTION] project
#-----------------------------------------------------------------------------
# where to output build scripts
working_directory = os.path.dirname(os.path.abspath(__file__)) + "/../src"
with pl.project("game"):
# used to decide hot reloading
pl.set_hot_reload_target(pilotlight_location + "/out/pilot_light")
# project wide settings
pl.set_output_directory(pilotlight_location + "/out")
pl.add_link_directories(pilotlight_location + "/out")
pl.add_include_directories(
"../src",
"../extensions",
pilotlight_location + "/src",
pilotlight_location + "/libs",
pilotlight_location + "/extensions",
pilotlight_location + "/dependencies/stb")
#-----------------------------------------------------------------------------
# [SECTION] profiles
#-----------------------------------------------------------------------------
# win32 or msvc only
pl.add_profile(compiler_filter=["msvc"],
target_type_filter=[pl.TargetType.DYNAMIC_LIBRARY],
linker_flags=["-noimplib", "-noexp"])
pl.add_profile(compiler_filter=["msvc"],
linker_flags=["-incremental:no"],
compiler_flags=["-Zc:preprocessor", "-nologo", "-std:c11", "-W4", "-WX", "-wd4201",
"-wd4100", "-wd4996", "-wd4505", "-wd4189", "-wd5105", "-wd4115", "-permissive-"])
pl.add_profile(compiler_filter=["msvc"],
configuration_filter=["debug"],
compiler_flags=["-Od", "-MDd", "-Zi"])
pl.add_profile(compiler_filter=["msvc"],
configuration_filter=["release"],
compiler_flags=["-O2", "-MD"])
# linux or gcc only
pl.add_profile(platform_filter=["Linux"],
link_directories=["/usr/lib/x86_64-linux-gnu"])
pl.add_profile(compiler_filter=["gcc"],
linker_flags=["-ldl", "-lm"],
compiler_flags=["-std=gnu11", "-fPIC"])
pl.add_profile(compiler_filter=["gcc"],
configuration_filter=["debug"],
compiler_flags=["--debug", "-g"])
# macos or clang only
pl.add_profile(platform_filter=["Darwin"],
link_frameworks=["Metal", "MetalKit", "Cocoa", "IOKit", "CoreVideo", "QuartzCore"])
pl.add_profile(compiler_filter=["clang"],
linker_flags=["-Wl,-rpath,/usr/local/lib"],
compiler_flags=["-std=c99", "-fmodules", "-ObjC", "-fPIC"])
pl.add_profile(compiler_filter=["clang"],
configuration_filter=["debug"],
compiler_flags=["--debug", "-g"])
# configs
pl.add_profile(configuration_filter=["debug"], definitions=["_DEBUG", "PL_CONFIG_DEBUG"])
pl.add_profile(configuration_filter=["release"], definitions=["NDEBUG", "PL_CONFIG_RELEASE"])
#-----------------------------------------------------------------------------
# [SECTION] extensions
#-----------------------------------------------------------------------------
with pl.target("pl_example_ext", pl.TargetType.DYNAMIC_LIBRARY, True):
pl.add_source_files("../extensions/pl_example_ext.c")
pl.set_output_binary("pl_example_ext")
# default config
with pl.configuration("debug"):
# win32
with pl.platform("Windows"):
with pl.compiler("msvc"):
pass
# linux
with pl.platform("Linux"):
with pl.compiler("gcc"):
pass
# macos
with pl.platform("Darwin"):
with pl.compiler("clang"):
pass
# release
with pl.configuration("release"):
# win32
with pl.platform("Windows"):
with pl.compiler("msvc"):
pass
# linux
with pl.platform("Linux"):
with pl.compiler("gcc"):
pass
# macos
with pl.platform("Darwin"):
with pl.compiler("clang"):
pass
#-----------------------------------------------------------------------------
# [SECTION] app
#-----------------------------------------------------------------------------
with pl.target("template_app", pl.TargetType.DYNAMIC_LIBRARY, True):
pl.add_source_files("../src/app.c")
pl.set_output_binary("template_app")
# default config
with pl.configuration("debug"):
# win32
with pl.platform("Windows"):
with pl.compiler("msvc"):
pass
# linux
with pl.platform("Linux"):
with pl.compiler("gcc"):
pass
# mac os
with pl.platform("Darwin"):
with pl.compiler("clang"):
pass
# release
with pl.configuration("release"):
# win32
with pl.platform("Windows"):
with pl.compiler("msvc"):
pass
# linux
with pl.platform("Linux"):
with pl.compiler("gcc"):
pass
# mac os
with pl.platform("Darwin"):
with pl.compiler("clang"):
pass
#-----------------------------------------------------------------------------
# [SECTION] generate scripts
#-----------------------------------------------------------------------------
if plat.system() == "Windows":
win32.generate_build(working_directory + '/' + "build.bat")
elif plat.system() == "Darwin":
apple.generate_build(working_directory + '/' + "build.sh")
elif plat.system() == "Linux":
linux.generate_build(working_directory + '/' + "build.sh")

135
scripts/setup.py Normal file
View File

@ -0,0 +1,135 @@
import os
import platform
defines = [
"_DEBUG",
"PL_PROFILE_ON",
"PL_LOG_IMPLEMENTATION",
"PL_MEMORY_IMPLEMENTATION",
"PL_PROFILE_IMPLEMENTATION",
"PL_LOG_ON",
"PL_MEMORY_IMPLEMENTATION",
"PL_STL_IMPLEMENTATION",
"PL_STRING_IMPLEMENTATION",
"PL_MATH_INCLUDE_FUNCTIONS",
"PL_JSON_IMPLEMENTATION",
"PL_VULKAN_BACKEND",
"PL_METAL_BACKEND",
"PL_TEST_IMPLEMENTATION",
"PL_CONFIG_DEBUG"
]
includes = [
"${workspaceFolder}/**",
"${workspaceFolder}/src",
"${workspaceFolder}/extensions",
"${workspaceFolder}/../pilotlight/sandbox",
"${workspaceFolder}/../pilotlight/src",
"${workspaceFolder}/../pilotlight/libs",
"${workspaceFolder}/../pilotlight/extensions",
"${workspaceFolder}/../pilotlight/dependencies/stb",
"${workspaceFolder}/../pilotlight/dependencies/cgltf"
]
########################################################################################################################
# setup vs code
########################################################################################################################
if not os.path.isdir('../.vscode'):
os.mkdir('../.vscode')
with open('../.vscode/launch.json', 'w') as file:
lines = []
lines.append('{')
lines.append(' "version": "0.2.0",')
lines.append(' "configurations": [')
lines.append(' {')
if platform.system() == "Windows":
lines.append(' "name": "(Windows) Launch",')
lines.append(' "type": "cppvsdbg",')
lines.append(' "program": "${workspaceFolder}/../pilotlight/out/pilot_light.exe",')
lines.append(' "console": "integratedTerminal",')
elif platform.system() == "Darwin":
lines.append(' "name": "(lldb) Launch",')
lines.append(' "type": "cppdbg",')
lines.append(' "program": "${workspaceFolder}/../pilotlight/out/pilot_light",')
lines.append(' "externalConsole": false,')
lines.append(' "MIMode": "lldb",')
elif platform.system() == "Linux":
lines.append(' "name": "(lldb) Launch",')
lines.append(' "type": "cppdbg",')
lines.append(' "program": "${workspaceFolder}/../pilotlight/out/pilot_light",')
lines.append(' "request": "launch",')
lines.append(' "args": ["-a", "template_app"],')
lines.append(' "stopAtEntry": false,')
lines.append(' "cwd": "${workspaceFolder}/../pilotlight/out/",')
lines.append(' "environment": []')
lines.append(' }')
lines.append(' ]')
lines.append('}')
for i in range(len(lines)):
lines[i] = lines[i] + "\n"
file.writelines(lines)
with open('../.vscode/c_cpp_properties.json', 'w') as file:
lines = []
lines.append('{')
lines.append(' "version" : 4,')
lines.append(' "configurations": [')
lines.append(' {')
if platform.system() == "Windows":
lines.append(' "name": "Win32",')
elif platform.system() == "Darwin":
lines.append(' "name": "Apple",')
elif platform.system() == "Linux":
lines.append(' "name": "Linux",')
lines.append(' "includePath": [')
for i in range(len(includes) - 1):
lines.append(' "' + includes[i] + '",')
lines.append(' "' + includes[-1] + '"')
lines.append(' ],')
lines.append(' "defines": [')
for i in range(len(defines) - 1):
lines.append(' "' + defines[i] + '",')
lines.append(' "' + defines[-1] + '"')
lines.append(' ],')
lines.append(' "cStandard": "c11",')
if platform.system() == "Windows":
lines.append(' "windowsSdkVersion": "10.0.19041.0",')
lines.append(' "intelliSenseMode": "windows-msvc-x64"')
elif platform.system() == "Darwin":
lines.append(' "intelliSenseMode": "macos-clang-arm64"')
elif platform.system() == "Linux":
lines.append(' "intelliSenseMode": "linux-gcc-x64"')
lines.append(' }')
lines.append(' ]')
lines.append('}')
for i in range(len(lines)):
lines[i] = lines[i] + "\n"
file.writelines(lines)
with open('../.vscode/settings.json', 'w') as file:
lines = []
lines.append('{')
lines.append(' "files.associations": {')
lines.append(' "pl_*.h": "c",')
lines.append(' "pl_*.m": "objective-c",')
lines.append(' "pl_*.inc": "c"')
lines.append(' },')
lines.append(' "python.analysis.extraPaths": ["../pilotlight/pl_build"]')
lines.append('}')
for i in range(len(lines)):
lines[i] = lines[i] + "\n"
file.writelines(lines)

569
src/app.c Normal file
View File

@ -0,0 +1,569 @@
/*
app.c
- template app
- loads only stable APIs
*/
/*
Index of this file:
// [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] includes
//-----------------------------------------------------------------------------
#include <stdlib.h> // malloc, free
#include <string.h> // memset
#include "pl.h"
#include "pl_memory.h"
#define PL_MATH_INCLUDE_FUNCTIONS
#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_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_network_ext.h"
#include "pl_threads_ext.h"
#include "pl_atomics_ext.h"
#include "pl_library_ext.h"
#include "pl_file_ext.h"
#include "pl_rect_pack_ext.h"
#include "pl_gpu_allocators_ext.h"
#include "pl_image_ext.h"
#include "pl_virtual_memory_ext.h"
#include "pl_debug_ext.h" // not technically stable
// example extensions
#include "pl_example_ext.h"
//-----------------------------------------------------------------------------
// [SECTION] structs
//-----------------------------------------------------------------------------
typedef struct _plAppData
{
// window
plWindow* ptWindow;
// drawing stuff
plDrawList2D* ptDrawlist;
plDrawLayer2D* ptFGLayer;
plDrawLayer2D* ptBGLayer;
plFont* ptDefaultFont;
// ui options
plDebugApiInfo tDebugInfo;
bool bShowUiDebug;
bool bShowUiStyle;
// 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;
} plAppData;
//-----------------------------------------------------------------------------
// [SECTION] apis
//-----------------------------------------------------------------------------
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 plDebugApiI* gptDebug = 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;
// 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
//-----------------------------------------------------------------------------
PL_EXPORT void*
pl_app_load(plApiRegistryI* ptApiRegistry, plAppData* ptAppData)
{
// NOTE: on first load, "ptAppData" 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
const plDataRegistryI* ptDataRegistry = pl_get_api_latest(ptApiRegistry, plDataRegistryI);
// if "ptAppData" is a valid pointer, then this function is being called
// during a hot reload.
if(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);
gptDebug = pl_get_api_latest(ptApiRegistry, plDebugApiI);
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);
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_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);
gptDebug = pl_get_api_latest(ptApiRegistry, plDebugApiI);
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);
// this path is taken only during first load, so we
// allocate app memory here
ptAppData = PL_ALLOC(sizeof(plAppData));
memset(ptAppData, 0, sizeof(plAppData));
// use window API to create a window
plWindowDesc tWindowDesc = {
.pcTitle = "App Template",
.iXPos = 200,
.iYPos = 200,
.uWidth = 500,
.uHeight = 500,
};
gptWindows->create_window(tWindowDesc, &ptAppData->ptWindow);
// setup graphics extension
pl__setup_graphics_extensions(ptAppData);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~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);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~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 ptAppData;
}
//-----------------------------------------------------------------------------
// [SECTION] pl_app_shutdown
//-----------------------------------------------------------------------------
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();
gptGfx->cleanup_swapchain(ptAppData->ptSwapchain);
gptGfx->cleanup_surface(ptAppData->ptSurface);
gptGfx->cleanup_device(ptAppData->ptDevice);
gptGfx->cleanup();
gptWindows->destroy_window(ptAppData->ptWindow);
PL_FREE(ptAppData);
}
//-----------------------------------------------------------------------------
// [SECTION] pl_app_resize
//-----------------------------------------------------------------------------
PL_EXPORT void
pl_app_resize(plAppData* ptAppData)
{
pl__resize_graphics_extensions(ptAppData);
}
//-----------------------------------------------------------------------------
// [SECTION] pl_app_update
//-----------------------------------------------------------------------------
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();
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(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->tDebugInfo.bShowDeviceMemoryAnalyzer);
gptUi->checkbox("Memory Allocations", &ptAppData->tDebugInfo.bShowMemoryAllocations);
gptUi->checkbox("Profiling", &ptAppData->tDebugInfo.bShowProfiling);
gptUi->checkbox("Statistics", &ptAppData->tDebugInfo.bShowStats);
gptUi->checkbox("Logging", &ptAppData->tDebugInfo.bShowLogging);
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);
gptDebug->show_debug_windows(&ptAppData->tDebugInfo);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~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);
// 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);
// 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}
}
}
};
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);
}