initial vulkan only
This commit is contained in:
parent
998f922764
commit
79be18d11f
@ -56,7 +56,7 @@ From within a local directory, enter the following commands in your terminal:
|
||||
# clone & build pilot light
|
||||
git clone https://github.com/PilotLightTech/pilotlight
|
||||
cd pilotlight/src
|
||||
chmod +x build_linux.sh
|
||||
chmod +x build.sh -c moltenvk
|
||||
./build_linux.sh
|
||||
|
||||
# clone & build example
|
||||
|
BIN
data/test.hdr
Normal file
BIN
data/test.hdr
Normal file
Binary file not shown.
@ -1,52 +0,0 @@
|
||||
/*
|
||||
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);
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
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 {1, 0, 0}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] public api
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
typedef struct _plExampleI
|
||||
{
|
||||
void (*print_to_console)(const char* text);
|
||||
} plExampleI;
|
||||
|
||||
#endif // PL_EXAMPLE_EXT_H
|
@ -57,9 +57,6 @@ with pl.project("game"):
|
||||
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
|
||||
@ -84,54 +81,6 @@ with pl.project("game"):
|
||||
|
||||
# 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
|
||||
@ -148,35 +97,21 @@ with pl.project("game"):
|
||||
# win32
|
||||
with pl.platform("Windows"):
|
||||
with pl.compiler("msvc"):
|
||||
pass
|
||||
pl.add_static_link_libraries("vulkan-1")
|
||||
pl.add_include_directories("%VULKAN_SDK%\\Include")
|
||||
pl.add_link_directories('%VULKAN_SDK%\\Lib')
|
||||
|
||||
# linux
|
||||
with pl.platform("Linux"):
|
||||
with pl.compiler("gcc"):
|
||||
pass
|
||||
pl.add_dynamic_link_libraries("vulkan")
|
||||
pl.add_include_directories('$VULKAN_SDK/include', '/usr/include/vulkan')
|
||||
pl.add_link_directories('$VULKAN_SDK/lib')
|
||||
|
||||
# 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
|
||||
pl.add_dynamic_link_libraries("vulkan")
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# [SECTION] generate scripts
|
||||
|
@ -28,7 +28,8 @@ includes = [
|
||||
"${workspaceFolder}/../pilotlight/libs",
|
||||
"${workspaceFolder}/../pilotlight/extensions",
|
||||
"${workspaceFolder}/../pilotlight/dependencies/stb",
|
||||
"${workspaceFolder}/../pilotlight/dependencies/cgltf"
|
||||
"${workspaceFolder}/../pilotlight/dependencies/cgltf",
|
||||
"${env:VK_SDK_PATH}/Include"
|
||||
]
|
||||
|
||||
########################################################################################################################
|
||||
|
30
shaders/shader.comp
Normal file
30
shaders/shader.comp
Normal file
@ -0,0 +1,30 @@
|
||||
#version 450
|
||||
|
||||
layout(set = 0, binding = 0) buffer Pos {
|
||||
float values[ ];
|
||||
};
|
||||
|
||||
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
void main()
|
||||
{
|
||||
// PRACTICE (how to get 6144 to the shader without hard coding?)
|
||||
const uint row_stride = 6144 * 4;
|
||||
|
||||
uint pixel_x = gl_GlobalInvocationID.x;
|
||||
uint pixel_y = gl_GlobalInvocationID.y;
|
||||
|
||||
// const int iXCoord = int(gl_WorkGroupID.x * 8 + gl_LocalInvocationID.x);
|
||||
|
||||
// original colors
|
||||
float fOriginalRed = values[ pixel_x * 4 + pixel_y * row_stride];
|
||||
float fOriginalGreen = values[ pixel_x * 4 + pixel_y * row_stride + 1];
|
||||
float fOriginalBlue = values[ pixel_x * 4 + pixel_y * row_stride + 2];
|
||||
float fOriginalAlpha = values[ pixel_x * 4 + pixel_y * row_stride + 3];
|
||||
|
||||
// swap red & green channels (and unnormalize)
|
||||
values[pixel_x * 4 + pixel_y * row_stride] = clamp(fOriginalRed * 255, 0, 255);
|
||||
values[pixel_x * 4 + pixel_y * row_stride + 1] = clamp(fOriginalGreen * 255, 0, 255);
|
||||
values[pixel_x * 4 + pixel_y * row_stride + 2] = clamp(fOriginalBlue * 255, 0, 255);
|
||||
values[pixel_x * 4 + pixel_y * row_stride + 3] = clamp(255, 0, 255);
|
||||
}
|
559
src/app.c
559
src/app.c
@ -1,15 +1,3 @@
|
||||
/*
|
||||
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
|
||||
@ -22,45 +10,6 @@ Index of this file:
|
||||
// [SECTION] pl_app_update
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [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.
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] includes
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -73,17 +22,17 @@ Index of this file:
|
||||
#include "pl_math.h"
|
||||
|
||||
// extensions
|
||||
#include "pl_draw_ext.h"
|
||||
#define PL_GRAPHICS_EXPOSE_VULKAN
|
||||
#include "pl_graphics_ext.h"
|
||||
#include "pl_shader_ext.h"
|
||||
#include "pl_starter_ext.h"
|
||||
#include "pl_ui_ext.h"
|
||||
#include "pl_image_ext.h"
|
||||
#include "pl_vfs_ext.h"
|
||||
#include "pl_screen_log_ext.h"
|
||||
#include "pl_profile_ext.h"
|
||||
#include "pl_log_ext.h"
|
||||
#include "pl_stats_ext.h"
|
||||
#include "pl_console_ext.h"
|
||||
|
||||
// out extension
|
||||
#include "pl_example_ext.h"
|
||||
#include "vulkan/vulkan.h"
|
||||
|
||||
#define VULKAN_CHECK(x) assert(x == VK_SUCCESS)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] structs
|
||||
@ -93,12 +42,12 @@ typedef struct _plAppData
|
||||
{
|
||||
// window
|
||||
plWindow* ptWindow;
|
||||
plSurface* ptSurface;
|
||||
plDevice* ptDevice;
|
||||
|
||||
// log channel
|
||||
uint64_t uExampleLogChannel;
|
||||
// shaders
|
||||
plShaderHandle tShader;
|
||||
|
||||
// console variable
|
||||
bool bShowHelpWindow;
|
||||
} plAppData;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -107,20 +56,12 @@ typedef struct _plAppData
|
||||
|
||||
const plIOI* gptIO = NULL;
|
||||
const plWindowI* gptWindows = NULL;
|
||||
const plDrawI* gptDraw = NULL;
|
||||
const plGraphicsI* gptGfx = NULL;
|
||||
const plShaderI* gptShader = NULL;
|
||||
const plStarterI* gptStarter = NULL;
|
||||
const plUiI* gptUI = NULL;
|
||||
const plImageI* gptImage = NULL;
|
||||
const plVfsI* gptVfs = 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;
|
||||
|
||||
#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__)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] pl_app_load
|
||||
@ -129,76 +70,35 @@ const plExampleI* gptExample = NULL;
|
||||
PL_EXPORT void*
|
||||
pl_app_load(plApiRegistryI* ptApiRegistry, plAppData* ptAppData)
|
||||
{
|
||||
// NOTE: on first load, "pAppData" will be NULL but on reloads
|
||||
// it will be the value returned from this function
|
||||
|
||||
// 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
|
||||
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;
|
||||
}
|
||||
// this path is taken only during first load, so we
|
||||
// allocate app memory here
|
||||
ptAppData = malloc(sizeof(plAppData));
|
||||
memset(ptAppData, 0, sizeof(plAppData));
|
||||
|
||||
// retrieve extension registry
|
||||
const plExtensionRegistryI* ptExtensionRegistry = pl_get_api_latest(ptApiRegistry, plExtensionRegistryI);
|
||||
|
||||
// load extensions
|
||||
// * 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->add_path("../../pl-template/out");
|
||||
ptExtensionRegistry->load("pl_unity_ext", NULL, NULL, false);
|
||||
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_unity_ext", NULL, NULL, true);
|
||||
ptExtensionRegistry->load("pl_platform_ext", NULL, NULL, false);
|
||||
|
||||
// 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);
|
||||
gptGfx = pl_get_api_latest(ptApiRegistry, plGraphicsI);
|
||||
gptShader = pl_get_api_latest(ptApiRegistry, plShaderI);
|
||||
gptStarter = pl_get_api_latest(ptApiRegistry, plStarterI);
|
||||
gptUI = pl_get_api_latest(ptApiRegistry, plUiI);
|
||||
gptImage = pl_get_api_latest(ptApiRegistry, plImageI);
|
||||
gptVfs = pl_get_api_latest(ptApiRegistry, plVfsI);
|
||||
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));
|
||||
|
||||
// default values
|
||||
ptAppData->bShowHelpWindow = true;
|
||||
|
||||
// use window API to create a window
|
||||
plWindowDesc tWindowDesc = {
|
||||
.pcTitle = "Template App",
|
||||
.pcTitle = "Vulkan Example",
|
||||
.iXPos = 200,
|
||||
.iYPos = 200,
|
||||
.uWidth = 600,
|
||||
@ -207,21 +107,27 @@ pl_app_load(plApiRegistryI* ptApiRegistry, plAppData* ptAppData)
|
||||
gptWindows->create(tWindowDesc, &ptAppData->ptWindow);
|
||||
gptWindows->show(ptAppData->ptWindow);
|
||||
|
||||
// initialize the starter API (handles alot of boilerplate)
|
||||
plStarterInit tStarterInit = {
|
||||
.tFlags = PL_STARTER_FLAGS_ALL_EXTENSIONS,
|
||||
.ptWindow = ptAppData->ptWindow
|
||||
plGraphicsInit tGraphicsDesc = {
|
||||
.tFlags = PL_GRAPHICS_INIT_FLAGS_SWAPCHAIN_ENABLED | PL_GRAPHICS_INIT_FLAGS_VALIDATION_ENABLED
|
||||
};
|
||||
gptStarter->initialize(tStarterInit);
|
||||
gptStarter->finalize();
|
||||
gptGfx->initialize(&tGraphicsDesc);
|
||||
|
||||
// add a log channel
|
||||
ptAppData->uExampleLogChannel = gptLog->add_channel("Example 2", (plLogExtChannelInit){.tType = PL_LOG_CHANNEL_TYPE_BUFFER});
|
||||
ptAppData->ptSurface = gptGfx->create_surface(ptAppData->ptWindow);
|
||||
|
||||
// add a console variable
|
||||
gptConsole->add_toggle_variable("a.HelpWindow", &ptAppData->bShowHelpWindow, "toggle help window", PL_CONSOLE_VARIABLE_FLAGS_NONE);
|
||||
|
||||
// return app memory
|
||||
ptAppData->ptDevice = gptStarter->create_device(ptAppData->ptSurface);
|
||||
|
||||
// initialize shader extension (we are doing this ourselves so we can add additional shader directories)
|
||||
static const plShaderOptions tDefaultShaderOptions = {
|
||||
.apcIncludeDirectories = {
|
||||
"../../pl-template/shaders/"
|
||||
},
|
||||
.apcDirectories = {
|
||||
"../shaders/",
|
||||
"../../pl-template/shaders/"
|
||||
},
|
||||
.tFlags = PL_SHADER_FLAGS_AUTO_OUTPUT | PL_SHADER_FLAGS_ALWAYS_COMPILE
|
||||
};
|
||||
gptShader->initialize(&tDefaultShaderOptions);
|
||||
return ptAppData;
|
||||
}
|
||||
|
||||
@ -232,9 +138,14 @@ pl_app_load(plApiRegistryI* ptApiRegistry, plAppData* ptAppData)
|
||||
PL_EXPORT void
|
||||
pl_app_shutdown(plAppData* ptAppData)
|
||||
{
|
||||
gptStarter->cleanup();
|
||||
gptGfx->flush_device(ptAppData->ptDevice);
|
||||
gptShader->cleanup();
|
||||
gptWindows->destroy(ptAppData->ptWindow);
|
||||
PL_FREE(ptAppData);
|
||||
gptScreenLog->cleanup();
|
||||
gptGfx->cleanup_surface(ptAppData->ptSurface);
|
||||
gptGfx->cleanup_device(ptAppData->ptDevice);
|
||||
gptGfx->cleanup();
|
||||
free(ptAppData);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -244,7 +155,7 @@ pl_app_shutdown(plAppData* ptAppData)
|
||||
PL_EXPORT void
|
||||
pl_app_resize(plAppData* ptAppData)
|
||||
{
|
||||
gptStarter->resize();
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -254,88 +165,322 @@ pl_app_resize(plAppData* ptAppData)
|
||||
PL_EXPORT void
|
||||
pl_app_update(plAppData* ptAppData)
|
||||
{
|
||||
// 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;
|
||||
gptIO->new_frame(); // must be called once at the beginning of a frame
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~stats API~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
// 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");
|
||||
plIO* ptIO = gptIO->get_io();
|
||||
ptIO->bRunning = false;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~drawing & profile API~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// core (basically every vulkan application will have these)
|
||||
|
||||
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});
|
||||
// global resources (for simplicity, we are treating these as global resources)
|
||||
VkDescriptorPool gtDescriptorPool; // pool of descriptors
|
||||
VkCommandPool gtComputeCommandPool; // pool of command buffers
|
||||
VkFence gtComputeWorkFence; // fence to signal compute work is done (not needed for this example but required by API)
|
||||
|
||||
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)});
|
||||
// example specific resources
|
||||
VkBuffer gtStorageBuffer; // storage buffer (reading & writing for this example)
|
||||
VkDeviceMemory gtStorageBufferMemory; // storage buffer actual device memory
|
||||
VkDescriptorSet gtDescriptorSet; // descriptor set which will contain a descriptor to the storage buffer
|
||||
float* gpfMemoryMap; // persistant mapping to the storage buffer
|
||||
|
||||
gptProfile->end_sample(0);
|
||||
// example specific compute pipeline stuff
|
||||
VkDescriptorSetLayout gtDescriptorSetLayout; // describes the compute shader descriptor set layout (single set in this example)
|
||||
VkPipelineLayout gtPipelineLayout; // describes the compute shader pipeline layout (descriptor set layout + other stuff in general)
|
||||
VkPipeline gtPipeline; // complete pipeline state object for compute shader (this really is the "shader")
|
||||
VkShaderModule gtShaderModule; // SPIR-V code
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~UI & Screen Log API~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
VkDevice gtLogicalDevice = gptGfx->get_vulkan_device(ptAppData->ptDevice);
|
||||
|
||||
// creating a window
|
||||
if(ptAppData->bShowHelpWindow)
|
||||
plShaderModule tShader = gptShader->load_glsl("shader.comp", "main", NULL, NULL);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// load an image
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
size_t szImageFileSize = gptVfs->get_file_size_str("../../pl-template/data/test.hdr");
|
||||
unsigned char* pucBuffer = malloc(szImageFileSize);
|
||||
plVfsFileHandle tEnvMapHandle = gptVfs->open_file("../../pl-template/data/test.hdr", PL_VFS_FILE_MODE_READ);
|
||||
gptVfs->read_file(tEnvMapHandle, pucBuffer, &szImageFileSize);
|
||||
gptVfs->close_file(tEnvMapHandle);
|
||||
|
||||
int iImageWidth = 0;
|
||||
int iImageHeight = 0;
|
||||
int iImageChannels = 0;
|
||||
float* pfRawImageBytes = gptImage->load_hdr(pucBuffer, (int)szImageFileSize, &iImageWidth, &iImageHeight, &iImageChannels, 4);
|
||||
assert(pfRawImageBytes);
|
||||
free(pucBuffer);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// create storage buffer
|
||||
// - we are using a storage buffer for this example to read & write from
|
||||
// - we are also storing it in host visible memory for simplicity
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// create storage buffer
|
||||
VkBufferCreateInfo tBufferInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = iImageWidth * iImageHeight * 4 * sizeof(float),
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
|
||||
};
|
||||
VULKAN_CHECK(vkCreateBuffer(gtLogicalDevice, &tBufferInfo, NULL, >StorageBuffer));
|
||||
|
||||
// get buffer memory requirements
|
||||
VkMemoryRequirements tMemRequirements = {0};
|
||||
vkGetBufferMemoryRequirements(gtLogicalDevice, gtStorageBuffer, &tMemRequirements);
|
||||
|
||||
// allocate actual device memory
|
||||
VkMemoryAllocateInfo tStorageAllocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = tMemRequirements.size,
|
||||
};
|
||||
|
||||
// find suitable memory type
|
||||
VkPhysicalDeviceMemoryProperties gtMemProps = gptGfx->get_vulkan_memory_properties(ptAppData->ptDevice);
|
||||
for (uint32_t i = 0; i < gtMemProps.memoryTypeCount; i++)
|
||||
{
|
||||
if(gptUI->begin_window("Help", NULL, PL_UI_WINDOW_FLAGS_AUTO_SIZE | PL_UI_WINDOW_FLAGS_NO_COLLAPSE))
|
||||
if ((tMemRequirements.memoryTypeBits & (1 << i)) && (gtMemProps.memoryTypes[i].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) == (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
|
||||
{
|
||||
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();
|
||||
tStorageAllocInfo.memoryTypeIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
VULKAN_CHECK(vkAllocateMemory(gtLogicalDevice, &tStorageAllocInfo, NULL, >StorageBufferMemory));
|
||||
|
||||
// creating another window
|
||||
if(gptUI->begin_window("Pilot Light", NULL, PL_UI_WINDOW_FLAGS_NONE))
|
||||
{
|
||||
gptUI->text("Pilot Light %s", PILOT_LIGHT_VERSION_STRING);
|
||||
// bind memory to resource
|
||||
VULKAN_CHECK(vkBindBufferMemory(gtLogicalDevice, gtStorageBuffer, gtStorageBufferMemory, 0));
|
||||
|
||||
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");
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// map to the memory persistantly
|
||||
// - this is really just to make the example simpler
|
||||
// - more generally, you would need to transfer the data & syncronize access
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static int iCounter = 0;
|
||||
gptUI->slider_int("Stat Counter Example", &iCounter, -10, 10, 0);
|
||||
*pdExample2Counter = iCounter; // setting our stat variable
|
||||
VULKAN_CHECK(vkMapMemory(gtLogicalDevice, gtStorageBufferMemory, 0, iImageWidth * iImageHeight * 4 * sizeof(float), 0, (void**)&gpfMemoryMap));
|
||||
memcpy(gpfMemoryMap, pfRawImageBytes, iImageWidth * iImageHeight * 4 * sizeof(float));
|
||||
gptImage->free(pfRawImageBytes);
|
||||
|
||||
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!");
|
||||
//-----------------------------------------------------------------------------
|
||||
// create descriptor pool, layout, & set
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
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!");
|
||||
// create descriptor pool
|
||||
VkDescriptorPoolSize tPoolSize = {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 100 };
|
||||
const VkDescriptorPoolCreateInfo tDescriptorPoolInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
||||
.maxSets = 100,
|
||||
.poolSizeCount = 1,
|
||||
.pPoolSizes = &tPoolSize,
|
||||
};
|
||||
VULKAN_CHECK(vkCreateDescriptorPool(gtLogicalDevice, &tDescriptorPoolInfo, NULL, >DescriptorPool));
|
||||
|
||||
gptUI->layout_row_end();
|
||||
// create descriptor set layout
|
||||
const VkDescriptorSetLayoutBinding tBindings = {
|
||||
.binding = 0u,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT
|
||||
};
|
||||
|
||||
gptUI->end_window();
|
||||
}
|
||||
const VkDescriptorSetLayoutCreateInfo tLayoutInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = 1,
|
||||
.pBindings = &tBindings
|
||||
};
|
||||
VULKAN_CHECK(vkCreateDescriptorSetLayout(gtLogicalDevice, &tLayoutInfo, NULL, >DescriptorSetLayout));
|
||||
|
||||
// must be the last function called when using the starter extension
|
||||
gptStarter->end_frame();
|
||||
// allocate descriptor set from descriptor pool
|
||||
const VkDescriptorSetAllocateInfo tAllocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.descriptorPool = gtDescriptorPool,
|
||||
.descriptorSetCount = 1,
|
||||
.pSetLayouts = >DescriptorSetLayout
|
||||
};
|
||||
VULKAN_CHECK(vkAllocateDescriptorSets(gtLogicalDevice, &tAllocInfo, >DescriptorSet));
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// write data to descriptors
|
||||
// - you have already created the storage buffer, created the descriptor set,
|
||||
// but you haven't "connected" them, which is what you are doing here
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// descriptor info
|
||||
VkDescriptorBufferInfo tBufferDescriptorInfo = {
|
||||
.buffer = gtStorageBuffer,
|
||||
.range = VK_WHOLE_SIZE
|
||||
};
|
||||
|
||||
VkWriteDescriptorSet tDescriptorWrites = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstBinding = 0u,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.dstSet = gtDescriptorSet,
|
||||
.pBufferInfo = &tBufferDescriptorInfo
|
||||
};
|
||||
vkUpdateDescriptorSets(gtLogicalDevice, 1, &tDescriptorWrites, 0, NULL);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// create shader & pipeline & pipeline layout
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// create pipeline layout (resource bindings)
|
||||
VkPipelineLayoutCreateInfo tPipelineLayoutInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = >DescriptorSetLayout
|
||||
};
|
||||
VULKAN_CHECK(vkCreatePipelineLayout(gtLogicalDevice, &tPipelineLayoutInfo, NULL, >PipelineLayout));
|
||||
|
||||
VkShaderModuleCreateInfo tShaderCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.codeSize = tShader.szCodeSize,
|
||||
.pCode = (const uint32_t*)(tShader.puCode)
|
||||
};
|
||||
VULKAN_CHECK(vkCreateShaderModule(gtLogicalDevice, &tShaderCreateInfo, NULL, >ShaderModule));
|
||||
|
||||
VkPipelineShaderStageCreateInfo tShaderStage = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.module = gtShaderModule,
|
||||
.pName = "main"
|
||||
};
|
||||
|
||||
// create compute pipeline (FYI, this is when actual shader compilation is performed by the driver)
|
||||
VkComputePipelineCreateInfo tPipelineCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
|
||||
.layout = gtPipelineLayout,
|
||||
.stage = tShaderStage
|
||||
};
|
||||
VULKAN_CHECK(vkCreateComputePipelines(gtLogicalDevice, VK_NULL_HANDLE, 1, &tPipelineCreateInfo, NULL, >Pipeline));
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
// WORK
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// create command pool
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// create command pool from compute queue
|
||||
const VkCommandPoolCreateInfo tCommandPoolInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.queueFamilyIndex = gptGfx->get_vulkan_queue_family(ptAppData->ptDevice),
|
||||
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
|
||||
};
|
||||
VULKAN_CHECK(vkCreateCommandPool(gtLogicalDevice, &tCommandPoolInfo, NULL, >ComputeCommandPool));
|
||||
|
||||
VkCommandBufferAllocateInfo tCommandAllocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandPool = gtComputeCommandPool,
|
||||
.commandBufferCount = 1,
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// allocate a command buffer from the command pool
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
VkCommandBuffer tCommandBuffer = {0};
|
||||
vkAllocateCommandBuffers(gtLogicalDevice, &tCommandAllocInfo, &tCommandBuffer);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// begin recording work into the command buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
VkCommandBufferBeginInfo tCommandBufferBeginInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
};
|
||||
VULKAN_CHECK(vkBeginCommandBuffer(tCommandBuffer, &tCommandBufferBeginInfo));
|
||||
|
||||
// bind compute pipeline
|
||||
vkCmdBindPipeline(tCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, gtPipeline);
|
||||
|
||||
// bind resources (remember this is done in sets)
|
||||
vkCmdBindDescriptorSets(tCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, gtPipelineLayout, 0, 1, >DescriptorSet, 0, 0);
|
||||
|
||||
// dispatch work
|
||||
// vkCmdDispatch(tCommandBuffer, iImageWidth, iImageHeight, 1);
|
||||
vkCmdDispatch(tCommandBuffer, iImageWidth, iImageHeight, 1);
|
||||
|
||||
// finish recording
|
||||
VULKAN_CHECK(vkEndCommandBuffer(tCommandBuffer));
|
||||
|
||||
// submit command buffer to compute queue
|
||||
const VkPipelineStageFlags tWaitStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
const VkSubmitInfo tComputeSubmitInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.pWaitDstStageMask = &tWaitStageMask,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &tCommandBuffer
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// create syncronization primitives
|
||||
// - not really needed for this example but is required when submitting work
|
||||
// I believe.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// fence for compute command buffer sync
|
||||
const VkFenceCreateInfo tFenceCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||
.flags = VK_FENCE_CREATE_SIGNALED_BIT
|
||||
};
|
||||
VULKAN_CHECK(vkCreateFence(gtLogicalDevice, &tFenceCreateInfo, NULL, >ComputeWorkFence));
|
||||
|
||||
vkResetFences(gtLogicalDevice, 1, >ComputeWorkFence);
|
||||
VULKAN_CHECK(vkQueueSubmit(gptGfx->get_vulkan_queue(ptAppData->ptDevice), 1, &tComputeSubmitInfo, gtComputeWorkFence));
|
||||
|
||||
// wait for fence to be signaled
|
||||
VULKAN_CHECK(vkWaitForFences(gtLogicalDevice, 1, >ComputeWorkFence, VK_TRUE, UINT64_MAX));
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
// RESULTS
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// can only output non-normalized unfortunately
|
||||
unsigned char* pucResults = malloc(iImageHeight * iImageWidth * 4);
|
||||
for(uint32_t i = 0; i < (uint32_t)(iImageHeight * iImageWidth * 4); i++)
|
||||
pucResults[i] = (unsigned char)gpfMemoryMap[i];
|
||||
|
||||
plImageWriteInfo tWriteInfo = {
|
||||
.iWidth = iImageWidth,
|
||||
.iHeight = iImageHeight,
|
||||
.iComponents = 4,
|
||||
.iByteStride = iImageWidth * 4
|
||||
};
|
||||
gptImage->write("../../pl-template/out/test_image_results.png", pucResults, &tWriteInfo);
|
||||
|
||||
free(pucResults);
|
||||
|
||||
// unmap memory & cleanup storage buffer
|
||||
vkUnmapMemory(gtLogicalDevice, gtStorageBufferMemory);
|
||||
vkDestroyBuffer(gtLogicalDevice, gtStorageBuffer, NULL);
|
||||
vkFreeMemory(gtLogicalDevice, gtStorageBufferMemory, NULL);
|
||||
|
||||
// cleanup pipeline & shader
|
||||
vkDestroyShaderModule(gtLogicalDevice, gtShaderModule, NULL);
|
||||
vkDestroyPipelineLayout(gtLogicalDevice, gtPipelineLayout, NULL);
|
||||
vkDestroyPipeline(gtLogicalDevice, gtPipeline, NULL);
|
||||
|
||||
// cleanup some global resources
|
||||
vkDestroyCommandPool(gtLogicalDevice, gtComputeCommandPool, NULL);
|
||||
vkDestroyDescriptorSetLayout(gtLogicalDevice, gtDescriptorSetLayout, NULL);
|
||||
vkDestroyDescriptorPool(gtLogicalDevice, gtDescriptorPool, NULL);
|
||||
vkDestroyFence(gtLogicalDevice, gtComputeWorkFence, NULL);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user