From 435d12136ee590d5cad234df8f629deb06b1b323 Mon Sep 17 00:00:00 2001 From: TheNumbat Date: Sun, 14 Jan 2024 18:38:21 -0500 Subject: [PATCH] shader reloading --- README.md | 1 - rvk/CMakeLists.txt | 17 +++++---- rvk/fwd.cpp | 87 +++++++++++++++++++++++++++++++++++++++++++ rvk/rvk.cpp | 85 ++---------------------------------------- rvk/rvk.h | 9 +++++ rvk/shader_loader.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++ rvk/shader_loader.h | 53 ++++++++++++++++++++++++++ 7 files changed, 249 insertions(+), 89 deletions(-) create mode 100644 rvk/fwd.cpp create mode 100644 rvk/shader_loader.cpp create mode 100644 rvk/shader_loader.h diff --git a/README.md b/README.md index 7098518..867c76c 100644 --- a/README.md +++ b/README.md @@ -78,5 +78,4 @@ For faster parallel builds, you can instead generate [ninja](https://ninja-build # ToDo Finish porting -- [ ] shader hot reloading - [ ] descriptor gen diff --git a/rvk/CMakeLists.txt b/rvk/CMakeLists.txt index 7346b75..377309f 100644 --- a/rvk/CMakeLists.txt +++ b/rvk/CMakeLists.txt @@ -3,29 +3,32 @@ cmake_minimum_required(VERSION 3.17) project(rvk LANGUAGES CXX) set(SOURCES_RVK - "fwd.h" "drop.h" "execute.h" + "fwd.h" + "fwd.cpp" "rvk.h" "rvk.cpp" - "instance.cpp" "instance.h" - "device.cpp" + "instance.cpp" "device.h" + "device.cpp" "swapchain.h" "swapchain.cpp" - "memory.cpp" "memory.h" - "descriptors.cpp" + "memory.cpp" "descriptors.h" + "descriptors.cpp" "commands.h" "commands.cpp" "pipeline.h" "pipeline.cpp" - "acceleration.cpp" "acceleration.h" - "imgui_impl_vulkan.cpp" + "acceleration.cpp" + "shader_loader.h" + "shader_loader.cpp" "imgui_impl_vulkan.h" + "imgui_impl_vulkan.cpp" ) if(UNIX AND NOT APPLE) diff --git a/rvk/fwd.cpp b/rvk/fwd.cpp new file mode 100644 index 0000000..e150b06 --- /dev/null +++ b/rvk/fwd.cpp @@ -0,0 +1,87 @@ + +#include "fwd.h" + +namespace rvk::impl { + +void check(VkResult result) { + RVK_CHECK(result); +} + +String_View describe(VkResult result) { + switch(result) { +#define STR(r) \ + case VK_##r: return #r##_v + STR(SUCCESS); + STR(NOT_READY); + STR(TIMEOUT); + STR(EVENT_SET); + STR(EVENT_RESET); + STR(INCOMPLETE); + STR(ERROR_OUT_OF_HOST_MEMORY); + STR(ERROR_OUT_OF_DEVICE_MEMORY); + STR(ERROR_INITIALIZATION_FAILED); + STR(ERROR_DEVICE_LOST); + STR(ERROR_MEMORY_MAP_FAILED); + STR(ERROR_LAYER_NOT_PRESENT); + STR(ERROR_EXTENSION_NOT_PRESENT); + STR(ERROR_FEATURE_NOT_PRESENT); + STR(ERROR_INCOMPATIBLE_DRIVER); + STR(ERROR_TOO_MANY_OBJECTS); + STR(ERROR_FORMAT_NOT_SUPPORTED); + STR(ERROR_SURFACE_LOST_KHR); + STR(ERROR_NATIVE_WINDOW_IN_USE_KHR); + STR(SUBOPTIMAL_KHR); + STR(ERROR_OUT_OF_DATE_KHR); + STR(ERROR_INCOMPATIBLE_DISPLAY_KHR); + STR(ERROR_VALIDATION_FAILED_EXT); + STR(ERROR_INVALID_SHADER_NV); +#undef STR + default: return "UNKNOWN_ERROR"_v; + } +} + +[[nodiscard]] String_View describe(VkObjectType type) { + switch(type) { +#define STR(r) \ + case VK_OBJECT_TYPE_##r: return #r##_v + STR(UNKNOWN); + STR(INSTANCE); + STR(PHYSICAL_DEVICE); + STR(DEVICE); + STR(QUEUE); + STR(SEMAPHORE); + STR(COMMAND_BUFFER); + STR(FENCE); + STR(DEVICE_MEMORY); + STR(BUFFER); + STR(IMAGE); + STR(EVENT); + STR(QUERY_POOL); + STR(BUFFER_VIEW); + STR(IMAGE_VIEW); + STR(SHADER_MODULE); + STR(PIPELINE_CACHE); + STR(PIPELINE_LAYOUT); + STR(RENDER_PASS); + STR(PIPELINE); + STR(DESCRIPTOR_SET_LAYOUT); + STR(SAMPLER); + STR(DESCRIPTOR_POOL); + STR(DESCRIPTOR_SET); + STR(FRAMEBUFFER); + STR(COMMAND_POOL); + STR(SAMPLER_YCBCR_CONVERSION); + STR(DESCRIPTOR_UPDATE_TEMPLATE); + STR(SURFACE_KHR); + STR(SWAPCHAIN_KHR); + STR(DISPLAY_KHR); + STR(DISPLAY_MODE_KHR); + STR(DEBUG_REPORT_CALLBACK_EXT); + STR(DEBUG_UTILS_MESSENGER_EXT); + STR(VALIDATION_CACHE_EXT); +#undef STR + default: return "UNKNOWN_OBJECT"_v; + } +} + +} // namespace rvk \ No newline at end of file diff --git a/rvk/rvk.cpp b/rvk/rvk.cpp index 66b031e..4ad64ce 100644 --- a/rvk/rvk.cpp +++ b/rvk/rvk.cpp @@ -16,87 +16,6 @@ using namespace rpp; namespace impl { -void check(VkResult result) { - RVK_CHECK(result); -} - -String_View describe(VkResult result) { - switch(result) { -#define STR(r) \ - case VK_##r: return #r##_v - STR(SUCCESS); - STR(NOT_READY); - STR(TIMEOUT); - STR(EVENT_SET); - STR(EVENT_RESET); - STR(INCOMPLETE); - STR(ERROR_OUT_OF_HOST_MEMORY); - STR(ERROR_OUT_OF_DEVICE_MEMORY); - STR(ERROR_INITIALIZATION_FAILED); - STR(ERROR_DEVICE_LOST); - STR(ERROR_MEMORY_MAP_FAILED); - STR(ERROR_LAYER_NOT_PRESENT); - STR(ERROR_EXTENSION_NOT_PRESENT); - STR(ERROR_FEATURE_NOT_PRESENT); - STR(ERROR_INCOMPATIBLE_DRIVER); - STR(ERROR_TOO_MANY_OBJECTS); - STR(ERROR_FORMAT_NOT_SUPPORTED); - STR(ERROR_SURFACE_LOST_KHR); - STR(ERROR_NATIVE_WINDOW_IN_USE_KHR); - STR(SUBOPTIMAL_KHR); - STR(ERROR_OUT_OF_DATE_KHR); - STR(ERROR_INCOMPATIBLE_DISPLAY_KHR); - STR(ERROR_VALIDATION_FAILED_EXT); - STR(ERROR_INVALID_SHADER_NV); -#undef STR - default: return "UNKNOWN_ERROR"_v; - } -} - -[[nodiscard]] String_View describe(VkObjectType type) { - switch(type) { -#define STR(r) \ - case VK_OBJECT_TYPE_##r: return #r##_v - STR(UNKNOWN); - STR(INSTANCE); - STR(PHYSICAL_DEVICE); - STR(DEVICE); - STR(QUEUE); - STR(SEMAPHORE); - STR(COMMAND_BUFFER); - STR(FENCE); - STR(DEVICE_MEMORY); - STR(BUFFER); - STR(IMAGE); - STR(EVENT); - STR(QUERY_POOL); - STR(BUFFER_VIEW); - STR(IMAGE_VIEW); - STR(SHADER_MODULE); - STR(PIPELINE_CACHE); - STR(PIPELINE_LAYOUT); - STR(RENDER_PASS); - STR(PIPELINE); - STR(DESCRIPTOR_SET_LAYOUT); - STR(SAMPLER); - STR(DESCRIPTOR_POOL); - STR(DESCRIPTOR_SET); - STR(FRAMEBUFFER); - STR(COMMAND_POOL); - STR(SAMPLER_YCBCR_CONVERSION); - STR(DESCRIPTOR_UPDATE_TEMPLATE); - STR(SURFACE_KHR); - STR(SWAPCHAIN_KHR); - STR(DISPLAY_KHR); - STR(DISPLAY_MODE_KHR); - STR(DEBUG_REPORT_CALLBACK_EXT); - STR(DEBUG_UTILS_MESSENGER_EXT); - STR(VALIDATION_CACHE_EXT); -#undef STR - default: return "UNKNOWN_OBJECT"_v; - } -} - struct Deletion_Queue { explicit Deletion_Queue() = default; @@ -594,6 +513,10 @@ Opt make_blas(Buffer& geometry, Vec offsets) return BLAS::make(impl::singleton->device_memory.dup(), geometry, move(offsets)); } +Shader_Loader make_shader_loader() { + return Shader_Loader{impl::singleton->device.dup()}; +} + void submit(Commands& cmds, u32 index) { impl::singleton->device->submit(cmds, index); } diff --git a/rvk/rvk.h b/rvk/rvk.h index 9baa4d8..65e1844 100644 --- a/rvk/rvk.h +++ b/rvk/rvk.h @@ -8,8 +8,11 @@ #include "acceleration.h" #include "commands.h" +#include "descriptors.h" #include "drop.h" #include "memory.h" +#include "pipeline.h" +#include "shader_loader.h" namespace rvk { @@ -21,8 +24,10 @@ using impl::Commands; using impl::Fence; using impl::Image; using impl::Image_View; +using impl::Pipeline; using impl::Sem_Ref; using impl::Semaphore; +using impl::Shader; using impl::TLAS; using Finalizer = FunctionN<8, void()>; @@ -77,6 +82,10 @@ Opt make_image(VkExtent3D extent, VkFormat format, VkImageUsageFlags usag Opt make_tlas(Buffer& instances, u32 n_instances); Opt make_blas(Buffer& geometry, Vec offsets); +// Pipelines + +Shader_Loader make_shader_loader(); + // Command execution void submit(Commands& cmds, u32 index); diff --git a/rvk/shader_loader.cpp b/rvk/shader_loader.cpp new file mode 100644 index 0000000..b0d5f02 --- /dev/null +++ b/rvk/shader_loader.cpp @@ -0,0 +1,86 @@ + +#include "shader_loader.h" + +#include + +namespace rvk { + +impl::Shader& Shader_Loader::get(Token token) { + return shaders.get(token).first; +} + +Shader_Loader::Token Shader_Loader::compile(String_View path) { + + if(Opt> data = Files::read(path)) { + + Shader shader{device.dup(), data->slice()}; + Files::Write_Watcher watcher{path}; + + Token token = next_token.incr(); + { + Thread::Lock lock{mutex}; + shaders.insert(token, Pair{move(shader), move(watcher)}); + } + + return token; + } + + die("[rvk] Failed to read shader from %!", path); +} + +Async::Task Shader_Loader::compile_async(Async::Pool<>& pool, + String_View path) { + + if(Opt> data = co_await Async::read(pool, path)) { + + // Will compile on another thread + Shader shader{device.dup(), data->slice()}; + Files::Write_Watcher watcher{path}; + + Token token = next_token.incr(); + { + Thread::Lock lock{mutex}; + shaders.insert(token, Pair{move(shader), move(watcher)}); + } + + co_return token; + } + + die("[rvk] Failed to read shader from %!", path); +} + +void Shader_Loader::try_reload() { + Thread::Lock lock{mutex}; + + Region(R) { + Map, Mregion> callbacks_to_run; + + for(auto& [token, shader] : shaders) { + if(shader.second.poll()) { + if(Opt> data = shader.second.read()) { + shader.first = Shader{device.dup(), data->slice()}; + callbacks_to_run.insert(reloads.get(token), {}); + } + } + } + + for(auto [token, _] : callbacks_to_run) { + static_cast(_); + callbacks.get(token)(*this); + } + } +} + +void Shader_Loader::on_reload(Slice tokens, + Function callback) { + Thread::Lock lock{mutex}; + + Reload_Token reload_token = next_reload_token++; + callbacks.insert(reload_token, move(callback)); + + for(auto token : tokens) { + reloads.insert(token, reload_token); + } +} + +} // namespace rvk diff --git a/rvk/shader_loader.h b/rvk/shader_loader.h new file mode 100644 index 0000000..f8f2f48 --- /dev/null +++ b/rvk/shader_loader.h @@ -0,0 +1,53 @@ + +#pragma once + +#include +#include +#include + +#include "fwd.h" + +#include "device.h" +#include "pipeline.h" + +namespace rvk { + +struct Shader_Loader { + using Token = u64; + using Shader = impl::Shader; + + ~Shader_Loader() = default; + + Shader_Loader(const Shader_Loader&) = delete; + Shader_Loader& operator=(const Shader_Loader&) = delete; + + Shader_Loader(Shader_Loader&&) = delete; + Shader_Loader& operator=(Shader_Loader&&) = delete; + + Shader& get(Token token); + + Token compile(String_View path); + Async::Task compile_async(Async::Pool<>& pool, String_View path); + + void try_reload(); + void on_reload(Slice shaders, Function callback); + +private: + using Device = impl::Device; + using Reload_Token = u64; + + explicit Shader_Loader(Arc device) : device(move(device)) { + } + friend Shader_Loader make_shader_loader(); + + Arc device; + Thread::Atomic next_token{1}; + Reload_Token next_reload_token = 1; + + Thread::Mutex mutex; + Map, Alloc> shaders; + Map reloads; + Map, Alloc> callbacks; +}; + +} // namespace rvk