diff --git a/data/test.hdr b/data/test.hdr new file mode 100644 index 0000000..db5e150 Binary files /dev/null and b/data/test.hdr differ diff --git a/extensions/pl_example_ext.c b/extensions/pl_example_ext.c deleted file mode 100644 index 939f834..0000000 --- a/extensions/pl_example_ext.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - pl_example_ext.c -*/ - -/* -Index of this file: -// [SECTION] includes -// [SECTION] public api implementation -// [SECTION] extension loading -*/ - -//----------------------------------------------------------------------------- -// [SECTION] includes -//----------------------------------------------------------------------------- - -#include -#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); -} diff --git a/extensions/pl_example_ext.h b/extensions/pl_example_ext.h deleted file mode 100644 index 5257a23..0000000 --- a/extensions/pl_example_ext.h +++ /dev/null @@ -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 \ No newline at end of file diff --git a/scripts/gen_build.py b/scripts/gen_build.py index b0ca50a..d0bff79 100644 --- a/scripts/gen_build.py +++ b/scripts/gen_build.py @@ -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 diff --git a/scripts/setup.py b/scripts/setup.py index d9e5f9c..000cf3c 100644 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -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" ] ######################################################################################################################## diff --git a/shaders/shader.comp b/shaders/shader.comp new file mode 100644 index 0000000..689635c --- /dev/null +++ b/shaders/shader.comp @@ -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); +} \ No newline at end of file diff --git a/src/app.c b/src/app.c index bb185d1..4b07028 100644 --- a/src/app.c +++ b/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); }