From d563004bc100f408ec4c885c0fe68b62074e5589 Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:02:54 +0100 Subject: [PATCH 01/11] Move `PrimitiveTopology` to rendering lib. --- src/Graphics/include/litefx/graphics_api.hpp | 8 ----- .../include/litefx/graphics_formatters.hpp | 18 +--------- .../include/litefx/rendering_api.hpp | 34 +++++++++++++++++++ .../include/litefx/rendering_formatters.hpp | 16 +++++++++ 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/src/Graphics/include/litefx/graphics_api.hpp b/src/Graphics/include/litefx/graphics_api.hpp index da4298c41..7e8c7feef 100644 --- a/src/Graphics/include/litefx/graphics_api.hpp +++ b/src/Graphics/include/litefx/graphics_api.hpp @@ -21,12 +21,4 @@ namespace LiteFX::Graphics { using namespace LiteFX; - enum class PrimitiveTopology { - PointList = 0x00010001, - LineList = 0x00020001, - TriangleList = 0x00040001, - LineStrip = 0x00020002, - TriangleStrip = 0x00040002 - }; - } \ No newline at end of file diff --git a/src/Graphics/include/litefx/graphics_formatters.hpp b/src/Graphics/include/litefx/graphics_formatters.hpp index 76ce86b10..26420ec17 100644 --- a/src/Graphics/include/litefx/graphics_formatters.hpp +++ b/src/Graphics/include/litefx/graphics_formatters.hpp @@ -3,20 +3,4 @@ #include #include "graphics_api.hpp" -using namespace LiteFX::Graphics; - -template <> -struct LITEFX_GRAPHICS_API std::formatter : std::formatter { - auto format(LiteFX::Graphics::PrimitiveTopology t, std::format_context& ctx) const { - string_view name = "Invalid"; - switch (t) { - using enum PrimitiveTopology; - case PointList: name = "PointList"; break; - case LineList: name = "LineList"; break; - case TriangleList: name = "TriangleList"; break; - case LineStrip: name = "LineStrip"; break; - case TriangleStrip: name = "TriangleStrip"; break; - } - return formatter::format(name, ctx); - } -}; \ No newline at end of file +using namespace LiteFX::Graphics; \ No newline at end of file diff --git a/src/Rendering/include/litefx/rendering_api.hpp b/src/Rendering/include/litefx/rendering_api.hpp index d237282d5..decfbcf1e 100644 --- a/src/Rendering/include/litefx/rendering_api.hpp +++ b/src/Rendering/include/litefx/rendering_api.hpp @@ -758,6 +758,40 @@ namespace LiteFX::Rendering { UInt32 = 0x00000020 }; + /// + /// Describes the topology of a mesh primitive. + /// + enum class PrimitiveTopology { + /// + /// A list of points where each vertex refers to an individual point. + /// + PointList = 0x00010001, + + /// + /// A list of lines where each vertex pair refers to the start and end points of a line. + /// + /// + LineList = 0x00020001, + + /// + /// A list of triangles, where each triplet of vertices refers to a whole triangle. + /// + /// + TriangleList = 0x00040001, + + /// + /// A strip of lines where each vertex (except the first one) refers to the end point for the next line segment. + /// + /// + LineStrip = 0x00020002, + + /// + /// A strip of triangles, where each vertex (except the first two) refers to the third vertex of the next triangle segment. + /// + /// + TriangleStrip = 0x00040002 + }; + /// /// Describes the valid shader stages of a graphics pipeline. /// diff --git a/src/Rendering/include/litefx/rendering_formatters.hpp b/src/Rendering/include/litefx/rendering_formatters.hpp index 763092313..238bb7722 100644 --- a/src/Rendering/include/litefx/rendering_formatters.hpp +++ b/src/Rendering/include/litefx/rendering_formatters.hpp @@ -316,6 +316,22 @@ struct LITEFX_RENDERING_API std::formatter : std::formatter +struct LITEFX_RENDERING_API std::formatter : std::formatter { + auto format(LiteFX::Rendering::PrimitiveTopology t, std::format_context& ctx) const { + string_view name = "Invalid"; + switch (t) { + using enum PrimitiveTopology; + case PointList: name = "PointList"; break; + case LineList: name = "LineList"; break; + case TriangleList: name = "TriangleList"; break; + case LineStrip: name = "LineStrip"; break; + case TriangleStrip: name = "TriangleStrip"; break; + } + return formatter::format(name, ctx); + } +}; + template <> struct LITEFX_RENDERING_API std::formatter : std::formatter { auto format(ShaderStage t, std::format_context& ctx) const { From 64dbf1952df77e3e47af2e302036d890806a02d9 Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:33:48 +0100 Subject: [PATCH 02/11] Alter dependency between rendering and graphics libraries. --- src/Rendering/CMakeLists.txt | 2 +- src/Rendering/include/litefx/rendering_api.hpp | 1 - src/Samples/BasicRendering/CMakeLists.txt | 2 +- src/Samples/BasicRendering/src/sample.h | 2 ++ src/Samples/Bindless/CMakeLists.txt | 2 +- src/Samples/Bindless/src/sample.h | 2 ++ src/Samples/Compute/CMakeLists.txt | 2 +- src/Samples/Compute/src/sample.h | 2 ++ src/Samples/Indirect/CMakeLists.txt | 2 +- src/Samples/Indirect/src/sample.h | 2 ++ src/Samples/MeshShader/CMakeLists.txt | 2 +- src/Samples/MeshShader/src/sample.h | 2 ++ src/Samples/Multisampling/CMakeLists.txt | 2 +- src/Samples/Multisampling/src/sample.h | 2 ++ src/Samples/Multithreading/CMakeLists.txt | 2 +- src/Samples/Multithreading/src/sample.h | 2 ++ src/Samples/PushConstants/CMakeLists.txt | 2 +- src/Samples/PushConstants/src/sample.h | 2 ++ src/Samples/RayQueries/CMakeLists.txt | 2 +- src/Samples/RayQueries/src/sample.h | 2 ++ src/Samples/RayTracing/CMakeLists.txt | 2 +- src/Samples/RayTracing/src/sample.h | 2 ++ src/Samples/RenderPasses/CMakeLists.txt | 2 +- src/Samples/RenderPasses/src/sample.h | 2 ++ src/Samples/Textures/CMakeLists.txt | 2 +- src/Samples/Textures/src/sample.h | 2 ++ src/Samples/UniformArrays/CMakeLists.txt | 2 +- src/Samples/UniformArrays/src/sample.h | 2 ++ 28 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/Rendering/CMakeLists.txt b/src/Rendering/CMakeLists.txt index e3c103f72..c3421a383 100644 --- a/src/Rendering/CMakeLists.txt +++ b/src/Rendering/CMakeLists.txt @@ -61,7 +61,7 @@ TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} # Link project dependencies. TARGET_LINK_LIBRARIES(${PROJECT_NAME} - PUBLIC LiteFX.Core LiteFX.Math LiteFX.Graphics LiteFX.AppModel LiteFX.Logging + PUBLIC LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Logging ) # Pre-define export specifier, to prevent dllimport/dllexport from being be emitted. diff --git a/src/Rendering/include/litefx/rendering_api.hpp b/src/Rendering/include/litefx/rendering_api.hpp index decfbcf1e..80bd5ee42 100644 --- a/src/Rendering/include/litefx/rendering_api.hpp +++ b/src/Rendering/include/litefx/rendering_api.hpp @@ -18,7 +18,6 @@ #include #include -#include namespace LiteFX::Rendering { using namespace LiteFX; diff --git a/src/Samples/BasicRendering/CMakeLists.txt b/src/Samples/BasicRendering/CMakeLists.txt index 09f8ac25a..0fb1ee8e0 100644 --- a/src/Samples/BasicRendering/CMakeLists.txt +++ b/src/Samples/BasicRendering/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/BasicRendering/src/sample.h b/src/Samples/BasicRendering/src/sample.h index 7df2c39b2..4f1ed78ab 100644 --- a/src/Samples/BasicRendering/src/sample.h +++ b/src/Samples/BasicRendering/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/Bindless/CMakeLists.txt b/src/Samples/Bindless/CMakeLists.txt index c31843a96..9474ee794 100644 --- a/src/Samples/Bindless/CMakeLists.txt +++ b/src/Samples/Bindless/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/Bindless/src/sample.h b/src/Samples/Bindless/src/sample.h index 4d56f9438..8f9d9f2a2 100644 --- a/src/Samples/Bindless/src/sample.h +++ b/src/Samples/Bindless/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/Compute/CMakeLists.txt b/src/Samples/Compute/CMakeLists.txt index e5c66c5f1..4bebfc14b 100644 --- a/src/Samples/Compute/CMakeLists.txt +++ b/src/Samples/Compute/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/Compute/src/sample.h b/src/Samples/Compute/src/sample.h index 3b6d5c2c1..7393119a8 100644 --- a/src/Samples/Compute/src/sample.h +++ b/src/Samples/Compute/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/Indirect/CMakeLists.txt b/src/Samples/Indirect/CMakeLists.txt index c99695a8e..be3b2ac24 100644 --- a/src/Samples/Indirect/CMakeLists.txt +++ b/src/Samples/Indirect/CMakeLists.txt @@ -69,7 +69,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/Indirect/src/sample.h b/src/Samples/Indirect/src/sample.h index 503b4a639..e4a2170f1 100644 --- a/src/Samples/Indirect/src/sample.h +++ b/src/Samples/Indirect/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/MeshShader/CMakeLists.txt b/src/Samples/MeshShader/CMakeLists.txt index d17eb4411..0c6739d8b 100644 --- a/src/Samples/MeshShader/CMakeLists.txt +++ b/src/Samples/MeshShader/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/MeshShader/src/sample.h b/src/Samples/MeshShader/src/sample.h index 621c604c7..743ca5147 100644 --- a/src/Samples/MeshShader/src/sample.h +++ b/src/Samples/MeshShader/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/Multisampling/CMakeLists.txt b/src/Samples/Multisampling/CMakeLists.txt index 91323b6b6..dffee14e0 100644 --- a/src/Samples/Multisampling/CMakeLists.txt +++ b/src/Samples/Multisampling/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/Multisampling/src/sample.h b/src/Samples/Multisampling/src/sample.h index 011c42e96..40ce1bb4c 100644 --- a/src/Samples/Multisampling/src/sample.h +++ b/src/Samples/Multisampling/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/Multithreading/CMakeLists.txt b/src/Samples/Multithreading/CMakeLists.txt index 666c80bea..0f4542b06 100644 --- a/src/Samples/Multithreading/CMakeLists.txt +++ b/src/Samples/Multithreading/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/Multithreading/src/sample.h b/src/Samples/Multithreading/src/sample.h index 53270405c..6a7a5e6c2 100644 --- a/src/Samples/Multithreading/src/sample.h +++ b/src/Samples/Multithreading/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -25,6 +26,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/PushConstants/CMakeLists.txt b/src/Samples/PushConstants/CMakeLists.txt index 40bc4cdf5..501446e5d 100644 --- a/src/Samples/PushConstants/CMakeLists.txt +++ b/src/Samples/PushConstants/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/PushConstants/src/sample.h b/src/Samples/PushConstants/src/sample.h index ea1fefb7d..9734ae904 100644 --- a/src/Samples/PushConstants/src/sample.h +++ b/src/Samples/PushConstants/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/RayQueries/CMakeLists.txt b/src/Samples/RayQueries/CMakeLists.txt index 1cc9c090f..4e394055b 100644 --- a/src/Samples/RayQueries/CMakeLists.txt +++ b/src/Samples/RayQueries/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/RayQueries/src/sample.h b/src/Samples/RayQueries/src/sample.h index 60fb52263..66bf7bfac 100644 --- a/src/Samples/RayQueries/src/sample.h +++ b/src/Samples/RayQueries/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/RayTracing/CMakeLists.txt b/src/Samples/RayTracing/CMakeLists.txt index 8cdeebe99..33b08348e 100644 --- a/src/Samples/RayTracing/CMakeLists.txt +++ b/src/Samples/RayTracing/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/RayTracing/src/sample.h b/src/Samples/RayTracing/src/sample.h index e6dfe2e7e..ff9b0b0a5 100644 --- a/src/Samples/RayTracing/src/sample.h +++ b/src/Samples/RayTracing/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/RenderPasses/CMakeLists.txt b/src/Samples/RenderPasses/CMakeLists.txt index ee6b4da28..d5ec83e59 100644 --- a/src/Samples/RenderPasses/CMakeLists.txt +++ b/src/Samples/RenderPasses/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/RenderPasses/src/sample.h b/src/Samples/RenderPasses/src/sample.h index 1e8bca943..79dd4e8b1 100644 --- a/src/Samples/RenderPasses/src/sample.h +++ b/src/Samples/RenderPasses/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/Textures/CMakeLists.txt b/src/Samples/Textures/CMakeLists.txt index 676a85a00..7615444cb 100644 --- a/src/Samples/Textures/CMakeLists.txt +++ b/src/Samples/Textures/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/Textures/src/sample.h b/src/Samples/Textures/src/sample.h index 49b868ce6..c6c0affa1 100644 --- a/src/Samples/Textures/src/sample.h +++ b/src/Samples/Textures/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/UniformArrays/CMakeLists.txt b/src/Samples/UniformArrays/CMakeLists.txt index c4f2b12f5..89bd39e89 100644 --- a/src/Samples/UniformArrays/CMakeLists.txt +++ b/src/Samples/UniformArrays/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/UniformArrays/src/sample.h b/src/Samples/UniformArrays/src/sample.h index dcb8d1d87..ccfdba4cd 100644 --- a/src/Samples/UniformArrays/src/sample.h +++ b/src/Samples/UniformArrays/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { From 259c8618468c9521c25aaeb4bace60e524760cc8 Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:33:55 +0100 Subject: [PATCH 03/11] Document vertex type. --- src/Graphics/include/litefx/graphics.hpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Graphics/include/litefx/graphics.hpp b/src/Graphics/include/litefx/graphics.hpp index 24a8c43e9..91da39445 100644 --- a/src/Graphics/include/litefx/graphics.hpp +++ b/src/Graphics/include/litefx/graphics.hpp @@ -8,11 +8,29 @@ namespace LiteFX::Graphics { using namespace LiteFX; using namespace LiteFX::Math; - struct LITEFX_GRAPHICS_API Vertex { + /// + /// Default definition for a simple vertex. + /// + struct Vertex { public: + /// + /// The position of the vertex. + /// Vector3f Position; + + /// + /// The color of the vertex. + /// Vector4f Color; + + /// + /// The normal vector of the vertex. + /// Vector3f Normal; + + /// + /// The texture coordinate of the vertex. + /// Vector2f TextureCoordinate0; }; From 0a344a8e8a05e5780d1ee32cfa1a79d89da0833c Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:37:26 +0100 Subject: [PATCH 04/11] Restructure graphics library headers. --- src/Graphics/CMakeLists.txt | 14 ++++++-- src/Graphics/include/litefx/gfx/blit.hpp | 9 +++++ src/Graphics/include/litefx/gfx/vertex.hpp | 36 +++++++++++++++++++ src/Graphics/include/litefx/graphics.hpp | 36 ++----------------- src/Graphics/include/litefx/graphics_api.hpp | 7 +--- .../include/litefx/graphics_formatters.hpp | 6 ---- 6 files changed, 59 insertions(+), 49 deletions(-) create mode 100644 src/Graphics/include/litefx/gfx/blit.hpp create mode 100644 src/Graphics/include/litefx/gfx/vertex.hpp delete mode 100644 src/Graphics/include/litefx/graphics_formatters.hpp diff --git a/src/Graphics/CMakeLists.txt b/src/Graphics/CMakeLists.txt index 09ff70394..cdfeceddc 100644 --- a/src/Graphics/CMakeLists.txt +++ b/src/Graphics/CMakeLists.txt @@ -10,8 +10,10 @@ MESSAGE(STATUS "Initializing: ${PROJECT_NAME}...") # Collect header & source files. SET(GRAPHICS_HEADERS "include/litefx/graphics_api.hpp" - "include/litefx/graphics_formatters.hpp" "include/litefx/graphics.hpp" + + "include/litefx/gfx/blit.hpp" + "include/litefx/gfx/vertex.hpp" ) SET(GRAPHICS_SOURCES @@ -20,12 +22,18 @@ SET(GRAPHICS_SOURCES # Add shared library project. ADD_LIBRARY(${PROJECT_NAME} - ${GRAPHICS_HEADERS} ${GRAPHICS_SOURCES} ".natvis" ) +TARGET_SOURCES(${PROJECT_NAME} PUBLIC + FILE_SET public_headers + TYPE HEADERS + BASE_DIRS "include/" + FILES ${GRAPHICS_HEADERS} +) + # Create source groups for better code organization. SOURCE_GROUP(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${GRAPHICS_HEADERS} ${GRAPHICS_SOURCES}) @@ -68,7 +76,7 @@ INSTALL(TARGETS ${PROJECT_NAME} EXPORT EXPORT LiteFX ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBRARY_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBRARY_DIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINARY_DIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDE_DIR}/litefx/ + FILE_SET public_headers DESTINATION ${CMAKE_INSTALL_INCLUDE_DIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDE_DIR} ) diff --git a/src/Graphics/include/litefx/gfx/blit.hpp b/src/Graphics/include/litefx/gfx/blit.hpp new file mode 100644 index 000000000..66d2778c7 --- /dev/null +++ b/src/Graphics/include/litefx/gfx/blit.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +namespace LiteFX::Graphics { + using namespace LiteFX; + +} \ No newline at end of file diff --git a/src/Graphics/include/litefx/gfx/vertex.hpp b/src/Graphics/include/litefx/gfx/vertex.hpp new file mode 100644 index 000000000..f5faa7314 --- /dev/null +++ b/src/Graphics/include/litefx/gfx/vertex.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +namespace LiteFX::Graphics { + using namespace LiteFX; + using namespace LiteFX::Math; + + /// + /// Default definition for a simple vertex. + /// + struct Vertex { + public: + /// + /// The position of the vertex. + /// + Vector3f Position; + + /// + /// The color of the vertex. + /// + Vector4f Color; + + /// + /// The normal vector of the vertex. + /// + Vector3f Normal; + + /// + /// The texture coordinate of the vertex. + /// + Vector2f TextureCoordinate0; + }; + +} \ No newline at end of file diff --git a/src/Graphics/include/litefx/graphics.hpp b/src/Graphics/include/litefx/graphics.hpp index 91da39445..846df15bf 100644 --- a/src/Graphics/include/litefx/graphics.hpp +++ b/src/Graphics/include/litefx/graphics.hpp @@ -1,37 +1,5 @@ #pragma once #include -#include -#include - -namespace LiteFX::Graphics { - using namespace LiteFX; - using namespace LiteFX::Math; - - /// - /// Default definition for a simple vertex. - /// - struct Vertex { - public: - /// - /// The position of the vertex. - /// - Vector3f Position; - - /// - /// The color of the vertex. - /// - Vector4f Color; - - /// - /// The normal vector of the vertex. - /// - Vector3f Normal; - - /// - /// The texture coordinate of the vertex. - /// - Vector2f TextureCoordinate0; - }; - -} \ No newline at end of file +#include +#include \ No newline at end of file diff --git a/src/Graphics/include/litefx/graphics_api.hpp b/src/Graphics/include/litefx/graphics_api.hpp index 7e8c7feef..de616fe0f 100644 --- a/src/Graphics/include/litefx/graphics_api.hpp +++ b/src/Graphics/include/litefx/graphics_api.hpp @@ -16,9 +16,4 @@ # define LITEFX_GRAPHICS_API #endif -#include - -namespace LiteFX::Graphics { - using namespace LiteFX; - -} \ No newline at end of file +#include \ No newline at end of file diff --git a/src/Graphics/include/litefx/graphics_formatters.hpp b/src/Graphics/include/litefx/graphics_formatters.hpp deleted file mode 100644 index 26420ec17..000000000 --- a/src/Graphics/include/litefx/graphics_formatters.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include -#include "graphics_api.hpp" - -using namespace LiteFX::Graphics; \ No newline at end of file From cd7ecfe51dcd33ca923eaedc8f5cb288383525ae Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:22:39 +0100 Subject: [PATCH 05/11] Add concepts for backend types. --- src/AppModel/include/litefx/app.hpp | 6 ++++++ src/Rendering/include/litefx/rendering_api.hpp | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/src/AppModel/include/litefx/app.hpp b/src/AppModel/include/litefx/app.hpp index cfedac501..c5d236b6e 100644 --- a/src/AppModel/include/litefx/app.hpp +++ b/src/AppModel/include/litefx/app.hpp @@ -66,6 +66,12 @@ namespace LiteFX { std::type_index typeId() const noexcept { return typeid(*this); } }; + /// + /// Concept that can be used to refer to backend implementations. + /// + template + concept backend = meta::implements; + /// /// Base class for additional event arguments. /// diff --git a/src/Rendering/include/litefx/rendering_api.hpp b/src/Rendering/include/litefx/rendering_api.hpp index 80bd5ee42..10ff2fb70 100644 --- a/src/Rendering/include/litefx/rendering_api.hpp +++ b/src/Rendering/include/litefx/rendering_api.hpp @@ -8734,4 +8734,11 @@ namespace LiteFX::Rendering { private: virtual Enumerable getAdapters() const = 0; }; + + /// + /// Concept that can be used to refer to render backend implementations. + /// + template + concept render_backend = meta::implements; + } \ No newline at end of file From 2fe77a604191ea5f7a295278e3072f12be62d8cc Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:58:11 +0100 Subject: [PATCH 06/11] Prototype mip map generation utility. --- src/Graphics/CMakeLists.txt | 26 +++- src/Graphics/include/litefx/gfx/blit.hpp | 9 -- src/Graphics/include/litefx/gfx/blitter.hpp | 80 ++++++++++ src/Graphics/include/litefx/graphics.hpp | 2 +- src/Graphics/shaders/blit.hlsl | 38 +++++ src/Graphics/src/blitter_d3d12.cpp | 163 ++++++++++++++++++++ src/Graphics/src/blitter_vk.cpp | 90 +++++++++++ src/Graphics/src/vertex.cpp | 1 - src/Samples/Textures/src/sample.cpp | 11 +- 9 files changed, 400 insertions(+), 20 deletions(-) delete mode 100644 src/Graphics/include/litefx/gfx/blit.hpp create mode 100644 src/Graphics/include/litefx/gfx/blitter.hpp create mode 100644 src/Graphics/shaders/blit.hlsl create mode 100644 src/Graphics/src/blitter_d3d12.cpp create mode 100644 src/Graphics/src/blitter_vk.cpp delete mode 100644 src/Graphics/src/vertex.cpp diff --git a/src/Graphics/CMakeLists.txt b/src/Graphics/CMakeLists.txt index cdfeceddc..241ff604a 100644 --- a/src/Graphics/CMakeLists.txt +++ b/src/Graphics/CMakeLists.txt @@ -12,12 +12,13 @@ SET(GRAPHICS_HEADERS "include/litefx/graphics_api.hpp" "include/litefx/graphics.hpp" - "include/litefx/gfx/blit.hpp" + "include/litefx/gfx/blitter.hpp" "include/litefx/gfx/vertex.hpp" ) SET(GRAPHICS_SOURCES - "src/vertex.cpp" + "src/blitter_vk.cpp" + "src/blitter_d3d12.cpp" ) # Add shared library project. @@ -58,9 +59,28 @@ TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} # Link project dependencies. TARGET_LINK_LIBRARIES(${PROJECT_NAME} - PUBLIC LiteFX.Core LiteFX.Logging LiteFX.Math + PUBLIC LiteFX.Core LiteFX.Logging LiteFX.Math LiteFX.Rendering ) +# Link supported backends. +IF(LITEFX_BUILD_DIRECTX_12_BACKEND) + TARGET_LINK_LIBRARIES(${PROJECT_NAME} PUBLIC LiteFX.Backends.DirectX12) + + # Add shader modules. + ADD_SHADER_LIBRARY(${PROJECT_NAME}.Shaders SOURCE_FILE "shader_resources.hpp" NAMESPACE "LiteFX::Graphics::Shaders") + ADD_SHADER_MODULE(${PROJECT_NAME}.Blit SOURCE "shaders/blit.hlsl" LANGUAGE HLSL TYPE COMPUTE COMPILE_AS DXIL SHADER_MODEL ${LITEFX_BUILD_HLSL_SHADER_MODEL} COMPILER DXC LIBRARY ${PROJECT_NAME}.Shaders) + SET_TARGET_PROPERTIES(${PROJECT_NAME}.Shaders PROPERTIES FOLDER "SDK/Graphics/Shaders") + SET_TARGET_PROPERTIES(${PROJECT_NAME}.Blit PROPERTIES FOLDER "SDK/Graphics/Shaders") + + TARGET_LINK_SHADER_LIBRARIES(${PROJECT_NAME} + LIBRARIES ${PROJECT_NAME}.Shaders + ) +ENDIF(LITEFX_BUILD_DIRECTX_12_BACKEND) + +IF(LITEFX_BUILD_VULKAN_BACKEND) + TARGET_LINK_LIBRARIES(${PROJECT_NAME} PUBLIC LiteFX.Backends.Vulkan) +ENDIF(LITEFX_BUILD_VULKAN_BACKEND) + # Pre-define export specifier, to prevent dllimport/dllexport from being be emitted. IF(NOT BUILD_SHARED_LIBS) TARGET_COMPILE_DEFINITIONS(${PROJECT_NAME} PUBLIC -DLITEFX_GRAPHICS_API=) diff --git a/src/Graphics/include/litefx/gfx/blit.hpp b/src/Graphics/include/litefx/gfx/blit.hpp deleted file mode 100644 index 66d2778c7..000000000 --- a/src/Graphics/include/litefx/gfx/blit.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include -#include - -namespace LiteFX::Graphics { - using namespace LiteFX; - -} \ No newline at end of file diff --git a/src/Graphics/include/litefx/gfx/blitter.hpp b/src/Graphics/include/litefx/gfx/blitter.hpp new file mode 100644 index 000000000..baf101174 --- /dev/null +++ b/src/Graphics/include/litefx/gfx/blitter.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include +#include + +#ifdef LITEFX_BUILD_VULKAN_BACKEND +#include +#endif // LITEFX_BUILD_VULKAN_BACKEND + +#ifdef LITEFX_BUILD_DIRECTX_12_BACKEND +#include +#endif // LITEFX_BUILD_DIRECTX_12_BACKEND + +namespace LiteFX::Graphics { + using namespace LiteFX; + + //class IBlitter { + // virtual blit(IImage, ICommandBuffer) + // virtual generateMipMaps(IImage, ICommandBuffer) + //}; + + /// + /// Utility class that can be used to issue blit commands and generate mip maps. + /// + /// + /// This utility class can be used to generate mip maps for images. Note however, that it is more efficient to pre-compute mip maps if possible. Also note that if + /// you need a direct copy of a image, use a command instead. + /// + /// The type of render backend that implements the blitter. + template + class Blitter /*: public LiteFX::SharedObject*/ { + LITEFX_IMPLEMENTATION(BlitImpl); + + private: + /// + /// Initializes a new blitter instance. + /// + /// The device to allocate resources from. + explicit Blitter(const TBackend::device_type& device); + + /// + Blitter(const Blitter&) = delete; + + /// + Blitter(Blitter&&) noexcept = delete; + + /// + Blitter& operator=(const Blitter&) = delete; + + /// + Blitter& operator=(Blitter&&) noexcept = delete; + + /// + ~Blitter() noexcept /*override*/; + + public: + /// + /// Creates a new blitter instance. + /// + /// The device to allocate resources from. + /// A shared pointer to the newly created blitter instance. + static inline auto create(const TBackend::device_type& device) { + //return SharedObject::create>(device); + throw; + } + + public: + //void blit(TBackend::image_type& image, TBackend::command_buffer_type& commandBuffer + void generateMipMaps(TBackend::image_type& image, TBackend::command_buffer_type& commandBuffer) /*override*/; + }; + +#ifdef LITEFX_BUILD_VULKAN_BACKEND + template class LITEFX_GRAPHICS_API Blitter; +#endif // LITEFX_BUILD_VULKAN_BACKEND + +#ifdef LITEFX_BUILD_DIRECTX_12_BACKEND + template class LITEFX_GRAPHICS_API Blitter; +#endif // LITEFX_BUILD_DIRECTX_12_BACKEND + +} \ No newline at end of file diff --git a/src/Graphics/include/litefx/graphics.hpp b/src/Graphics/include/litefx/graphics.hpp index 846df15bf..4f3c7e743 100644 --- a/src/Graphics/include/litefx/graphics.hpp +++ b/src/Graphics/include/litefx/graphics.hpp @@ -1,5 +1,5 @@ #pragma once #include -#include +#include #include \ No newline at end of file diff --git a/src/Graphics/shaders/blit.hlsl b/src/Graphics/shaders/blit.hlsl new file mode 100644 index 000000000..afd4a3c85 --- /dev/null +++ b/src/Graphics/shaders/blit.hlsl @@ -0,0 +1,38 @@ +struct BlitParameters +{ + float2 TexelSize; // Size of a single texel in UV coordinates ([0..1]). + float isSRGB; + float Padding; +}; + +ConstantBuffer input : register(b0, space0); +Texture2D parent : register(t1, space0); +RWTexture2D result : register(u2, space0); +SamplerState linearFilter : register(s0, space1); + +float3 applySRGB(float3 x) +{ + // See: https://github.com/Microsoft/DirectX-Graphics-Samples/blob/master/MiniEngine/Core/Shaders/GenerateMipsCS.hlsli#L55 + //return x < 0.0031308 ? 12.92 * x : 1.055 * pow(abs(x), 1.0 / 2.4) - 0.055; + return float3( + x.r < 0.0031308 ? 12.92 * x.r : 1.13005 * sqrt(abs(x.r - 0.00228)) - 0.13448 * x.r + 0.005719, + x.g < 0.0031308 ? 12.92 * x.g : 1.13005 * sqrt(abs(x.g - 0.00228)) - 0.13448 * x.g + 0.005719, + x.b < 0.0031308 ? 12.92 * x.b : 1.13005 * sqrt(abs(x.b - 0.00228)) - 0.13448 * x.b + 0.005719 + ); +} + +float4 packColor(float4 color) +{ + if (input.isSRGB > 0.0) + return float4(applySRGB(color.rgb), color.a); + else + return color; +} + +[numthreads(8, 8, 1)] +void main(uint3 threadId : SV_DispatchThreadID) +{ + float2 texcoords = input.TexelSize * (threadId.xy + 0.5); + float4 color = parent.SampleLevel(linearFilter, texcoords, 0); + result[threadId.xy] = packColor(color); +} \ No newline at end of file diff --git a/src/Graphics/src/blitter_d3d12.cpp b/src/Graphics/src/blitter_d3d12.cpp new file mode 100644 index 000000000..3d523390e --- /dev/null +++ b/src/Graphics/src/blitter_d3d12.cpp @@ -0,0 +1,163 @@ +#include + +using namespace LiteFX::Graphics; +using namespace LiteFX::Rendering::Backends; + +#ifdef LITEFX_BUILD_DIRECTX_12_BACKEND + +#include + +// ------------------------------------------------------------------------------------------------ +// Implementation. +// ------------------------------------------------------------------------------------------------ + +template<> +class Blitter::BlitImpl { + friend class Blitter; + +private: + WeakPtr m_device; + UniquePtr m_pipeline; + SharedPtr m_sampler; + +public: + BlitImpl(const DirectX12Device& device) : + m_device(device.weak_from_this()) + { + } + +public: + void initialize(const DirectX12Device& device) + { + // Allocate shader module. + Array> modules; + auto blitShader = LiteFX::Graphics::Shaders::blit_dxi::open(); + modules.push_back(std::move(makeUnique(device, ShaderStage::Compute, blitShader, LiteFX::Graphics::Shaders::blit_dxi::name(), "main"))); + auto shaderProgram = DirectX12ShaderProgram::create(device, std::move(modules | std::views::as_rvalue)); + + // Allocate descriptor set layouts. + UniquePtr pushConstantsLayout = nullptr; + auto bufferLayouts = Enumerable>( + makeUnique(DescriptorType::ConstantBuffer, 0, 16), + makeUnique(DescriptorType::Texture, 1, 0), + makeUnique(DescriptorType::RWTexture, 2, 0) + ); + auto samplerLayouts = Enumerable>( + makeUnique(DescriptorType::Sampler, 0, 0) + ); + auto descriptorSetLayouts = Enumerable>( + makeUnique(device, std::move(bufferLayouts), 0, ShaderStage::Compute), + makeUnique(device, std::move(samplerLayouts), 1, ShaderStage::Compute) + ); + + // Create a pipeline layout. + auto pipelineLayout = makeShared(device, std::move(descriptorSetLayouts), std::move(pushConstantsLayout)); + + // Create the pipeline. + m_pipeline = makeUnique(device, pipelineLayout, shaderProgram, "Blit"); + + // Create the sampler state. + auto sampler = device.factory().createSampler(FilterMode::Linear, FilterMode::Linear, BorderMode::ClampToEdge, BorderMode::ClampToEdge, BorderMode::ClampToEdge); + } +}; + +// ------------------------------------------------------------------------------------------------ +// Shared interface. +// ------------------------------------------------------------------------------------------------ + +Blitter::Blitter(const DirectX12Device& device) : + m_impl(device) +{ + m_impl->initialize(device); +} + +Blitter::~Blitter() noexcept = default; + +void Blitter::generateMipMaps(IDirectX12Image& image, DirectX12CommandBuffer& commandBuffer) +{ + auto device = m_impl->m_device.lock(); + + if (device == nullptr) [[unlikely]] + throw RuntimeException("Unable to generate mip maps on a device that has been released."); + + struct Parameters { + Float sizeX; + Float sizeY; + Float sRGB; + Float padding; + }; + + // Create the array of parameter data. + Array parametersData(image.levels()); + + std::ranges::generate(parametersData, [this, &image, i = 0]() mutable { + auto level = i++; + + return Parameters{ + .sizeX = 1.f / static_cast(std::max(image.extent(level).width(), 1)), + .sizeY = 1.f / static_cast(std::max(image.extent(level).height(), 1)), + .sRGB = DX12::isSRGB(image.format()) ? 1.f : 0.f + }; + }); + + auto parametersBlock = parametersData | + std::views::transform([](const Parameters& parameters) { return reinterpret_cast(¶meters); }) | + std::ranges::to>(); + + // Set the active pipeline state. + auto& pipeline = *m_impl->m_pipeline; + commandBuffer.use(pipeline); + + // Create and bind the parameters. + const auto& resourceBindingsLayout = pipeline.layout()->descriptorSet(0); + auto resourceBindings = resourceBindingsLayout.allocateMultiple(image.levels() * image.layers()); + const auto& parametersLayout = resourceBindingsLayout.descriptor(0); + auto parameters = device->factory().createBuffer(parametersLayout.type(), ResourceHeap::Dynamic, parametersLayout.elementSize(), image.levels()); + parameters->map(parametersBlock, sizeof(Parameters)); + + // Create and bind the sampler. + const auto& samplerBindingsLayout = pipeline.layout()->descriptorSet(1); + auto samplerBindings = samplerBindingsLayout.allocate(); + samplerBindings->update(0, *m_impl->m_sampler); + commandBuffer.bind(*samplerBindings, pipeline); + + // Transition the texture into a read/write state. + DirectX12Barrier startBarrier(PipelineStage::All, PipelineStage::Compute); + startBarrier.transition(image, ResourceAccess::None, ResourceAccess::ShaderReadWrite, ImageLayout::Undefined, ImageLayout::ReadWrite); + commandBuffer.barrier(startBarrier); + auto resource = resourceBindings.begin(); + + for (int l(0); l < image.layers(); ++l, ++resource) + { + auto size = image.extent(); + + for (UInt32 i(1); i < image.levels(); ++i, size /= 2) + { + // Update the invocation parameters. + (*resource)->update(parametersLayout.binding(), *parameters, i, 1); + + // Bind the previous mip map level to the SRV at binding point 1. + (*resource)->update(1, image, 0, i - 1, 1, l, 1); + + // Bind the current level to the UAV at binding point 2. + (*resource)->update(2, image, 0, i, 1, l, 1); + + // Dispatch the pipeline. + commandBuffer.bind(*(*resource), pipeline); + commandBuffer.dispatch({ std::max(size.width() / 8, 1), std::max(size.height() / 8, 1), 1 }); + + // Wait for all writes. + DirectX12Barrier subBarrier(PipelineStage::Compute, PipelineStage::Compute); + subBarrier.transition(image, i, 1, l, 1, 0, ResourceAccess::ShaderReadWrite, ResourceAccess::ShaderRead, ImageLayout::ReadWrite, ImageLayout::ShaderResource); + commandBuffer.barrier(subBarrier); + resource++; + } + + // Original sub-resource also needs to be transitioned. + DirectX12Barrier endBarrier(PipelineStage::Compute, PipelineStage::All); + endBarrier.transition(image, 0, 1, l, 1, 0, ResourceAccess::ShaderReadWrite, ResourceAccess::ShaderRead, ImageLayout::ReadWrite, ImageLayout::ShaderResource); + commandBuffer.barrier(endBarrier); + } +} + +#endif // LITEFX_BUILD_DIRECTX_12_BACKEND \ No newline at end of file diff --git a/src/Graphics/src/blitter_vk.cpp b/src/Graphics/src/blitter_vk.cpp new file mode 100644 index 000000000..6800638b1 --- /dev/null +++ b/src/Graphics/src/blitter_vk.cpp @@ -0,0 +1,90 @@ +#include + +using namespace LiteFX::Graphics; +using namespace LiteFX::Rendering::Backends; + +#ifdef LITEFX_BUILD_VULKAN_BACKEND + +// ------------------------------------------------------------------------------------------------ +// Implementation. +// ------------------------------------------------------------------------------------------------ + +template<> +class Blitter::BlitImpl { + friend class Blitter; + +public: + +private: +}; + +// ------------------------------------------------------------------------------------------------ +// Shared interface. +// ------------------------------------------------------------------------------------------------ + +Blitter::Blitter(const VulkanDevice& /*device*/) : + m_impl() +{ +} + +Blitter::~Blitter() noexcept = default; + +void Blitter::generateMipMaps(IVulkanImage& image, VulkanCommandBuffer& commandBuffer) +{ + VulkanBarrier startBarrier(PipelineStage::None, PipelineStage::Transfer); + startBarrier.transition(image, ResourceAccess::None, ResourceAccess::TransferWrite, ImageLayout::Undefined, ImageLayout::CopyDestination); + commandBuffer.barrier(startBarrier); + + for (UInt32 layer(0); layer < image.layers(); ++layer) + { + Int32 mipWidth = static_cast(image.extent().width()); + Int32 mipHeight = static_cast(image.extent().height()); + Int32 mipDepth = static_cast(image.extent().depth()); + + for (UInt32 level(1); level < image.levels(); ++level) + { + VulkanBarrier subBarrier(PipelineStage::Transfer, PipelineStage::Transfer); + subBarrier.transition(image, level - 1, 1, layer, 1, 0, ResourceAccess::TransferWrite, ResourceAccess::TransferRead, ImageLayout::CopySource); + commandBuffer.barrier(subBarrier); + + // Blit the image of the previous level into the current level. + VkImageBlit blit{ + .srcSubresource = VkImageSubresourceLayers { + .aspectMask = image.aspectMask(), + .mipLevel = level - 1, + .baseArrayLayer = layer, + .layerCount = 1 + }, + .dstSubresource = VkImageSubresourceLayers { + .aspectMask = image.aspectMask(), + .mipLevel = level, + .baseArrayLayer = layer, + .layerCount = 1 + } + }; + + blit.srcOffsets[0] = { 0, 0, 0 }; + blit.srcOffsets[1] = { mipWidth, mipHeight, mipDepth }; + blit.dstOffsets[0] = { 0, 0, 0 }; + blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, mipDepth > 1 ? mipDepth / 2 : 1 }; + + ::vkCmdBlitImage(std::as_const(commandBuffer).handle(), std::as_const(image).handle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, std::as_const(image).handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR); + + // Compute the new size. + mipWidth = std::max(mipWidth / 2, 1); + mipHeight = std::max(mipHeight / 2, 1); + mipDepth = std::max(mipDepth / 2, 1); + } + + VulkanBarrier subBarrier(PipelineStage::Transfer, PipelineStage::Transfer); + subBarrier.transition(image, image.levels() - 1, 1, layer, 1, 0, ResourceAccess::TransferWrite, ResourceAccess::TransferRead, ImageLayout::CopySource); + subBarrier.transition(image, 0, 1, layer, 1, 0, ResourceAccess::TransferWrite, ResourceAccess::TransferRead, ImageLayout::CopySource); + commandBuffer.barrier(subBarrier); + } + + VulkanBarrier endBarrier(PipelineStage::Transfer, PipelineStage::All); + endBarrier.transition(image, ResourceAccess::TransferRead | ResourceAccess::TransferWrite, ResourceAccess::ShaderRead, ImageLayout::ShaderResource); + commandBuffer.barrier(endBarrier); +} + +#endif // LITEFX_BUILD_VULKAN_BACKEND \ No newline at end of file diff --git a/src/Graphics/src/vertex.cpp b/src/Graphics/src/vertex.cpp deleted file mode 100644 index cf8a71b32..000000000 --- a/src/Graphics/src/vertex.cpp +++ /dev/null @@ -1 +0,0 @@ -#include \ No newline at end of file diff --git a/src/Samples/Textures/src/sample.cpp b/src/Samples/Textures/src/sample.cpp index 5ea9df2f4..b3b49f58e 100644 --- a/src/Samples/Textures/src/sample.cpp +++ b/src/Samples/Textures/src/sample.cpp @@ -106,8 +106,7 @@ void initRenderGraph(TRenderBackend* backend, SharedPtr& inputA std::ranges::for_each(frameBuffers, [device](auto& frameBuffer) { device->state().add(std::move(frameBuffer)); }); } -template requires - meta::implements +template void loadTexture(TDevice& device, UniquePtr& texture, UniquePtr& sampler) { using TBarrier = typename TDevice::barrier_type; @@ -136,7 +135,8 @@ void loadTexture(TDevice& device, UniquePtr& texture, UniquePtrtransfer(imageData.get(), texture->size(0), *texture); // Generate the rest of the mip maps. - commandBuffer->generateMipMaps(*texture); + auto blitter = Blitter::create(device); + blitter->generateMipMaps(*texture, *commandBuffer); // Create a barrier to ensure the texture is readable. barrier = device.buildBarrier() @@ -153,8 +153,7 @@ void loadTexture(TDevice& device, UniquePtr& texture, UniquePtr::max(), 0.f, 16.f); } -template requires - meta::implements +template UInt64 initBuffers(SampleApp& app, TDevice& device, SharedPtr inputAssembler) { // Get a command buffer @@ -284,7 +283,7 @@ void SampleApp::onInit() // Initialize resources. ::initRenderGraph(backend, m_inputAssembler); - m_transferFence = ::initBuffers(*this, *device, m_inputAssembler); + m_transferFence = ::initBuffers(*this, *device, m_inputAssembler); return true; }; From e94fec65d121e38ccddfb74cf00f257c9bd31b72 Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:58:26 +0100 Subject: [PATCH 07/11] Remove mip map generation from rendering code. --- src/Backends/DirectX12/CMakeLists.txt | 10 --- .../include/litefx/backends/dx12.hpp | 14 ---- src/Backends/DirectX12/shaders/blit.hlsl | 38 --------- src/Backends/DirectX12/src/command_buffer.cpp | 83 ------------------- src/Backends/DirectX12/src/device.cpp | 45 ---------- .../Vulkan/include/litefx/backends/vulkan.hpp | 4 - src/Backends/Vulkan/src/command_buffer.cpp | 58 ------------- src/Rendering/include/litefx/rendering.hpp | 8 -- .../include/litefx/rendering_api.hpp | 18 ---- 9 files changed, 278 deletions(-) delete mode 100644 src/Backends/DirectX12/shaders/blit.hlsl diff --git a/src/Backends/DirectX12/CMakeLists.txt b/src/Backends/DirectX12/CMakeLists.txt index 055cda012..74d2bdc43 100644 --- a/src/Backends/DirectX12/CMakeLists.txt +++ b/src/Backends/DirectX12/CMakeLists.txt @@ -104,16 +104,6 @@ IF(LITEFX_BUILD_WITH_PIX_RUNTIME) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE Microsoft::WinPixEventRuntime) ENDIF(LITEFX_BUILD_WITH_PIX_RUNTIME) -# Add shader modules. -ADD_SHADER_LIBRARY(${PROJECT_NAME}.Shaders SOURCE_FILE "shader_resources.hpp" NAMESPACE "LiteFX::Backends::DirectX12::Shaders") -ADD_SHADER_MODULE(${PROJECT_NAME}.Blit SOURCE "shaders/blit.hlsl" LANGUAGE HLSL TYPE COMPUTE COMPILE_AS DXIL SHADER_MODEL ${LITEFX_BUILD_HLSL_SHADER_MODEL} COMPILER DXC LIBRARY ${PROJECT_NAME}.Shaders) -SET_TARGET_PROPERTIES(${PROJECT_NAME}.Shaders PROPERTIES FOLDER "Backends/Shaders") -SET_TARGET_PROPERTIES(${PROJECT_NAME}.Blit PROPERTIES FOLDER "Backends/Shaders") - -TARGET_LINK_SHADER_LIBRARIES(${PROJECT_NAME} - LIBRARIES ${PROJECT_NAME}.Shaders -) - # Copy the DXC runtime to the project build dir. IF(DXCOMPILER_DLL) ADD_CUSTOM_COMMAND(TARGET ${PROJECT_NAME} POST_BUILD diff --git a/src/Backends/DirectX12/include/litefx/backends/dx12.hpp b/src/Backends/DirectX12/include/litefx/backends/dx12.hpp index 0607a93ed..746c4f30e 100644 --- a/src/Backends/DirectX12/include/litefx/backends/dx12.hpp +++ b/src/Backends/DirectX12/include/litefx/backends/dx12.hpp @@ -1008,7 +1008,6 @@ namespace LiteFX::Rendering::Backends { using base_type::drawIndexedIndirect; using base_type::barrier; using base_type::transfer; - using base_type::generateMipMaps; using base_type::bind; using base_type::use; using base_type::pushConstants; @@ -1076,9 +1075,6 @@ namespace LiteFX::Rendering::Backends { /// UInt64 submit() const override; - /// - void generateMipMaps(IDirectX12Image& image) noexcept override; - /// [[nodiscard]] UniquePtr makeBarrier(PipelineStage syncBefore, PipelineStage syncAfter) const noexcept override; @@ -1998,16 +1994,6 @@ namespace LiteFX::Rendering::Backends { /// The command buffer to issue the bind command on. virtual void bindGlobalDescriptorHeaps(const DirectX12CommandBuffer& commandBuffer) const noexcept; - /// - /// Returns the compute pipeline that can be invoked to blit an image resource. - /// - /// - /// Blitting is used by to generate mip maps. - /// - /// The compute pipeline that can be invoked to blit an image resource. - /// - virtual DirectX12ComputePipeline& blitPipeline() const noexcept; - /// /// Returns the command signatures for indirect dispatch and draw calls. /// diff --git a/src/Backends/DirectX12/shaders/blit.hlsl b/src/Backends/DirectX12/shaders/blit.hlsl deleted file mode 100644 index afd4a3c85..000000000 --- a/src/Backends/DirectX12/shaders/blit.hlsl +++ /dev/null @@ -1,38 +0,0 @@ -struct BlitParameters -{ - float2 TexelSize; // Size of a single texel in UV coordinates ([0..1]). - float isSRGB; - float Padding; -}; - -ConstantBuffer input : register(b0, space0); -Texture2D parent : register(t1, space0); -RWTexture2D result : register(u2, space0); -SamplerState linearFilter : register(s0, space1); - -float3 applySRGB(float3 x) -{ - // See: https://github.com/Microsoft/DirectX-Graphics-Samples/blob/master/MiniEngine/Core/Shaders/GenerateMipsCS.hlsli#L55 - //return x < 0.0031308 ? 12.92 * x : 1.055 * pow(abs(x), 1.0 / 2.4) - 0.055; - return float3( - x.r < 0.0031308 ? 12.92 * x.r : 1.13005 * sqrt(abs(x.r - 0.00228)) - 0.13448 * x.r + 0.005719, - x.g < 0.0031308 ? 12.92 * x.g : 1.13005 * sqrt(abs(x.g - 0.00228)) - 0.13448 * x.g + 0.005719, - x.b < 0.0031308 ? 12.92 * x.b : 1.13005 * sqrt(abs(x.b - 0.00228)) - 0.13448 * x.b + 0.005719 - ); -} - -float4 packColor(float4 color) -{ - if (input.isSRGB > 0.0) - return float4(applySRGB(color.rgb), color.a); - else - return color; -} - -[numthreads(8, 8, 1)] -void main(uint3 threadId : SV_DispatchThreadID) -{ - float2 texcoords = input.TexelSize * (threadId.xy + 0.5); - float4 color = parent.SampleLevel(linearFilter, texcoords, 0); - result[threadId.xy] = packColor(color); -} \ No newline at end of file diff --git a/src/Backends/DirectX12/src/command_buffer.cpp b/src/Backends/DirectX12/src/command_buffer.cpp index 3a02b225d..4314ca439 100644 --- a/src/Backends/DirectX12/src/command_buffer.cpp +++ b/src/Backends/DirectX12/src/command_buffer.cpp @@ -236,89 +236,6 @@ UInt64 DirectX12CommandBuffer::submit() const return m_impl->m_queue.submit(this->shared_from_this()); } -void DirectX12CommandBuffer::generateMipMaps(IDirectX12Image& image) noexcept -{ - struct Parameters { - Float sizeX; - Float sizeY; - Float sRGB; - Float padding; - }; - - // Create the array of parameter data. - Array parametersData(image.levels()); - - std::ranges::generate(parametersData, [this, &image, i = 0]() mutable { - auto level = i++; - - return Parameters { - .sizeX = 1.f / static_cast(std::max(image.extent(level).width(), 1)), - .sizeY = 1.f / static_cast(std::max(image.extent(level).height(), 1)), - .sRGB = DX12::isSRGB(image.format()) ? 1.f : 0.f - }; - }); - - auto parametersBlock = parametersData | - std::views::transform([](const Parameters& parameters) { return reinterpret_cast(¶meters); }) | - std::ranges::to>(); - - // Set the active pipeline state. - auto& pipeline = m_impl->m_queue.device().blitPipeline(); - this->use(pipeline); - - // Create and bind the parameters. - const auto& resourceBindingsLayout = pipeline.layout()->descriptorSet(0); - auto resourceBindings = resourceBindingsLayout.allocateMultiple(image.levels() * image.layers()); - const auto& parametersLayout = resourceBindingsLayout.descriptor(0); - auto parameters = m_impl->m_queue.device().factory().createBuffer(parametersLayout.type(), ResourceHeap::Dynamic, parametersLayout.elementSize(), image.levels()); - parameters->map(parametersBlock, sizeof(Parameters)); - - // Create and bind the sampler. - const auto& samplerBindingsLayout = pipeline.layout()->descriptorSet(1); - auto samplerBindings = samplerBindingsLayout.allocate(); - auto sampler = m_impl->m_queue.device().factory().createSampler(FilterMode::Linear, FilterMode::Linear, BorderMode::ClampToEdge, BorderMode::ClampToEdge, BorderMode::ClampToEdge); - samplerBindings->update(0, *sampler); - this->bind(*samplerBindings, pipeline); - - // Transition the texture into a read/write state. - DirectX12Barrier startBarrier(PipelineStage::All, PipelineStage::Compute); - startBarrier.transition(image, ResourceAccess::None, ResourceAccess::ShaderReadWrite, ImageLayout::Undefined, ImageLayout::ReadWrite); - this->barrier(startBarrier); - auto resource = resourceBindings.begin(); - - for (int l(0); l < image.layers(); ++l, ++resource) - { - auto size = image.extent(); - - for (UInt32 i(1); i < image.levels(); ++i, size /= 2) - { - // Update the invocation parameters. - (*resource)->update(parametersLayout.binding(), *parameters, i, 1); - - // Bind the previous mip map level to the SRV at binding point 1. - (*resource)->update(1, image, 0, i - 1, 1, l, 1); - - // Bind the current level to the UAV at binding point 2. - (*resource)->update(2, image, 0, i, 1, l, 1); - - // Dispatch the pipeline. - this->bind(*(*resource), pipeline); - this->dispatch({ std::max(size.width() / 8, 1), std::max(size.height() / 8, 1), 1 }); - - // Wait for all writes. - DirectX12Barrier subBarrier(PipelineStage::Compute, PipelineStage::Compute); - subBarrier.transition(image, i, 1, l, 1, 0, ResourceAccess::ShaderReadWrite, ResourceAccess::ShaderRead, ImageLayout::ReadWrite, ImageLayout::ShaderResource); - this->barrier(subBarrier); - resource++; - } - - // Original sub-resource also needs to be transitioned. - DirectX12Barrier endBarrier(PipelineStage::Compute, PipelineStage::All); - endBarrier.transition(image, 0, 1, l, 1, 0, ResourceAccess::ShaderReadWrite, ResourceAccess::ShaderRead, ImageLayout::ReadWrite, ImageLayout::ShaderResource); - this->barrier(endBarrier); - } -} - UniquePtr DirectX12CommandBuffer::makeBarrier(PipelineStage syncBefore, PipelineStage syncAfter) const noexcept { return m_impl->m_queue.device().makeBarrier(syncBefore, syncAfter); diff --git a/src/Backends/DirectX12/src/device.cpp b/src/Backends/DirectX12/src/device.cpp index 07c026220..0dee43791 100644 --- a/src/Backends/DirectX12/src/device.cpp +++ b/src/Backends/DirectX12/src/device.cpp @@ -1,6 +1,5 @@ #include #include -#include using namespace LiteFX::Rendering::Backends; @@ -20,7 +19,6 @@ class DirectX12Device::DirectX12DeviceImpl : public Implement { DirectX12Queue* m_graphicsQueue, * m_transferQueue, * m_computeQueue; Array> m_queues; UniquePtr m_factory; - UniquePtr m_blitPipeline; ComPtr m_eventQueue; UniquePtr m_swapChain; DWORD m_debugCallbackCookie = 0; @@ -217,43 +215,6 @@ class DirectX12Device::DirectX12DeviceImpl : public Implement { return result; } - void createBlitPipeline() - { - try - { - // Allocate shader module. - Array> modules; - auto blitShader = LiteFX::Backends::DirectX12::Shaders::blit_dxi::open(); - modules.push_back(std::move(makeUnique(*m_parent, ShaderStage::Compute, blitShader, LiteFX::Backends::DirectX12::Shaders::blit_dxi::name(), "main"))); - auto shaderProgram = DirectX12ShaderProgram::create(*m_parent, std::move(modules | std::views::as_rvalue)); - - // Allocate descriptor set layouts. - UniquePtr pushConstantsLayout = nullptr; - auto bufferLayouts = Enumerable>( - makeUnique(DescriptorType::ConstantBuffer, 0, 16), - makeUnique(DescriptorType::Texture, 1, 0), - makeUnique(DescriptorType::RWTexture, 2, 0) - ); - auto samplerLayouts = Enumerable>( - makeUnique(DescriptorType::Sampler, 0, 0) - ); - auto descriptorSetLayouts = Enumerable>( - makeUnique(*m_parent, std::move(bufferLayouts), 0, ShaderStage::Compute), - makeUnique(*m_parent, std::move(samplerLayouts), 1, ShaderStage::Compute) - ); - - // Create a pipeline layout. - auto pipelineLayout = makeShared(*m_parent, std::move(descriptorSetLayouts), std::move(pushConstantsLayout)); - - // Create the pipeline. - m_blitPipeline = makeUnique(*m_parent, pipelineLayout, shaderProgram, "Blit"); - } - catch (const Exception& ex) - { - LITEFX_WARNING(DIRECTX12_LOG, "Unable to create blit pipeline. Blitting will not be available. Error: {0}", ex.what()); - } - } - public: Array getSurfaceFormats() const { @@ -297,7 +258,6 @@ DirectX12Device::DirectX12Device(const DirectX12Backend& backend, const DirectX1 m_impl->createQueues(); m_impl->createFactory(); m_impl->createSwapChain(format, renderArea, backBuffers, enableVsync); - m_impl->createBlitPipeline(); } DirectX12Device::~DirectX12Device() noexcept = default; @@ -465,11 +425,6 @@ void DirectX12Device::bindGlobalDescriptorHeaps(const DirectX12CommandBuffer& co commandBuffer.handle()->SetDescriptorHeaps(globalHeaps.size(), globalHeaps.data()); } -DirectX12ComputePipeline& DirectX12Device::blitPipeline() const noexcept -{ - return *m_impl->m_blitPipeline; -} - void DirectX12Device::indirectDrawSignatures(ComPtr& dispatchSignature, ComPtr& dispatchMeshSignature, ComPtr& drawSignature, ComPtr& drawIndexedSignature) const noexcept { dispatchSignature = m_impl->m_dispatchSignature; diff --git a/src/Backends/Vulkan/include/litefx/backends/vulkan.hpp b/src/Backends/Vulkan/include/litefx/backends/vulkan.hpp index aeb6da4bd..92454caac 100644 --- a/src/Backends/Vulkan/include/litefx/backends/vulkan.hpp +++ b/src/Backends/Vulkan/include/litefx/backends/vulkan.hpp @@ -991,7 +991,6 @@ namespace LiteFX::Rendering::Backends { using base_type::drawIndexedIndirect; using base_type::barrier; using base_type::transfer; - using base_type::generateMipMaps; using base_type::bind; using base_type::use; using base_type::pushConstants; @@ -1068,9 +1067,6 @@ namespace LiteFX::Rendering::Backends { /// UInt64 submit() const override; - /// - void generateMipMaps(IVulkanImage& image) noexcept override; - /// [[nodiscard]] UniquePtr makeBarrier(PipelineStage syncBefore, PipelineStage syncAfter) const noexcept override; diff --git a/src/Backends/Vulkan/src/command_buffer.cpp b/src/Backends/Vulkan/src/command_buffer.cpp index 715da6b13..d9f0874f2 100644 --- a/src/Backends/Vulkan/src/command_buffer.cpp +++ b/src/Backends/Vulkan/src/command_buffer.cpp @@ -347,64 +347,6 @@ UInt64 VulkanCommandBuffer::submit() const return m_impl->m_queue.submit(this->shared_from_this()); } -void VulkanCommandBuffer::generateMipMaps(IVulkanImage& image) noexcept -{ - VulkanBarrier startBarrier(PipelineStage::None, PipelineStage::Transfer); - startBarrier.transition(image, ResourceAccess::None, ResourceAccess::TransferWrite, ImageLayout::Undefined, ImageLayout::CopyDestination); - this->barrier(startBarrier); - - for (UInt32 layer(0); layer < image.layers(); ++layer) - { - Int32 mipWidth = static_cast(image.extent().width()); - Int32 mipHeight = static_cast(image.extent().height()); - Int32 mipDepth = static_cast(image.extent().depth()); - - for (UInt32 level(1); level < image.levels(); ++level) - { - VulkanBarrier subBarrier(PipelineStage::Transfer, PipelineStage::Transfer); - subBarrier.transition(image, level - 1, 1, layer, 1, 0, ResourceAccess::TransferWrite, ResourceAccess::TransferRead, ImageLayout::CopySource); - this->barrier(subBarrier); - - // Blit the image of the previous level into the current level. - VkImageBlit blit { - .srcSubresource = VkImageSubresourceLayers { - .aspectMask = image.aspectMask(), - .mipLevel = level - 1, - .baseArrayLayer = layer, - .layerCount = 1 - }, - .dstSubresource = VkImageSubresourceLayers { - .aspectMask = image.aspectMask(), - .mipLevel = level, - .baseArrayLayer = layer, - .layerCount = 1 - } - }; - - blit.srcOffsets[0] = { 0, 0, 0 }; - blit.srcOffsets[1] = { mipWidth, mipHeight, mipDepth }; - blit.dstOffsets[0] = { 0, 0, 0 }; - blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, mipDepth > 1 ? mipDepth / 2 : 1 }; - - ::vkCmdBlitImage(this->handle(), std::as_const(image).handle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, std::as_const(image).handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR); - - // Compute the new size. - mipWidth = std::max(mipWidth / 2, 1); - mipHeight = std::max(mipHeight / 2, 1); - mipDepth = std::max(mipDepth / 2, 1); - } - - VulkanBarrier subBarrier(PipelineStage::Transfer, PipelineStage::Transfer); - subBarrier.transition(image, image.levels() - 1, 1, layer, 1, 0, ResourceAccess::TransferWrite, ResourceAccess::TransferRead, ImageLayout::CopySource); - subBarrier.transition(image, 0, 1, layer, 1, 0, ResourceAccess::TransferWrite, ResourceAccess::TransferRead, ImageLayout::CopySource); - this->barrier(subBarrier); - } - - VulkanBarrier endBarrier(PipelineStage::Transfer, PipelineStage::All); - endBarrier.transition(image, ResourceAccess::TransferRead | ResourceAccess::TransferWrite, ResourceAccess::ShaderRead, ImageLayout::ShaderResource); - this->barrier(endBarrier); -} - UniquePtr VulkanCommandBuffer::makeBarrier(PipelineStage syncBefore, PipelineStage syncAfter) const noexcept { return m_impl->m_queue.device().makeBarrier(syncBefore, syncAfter); diff --git a/src/Rendering/include/litefx/rendering.hpp b/src/Rendering/include/litefx/rendering.hpp index 8c039c5ee..3e966227b 100644 --- a/src/Rendering/include/litefx/rendering.hpp +++ b/src/Rendering/include/litefx/rendering.hpp @@ -490,7 +490,6 @@ namespace LiteFX::Rendering { using ICommandBuffer::drawIndexedIndirect; using ICommandBuffer::barrier; using ICommandBuffer::transfer; - using ICommandBuffer::generateMipMaps; using ICommandBuffer::bind; using ICommandBuffer::use; using ICommandBuffer::pushConstants; @@ -523,9 +522,6 @@ namespace LiteFX::Rendering { /// virtual void barrier(const barrier_type& barrier) const noexcept = 0; - /// - virtual void generateMipMaps(image_type& image) noexcept = 0; - /// virtual void transfer(const buffer_type& source, const buffer_type& target, UInt32 sourceElement = 0, UInt32 targetElement = 0, UInt32 elements = 1) const = 0; @@ -667,10 +663,6 @@ namespace LiteFX::Rendering { this->barrier(dynamic_cast(barrier)); } - inline void cmdGenerateMipMaps(IImage& image) noexcept override { - this->generateMipMaps(dynamic_cast(image)); - } - inline void cmdTransfer(const IBuffer& source, const IBuffer& target, UInt32 sourceElement, UInt32 targetElement, UInt32 elements) const override { this->transfer(dynamic_cast(source), dynamic_cast(target), sourceElement, targetElement, elements); } diff --git a/src/Rendering/include/litefx/rendering_api.hpp b/src/Rendering/include/litefx/rendering_api.hpp index 10ff2fb70..00fae61d1 100644 --- a/src/Rendering/include/litefx/rendering_api.hpp +++ b/src/Rendering/include/litefx/rendering_api.hpp @@ -5980,23 +5980,6 @@ namespace LiteFX::Rendering { this->cmdBarrier(barrier); } - /// - /// Uses the image at level *0* to generate mip-maps for the remaining levels. - /// - /// - /// It is strongly advised, not to generate mip maps at runtime. Instead, prefer using a format that supports pre-computed mip maps. If you have to, prefer computing - /// mip maps in a pre-process. - /// - /// Note that not all texture formats and sizes are supported for mip map generation and the result might not be satisfactory. For example, it is not possible to compute - /// proper mip maps for pre-compressed formats. Textures should have power of two sizes in order to not appear under-sampled. - /// - /// Note that generating mip maps might require the texture to be writable. You can transfer the texture into a non-writable resource afterwards to improve performance. - /// - /// The command buffer used to issue the transition and transfer operations. - inline void generateMipMaps(IImage& image) noexcept { - this->cmdGenerateMipMaps(image); - } - /// /// Performs a buffer-to-buffer transfer from to . /// @@ -6869,7 +6852,6 @@ namespace LiteFX::Rendering { private: virtual UniquePtr getBarrier(PipelineStage syncBefore, PipelineStage syncAfter) const noexcept = 0; virtual void cmdBarrier(const IBarrier& barrier) const noexcept = 0; - virtual void cmdGenerateMipMaps(IImage& image) noexcept = 0; virtual void cmdTransfer(const IBuffer& source, const IBuffer& target, UInt32 sourceElement, UInt32 targetElement, UInt32 elements) const = 0; virtual void cmdTransfer(const IBuffer& source, const IImage& target, UInt32 sourceElement, UInt32 firstSubresource, UInt32 elements) const = 0; virtual void cmdTransfer(const IImage& source, const IImage& target, UInt32 sourceSubresource, UInt32 targetSubresource, UInt32 subresources) const = 0; From 42f374d6016ae2ab1f627b91afde63dd17049c42 Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Sat, 14 Dec 2024 18:21:43 +0100 Subject: [PATCH 08/11] Document mip-map generation movement. --- docs/release-logs/0.4.1.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-logs/0.4.1.md b/docs/release-logs/0.4.1.md index 50e9dc34a..8a9c4f424 100644 --- a/docs/release-logs/0.4.1.md +++ b/docs/release-logs/0.4.1.md @@ -46,6 +46,7 @@ - Simplified math type interface. ([See PR #120](https://github.com/crud89/LiteFX/pull/120)) - Add support for indirect draws/dispatches. ([See PR #118](https://github.com/crud89/LiteFX/pull/118)) - Add native visualizers for improved debugging. ([See PR #137](https://github.com/crud89/LiteFX/pull/137)) +- Mip-map generation moved into graphics utility library. ([See PR #144](https://github.com/crud89/LiteFX/pull/144)) **🌋 Vulkan:** From a8129453bb025a8e58203beb8b0c14b3236bc2a2 Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Sat, 14 Dec 2024 21:52:51 +0100 Subject: [PATCH 09/11] Store parent device as shared pointer in allocated resources to prevent release order issues. --- .../include/litefx/backends/dx12.hpp | 20 ++-- .../include/litefx/backends/dx12_builders.hpp | 2 - .../DirectX12/src/compute_pipeline.cpp | 23 ++--- .../DirectX12/src/pipeline_layout.cpp | 18 ++-- .../DirectX12/src/ray_tracing_pipeline.cpp | 24 ++--- src/Backends/DirectX12/src/render_pass.cpp | 18 ++-- .../DirectX12/src/render_pipeline.cpp | 12 +-- src/Backends/DirectX12/src/shader_module.cpp | 13 ++- src/Backends/DirectX12/src/shader_program.cpp | 42 ++------- .../Vulkan/include/litefx/backends/vulkan.hpp | 26 ++---- src/Backends/Vulkan/src/compute_pipeline.cpp | 25 ++--- src/Backends/Vulkan/src/descriptor_set.cpp | 33 +++---- .../Vulkan/src/descriptor_set_layout.cpp | 77 +++++----------- src/Backends/Vulkan/src/pipeline_layout.cpp | 28 ++---- .../Vulkan/src/ray_tracing_pipeline.cpp | 36 ++------ src/Backends/Vulkan/src/render_pass.cpp | 92 +++++-------------- src/Backends/Vulkan/src/render_pipeline.cpp | 16 ++-- src/Backends/Vulkan/src/shader_module.cpp | 22 +---- src/Backends/Vulkan/src/shader_program.cpp | 34 ++----- .../include/litefx/rendering_api.hpp | 12 +++ 20 files changed, 177 insertions(+), 396 deletions(-) diff --git a/src/Backends/DirectX12/include/litefx/backends/dx12.hpp b/src/Backends/DirectX12/include/litefx/backends/dx12.hpp index e49f314de..f0933dd9e 100644 --- a/src/Backends/DirectX12/include/litefx/backends/dx12.hpp +++ b/src/Backends/DirectX12/include/litefx/backends/dx12.hpp @@ -1225,15 +1225,11 @@ namespace LiteFX::Rendering::Backends { return SharedObject::create(device); } - public: - /// - /// Returns a pointer to the device that provides this layout or `nullptr` if the device is already released. - /// - /// A reference to the layouts parent device or `nullptr` if the device is already released. - virtual SharedPtr device() const noexcept; - // PipelineLayout interface. public: + /// + const DirectX12Device& device() const noexcept /*override*/; + /// const DirectX12DescriptorSetLayout& descriptorSet(UInt32 space) const override; @@ -2294,16 +2290,14 @@ namespace LiteFX::Rendering::Backends { return SharedObject::create(device, name); } - // DirectX 12 render pass. + // RenderPass interface. public: /// - /// Returns a reference to the device that provides this queue. + /// Returns a reference of the device that provides this queue. /// - /// A reference to the queue's parent device. - virtual SharedPtr device() const noexcept; + /// A reference of the queue's parent device. + const DirectX12Device& device() const noexcept /*override*/; - // RenderPass interface. - public: /// SharedPtr activeFrameBuffer() const noexcept override; diff --git a/src/Backends/DirectX12/include/litefx/backends/dx12_builders.hpp b/src/Backends/DirectX12/include/litefx/backends/dx12_builders.hpp index 2ce6e8630..fb6b55b5f 100644 --- a/src/Backends/DirectX12/include/litefx/backends/dx12_builders.hpp +++ b/src/Backends/DirectX12/include/litefx/backends/dx12_builders.hpp @@ -46,8 +46,6 @@ namespace LiteFX::Rendering::Backends { /// /// class LITEFX_DIRECTX12_API [[nodiscard]] DirectX12ShaderProgramBuilder final : public ShaderProgramBuilder { - LITEFX_IMPLEMENTATION(DirectX12ShaderProgramBuilderImpl); - public: /// /// Initializes a DirectX 12 shader program builder. diff --git a/src/Backends/DirectX12/src/compute_pipeline.cpp b/src/Backends/DirectX12/src/compute_pipeline.cpp index f7f3bff96..a589a61b1 100644 --- a/src/Backends/DirectX12/src/compute_pipeline.cpp +++ b/src/Backends/DirectX12/src/compute_pipeline.cpp @@ -13,18 +13,18 @@ class DirectX12ComputePipeline::DirectX12ComputePipelineImpl { friend class DirectX12ComputePipeline; private: - WeakPtr m_device; + SharedPtr m_device; SharedPtr m_layout; SharedPtr m_program; public: DirectX12ComputePipelineImpl(const DirectX12Device& device, const SharedPtr& layout, const SharedPtr& shaderProgram) : - m_device(device.weak_from_this()), m_layout(layout), m_program(shaderProgram) + m_device(device.shared_from_this()), m_layout(layout), m_program(shaderProgram) { } DirectX12ComputePipelineImpl(const DirectX12Device& device) : - m_device(device.weak_from_this()) + m_device(device.shared_from_this()) { } @@ -59,22 +59,15 @@ class DirectX12ComputePipeline::DirectX12ComputePipelineImpl { // Create a pipeline state description. pipelineStateDescription.pRootSignature = std::as_const(*m_layout).handle().Get(); - if (auto device = m_device.lock()) [[likely]] - { - // Create the pipeline state instance. - ComPtr pipelineState; - raiseIfFailed(device->handle()->CreateComputePipelineState(&pipelineStateDescription, IID_PPV_ARGS(&pipelineState)), "Unable to create compute pipeline state."); + // Create the pipeline state instance. + ComPtr pipelineState; + raiseIfFailed(m_device->handle()->CreateComputePipelineState(&pipelineStateDescription, IID_PPV_ARGS(&pipelineState)), "Unable to create compute pipeline state."); #ifndef NDEBUG - pipelineState->SetName(Widen(pipeline.name()).c_str()); + pipelineState->SetName(Widen(pipeline.name()).c_str()); #endif - return pipelineState; - } - else - { - throw RuntimeException("Cannot create compute pipeline from release device instance."); - } + return pipelineState; } }; diff --git a/src/Backends/DirectX12/src/pipeline_layout.cpp b/src/Backends/DirectX12/src/pipeline_layout.cpp index dced86f72..3273146db 100644 --- a/src/Backends/DirectX12/src/pipeline_layout.cpp +++ b/src/Backends/DirectX12/src/pipeline_layout.cpp @@ -15,17 +15,17 @@ class DirectX12PipelineLayout::DirectX12PipelineLayoutImpl { private: UniquePtr m_pushConstantsLayout{}; Array> m_descriptorSetLayouts{}; - WeakPtr m_device; + SharedPtr m_device; public: DirectX12PipelineLayoutImpl(const DirectX12Device& device, Enumerable>&& descriptorLayouts, UniquePtr&& pushConstantsLayout) : - m_pushConstantsLayout(std::move(pushConstantsLayout)), m_device(device.weak_from_this()) + m_pushConstantsLayout(std::move(pushConstantsLayout)), m_device(device.shared_from_this()) { m_descriptorSetLayouts = std::move(descriptorLayouts) | std::views::as_rvalue | std::ranges::to(); } DirectX12PipelineLayoutImpl(const DirectX12Device& device) : - m_device(device.weak_from_this()) + m_device(device.shared_from_this()) { } @@ -60,12 +60,6 @@ class DirectX12PipelineLayout::DirectX12PipelineLayoutImpl { public: ComPtr initialize([[maybe_unused]] const DirectX12PipelineLayout& pipelineLayout) { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot build pipeline layout from a released device instance."); - // Sort and check if there are duplicate space indices. std::ranges::sort(m_descriptorSetLayouts, [](const SharedPtr& a, const SharedPtr& b) { return a->space() < b->space(); }); @@ -218,7 +212,7 @@ class DirectX12PipelineLayout::DirectX12PipelineLayoutImpl { // Create the root signature. ComPtr rootSignature; - raiseIfFailed(device->handle()->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&rootSignature)), "Unable to create root signature for pipeline layout."); + raiseIfFailed(m_device->handle()->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&rootSignature)), "Unable to create root signature for pipeline layout."); return rootSignature; } @@ -241,9 +235,9 @@ DirectX12PipelineLayout::DirectX12PipelineLayout(const DirectX12Device& device) DirectX12PipelineLayout::~DirectX12PipelineLayout() noexcept = default; -SharedPtr DirectX12PipelineLayout::device() const noexcept +const DirectX12Device& DirectX12PipelineLayout::device() const noexcept { - return m_impl->m_device.lock(); + return *m_impl->m_device; } const DirectX12DescriptorSetLayout& DirectX12PipelineLayout::descriptorSet(UInt32 space) const diff --git a/src/Backends/DirectX12/src/ray_tracing_pipeline.cpp b/src/Backends/DirectX12/src/ray_tracing_pipeline.cpp index a75581b58..e02a4a449 100644 --- a/src/Backends/DirectX12/src/ray_tracing_pipeline.cpp +++ b/src/Backends/DirectX12/src/ray_tracing_pipeline.cpp @@ -39,7 +39,7 @@ class DirectX12RayTracingPipeline::DirectX12RayTracingPipelineImpl { friend class DirectX12RayTracingPipeline; private: - WeakPtr m_device; + SharedPtr m_device; SharedPtr m_layout; SharedPtr m_program; const ShaderRecordCollection m_shaderRecordCollection; @@ -48,7 +48,7 @@ class DirectX12RayTracingPipeline::DirectX12RayTracingPipelineImpl { public: DirectX12RayTracingPipelineImpl(const DirectX12Device& device, const SharedPtr& layout, const SharedPtr& shaderProgram, UInt32 maxRecursionDepth, UInt32 maxPayloadSize, UInt32 maxAttributeSize, ShaderRecordCollection&& shaderRecords) : - m_device(device.weak_from_this()), m_layout(layout), m_program(shaderProgram), m_shaderRecordCollection(std::move(shaderRecords)), m_maxRecursionDepth(maxRecursionDepth), m_maxPayloadSize(maxPayloadSize), m_maxAttributeSize(maxAttributeSize) + m_device(device.shared_from_this()), m_layout(layout), m_program(shaderProgram), m_shaderRecordCollection(std::move(shaderRecords)), m_maxRecursionDepth(maxRecursionDepth), m_maxPayloadSize(maxPayloadSize), m_maxAttributeSize(maxAttributeSize) { if (maxRecursionDepth > D3D12_RAYTRACING_MAX_DECLARABLE_TRACE_RECURSION_DEPTH) [[unlikely]] throw ArgumentOutOfRangeException("maxRecursionDepth", std::make_pair(0_ui32, static_cast(D3D12_RAYTRACING_MAX_DECLARABLE_TRACE_RECURSION_DEPTH)), maxRecursionDepth, "The specified ray tracing recursion depth too large."); @@ -58,7 +58,7 @@ class DirectX12RayTracingPipeline::DirectX12RayTracingPipelineImpl { } DirectX12RayTracingPipelineImpl(const DirectX12Device& device, ShaderRecordCollection&& shaderRecords) : - m_device(device.weak_from_this()), m_shaderRecordCollection(std::move(shaderRecords)) + m_device(device.shared_from_this()), m_shaderRecordCollection(std::move(shaderRecords)) { m_program = std::dynamic_pointer_cast(m_shaderRecordCollection.program()); } @@ -66,12 +66,6 @@ class DirectX12RayTracingPipeline::DirectX12RayTracingPipelineImpl { public: void initialize([[maybe_unused]] const DirectX12RayTracingPipeline& pipeline) { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot create ray tracing pipeline on a released device instance."); - if (m_program == nullptr) [[unlikely]] throw ArgumentNotInitializedException("shaderProgram", "The shader program must be initialized."); if (m_layout == nullptr) [[unlikely]] @@ -265,7 +259,7 @@ class DirectX12RayTracingPipeline::DirectX12RayTracingPipelineImpl { // Create the root signature. ComPtr rootSignature; - raiseIfFailed(device->handle()->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&rootSignature)), "Unable to create root signature for shader-local payload."); + raiseIfFailed(m_device->handle()->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&rootSignature)), "Unable to create root signature for shader-local payload."); // Add the root signature to the binding associations set. rootSignatures[binding].RootSignature = rootSignature; @@ -358,7 +352,7 @@ class DirectX12RayTracingPipeline::DirectX12RayTracingPipelineImpl { // Create the pipeline. ComPtr pipelineState; - raiseIfFailed(device->handle()->CreateStateObject(&pipelineDesc, IID_PPV_ARGS(&pipelineState)), "Unable to create ray tracing pipeline state."); + raiseIfFailed(m_device->handle()->CreateStateObject(&pipelineDesc, IID_PPV_ARGS(&pipelineState)), "Unable to create ray tracing pipeline state."); #ifndef NDEBUG pipelineState->SetName(Widen(pipeline.name()).c_str()); @@ -369,12 +363,6 @@ class DirectX12RayTracingPipeline::DirectX12RayTracingPipelineImpl { SharedPtr allocateShaderBindingTable(ShaderBindingTableOffsets& offsets, ShaderBindingGroup groups) { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot allocate shader binding table on a released device instance."); - // Query the interface used to obtain the shader identifiers. ComPtr stateProperties; raiseIfFailed(m_pipelineState.As(&stateProperties), "Unable to query ray tracing pipeline state properties."); @@ -417,7 +405,7 @@ class DirectX12RayTracingPipeline::DirectX12RayTracingPipelineImpl { // Allocate a buffer for the shader binding table. // NOTE: Updating the SBT to change shader-local data is currently unsupported. Instead, bind-less resources should be used. - auto result = device->factory().createBuffer(BufferType::ShaderBindingTable, ResourceHeap::Dynamic, recordSize, static_cast(totalRecordCount), ResourceUsage::TransferSource); + auto result = m_device->factory().createBuffer(BufferType::ShaderBindingTable, ResourceHeap::Dynamic, recordSize, static_cast(totalRecordCount), ResourceUsage::TransferSource); // Write each record group by group. UInt32 record{ 0 }; diff --git a/src/Backends/DirectX12/src/render_pass.cpp b/src/Backends/DirectX12/src/render_pass.cpp index 80e0f0502..99253c9a5 100644 --- a/src/Backends/DirectX12/src/render_pass.cpp +++ b/src/Backends/DirectX12/src/render_pass.cpp @@ -32,13 +32,13 @@ class DirectX12RenderPass::DirectX12RenderPassImpl { const RenderTarget* m_presentTarget = nullptr; const RenderTarget* m_depthStencilTarget = nullptr; Optional m_inputAttachmentSamplerBinding{ }; - WeakPtr m_device; + SharedPtr m_device; SharedPtr m_queue; bool m_onDefaultGraphicsQueue = false; public: DirectX12RenderPassImpl(const DirectX12Device& device, const DirectX12Queue& queue, Span renderTargets, Span inputAttachments, Optional inputAttachmentSamplerBinding, UInt32 secondaryCommandBuffers) : - m_secondaryCommandBufferCount(secondaryCommandBuffers), m_inputAttachmentSamplerBinding(inputAttachmentSamplerBinding), m_device(device.weak_from_this()), m_queue(queue.shared_from_this()), m_onDefaultGraphicsQueue(std::addressof(queue) == std::addressof(device.defaultQueue(QueueType::Graphics))) + m_secondaryCommandBufferCount(secondaryCommandBuffers), m_inputAttachmentSamplerBinding(inputAttachmentSamplerBinding), m_device(device.shared_from_this()), m_queue(queue.shared_from_this()), m_onDefaultGraphicsQueue(std::addressof(queue) == std::addressof(device.defaultQueue(QueueType::Graphics))) { this->mapRenderTargets(renderTargets); this->mapInputAttachments(inputAttachments); @@ -48,7 +48,7 @@ class DirectX12RenderPass::DirectX12RenderPassImpl { } DirectX12RenderPassImpl(const DirectX12Device& device) : - m_device(device.weak_from_this()), m_queue(device.defaultQueue(QueueType::Graphics).shared_from_this()), m_onDefaultGraphicsQueue(true) + m_device(device.shared_from_this()), m_queue(device.defaultQueue(QueueType::Graphics).shared_from_this()), m_onDefaultGraphicsQueue(true) { } @@ -252,9 +252,9 @@ DirectX12RenderPass::DirectX12RenderPass(const DirectX12Device& device, const St DirectX12RenderPass::~DirectX12RenderPass() noexcept = default; -SharedPtr DirectX12RenderPass::device() const noexcept +const DirectX12Device& DirectX12RenderPass::device() const noexcept { - return m_impl->m_device.lock(); + return *m_impl->m_device; } SharedPtr DirectX12RenderPass::activeFrameBuffer() const noexcept @@ -386,12 +386,6 @@ void DirectX12RenderPass::begin(const DirectX12FrameBuffer& frameBuffer) const UInt64 DirectX12RenderPass::end() const { - // Check if the device and the queue are still valid. - auto device = m_impl->m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot end render pass: the device instance has been released."); - // Check if we are running. if (m_impl->m_activeFrameBuffer == nullptr) throw RuntimeException("Unable to end a render pass, that has not been begun. Start the render pass first."); @@ -400,7 +394,7 @@ UInt64 DirectX12RenderPass::end() const this->ending(this, { }); auto& frameBuffer = *m_impl->m_activeFrameBuffer; - const auto& swapChain = device->swapChain(); + const auto& swapChain = m_impl->m_device->swapChain(); // Resume and end the render pass. const auto& context = m_impl->m_activeContext; diff --git a/src/Backends/DirectX12/src/render_pipeline.cpp b/src/Backends/DirectX12/src/render_pipeline.cpp index 1ebc7b31d..7618a45d1 100644 --- a/src/Backends/DirectX12/src/render_pipeline.cpp +++ b/src/Backends/DirectX12/src/render_pipeline.cpp @@ -31,19 +31,15 @@ class DirectX12RenderPipeline::DirectX12RenderPipelineImpl { DirectX12RenderPipelineImpl(const DirectX12RenderPass& renderPass, bool alphaToCoverage, const SharedPtr& layout, const SharedPtr& shaderProgram, const SharedPtr& inputAssembler, const SharedPtr& rasterizer) : m_renderPass(renderPass.shared_from_this()), m_layout(layout), m_program(shaderProgram), m_inputAssembler(inputAssembler), m_rasterizer(rasterizer), m_alphaToCoverage(alphaToCoverage) { - auto device = renderPass.device(); - if (renderPass.inputAttachmentSamplerBinding().has_value()) - m_inputAttachmentSampler = device->factory().createSampler(); + m_inputAttachmentSampler = renderPass.device().factory().createSampler(); } DirectX12RenderPipelineImpl(const DirectX12RenderPass& renderPass) : m_renderPass(renderPass.shared_from_this()) { - auto device = renderPass.device(); - if (renderPass.inputAttachmentSamplerBinding().has_value()) - m_inputAttachmentSampler = device->factory().createSampler(); + m_inputAttachmentSampler = renderPass.device().factory().createSampler(); } ~DirectX12RenderPipelineImpl() noexcept @@ -264,7 +260,7 @@ class DirectX12RenderPipeline::DirectX12RenderPipelineImpl { // Create the pipeline state instance. ComPtr pipelineState; - raiseIfFailed(m_renderPass->device()->handle()->CreatePipelineState(&pipelineDesc, IID_PPV_ARGS(&pipelineState)), "Unable to create render pipeline state."); + raiseIfFailed(m_renderPass->device().handle()->CreatePipelineState(&pipelineDesc, IID_PPV_ARGS(&pipelineState)), "Unable to create render pipeline state."); #ifndef NDEBUG pipelineState->SetName(Widen(pipeline.name()).c_str()); @@ -331,7 +327,7 @@ class DirectX12RenderPipeline::DirectX12RenderPipelineImpl { // Create the pipeline state instance. ComPtr pipelineState; - raiseIfFailed(m_renderPass->device()->handle()->CreateGraphicsPipelineState(&pipelineStateDescription, IID_PPV_ARGS(&pipelineState)), "Unable to create render pipeline state."); + raiseIfFailed(m_renderPass->device().handle()->CreateGraphicsPipelineState(&pipelineStateDescription, IID_PPV_ARGS(&pipelineState)), "Unable to create render pipeline state."); #ifndef NDEBUG pipelineState->SetName(Widen(pipeline.name()).c_str()); diff --git a/src/Backends/DirectX12/src/shader_module.cpp b/src/Backends/DirectX12/src/shader_module.cpp index 3653dfc5a..c5d52e621 100644 --- a/src/Backends/DirectX12/src/shader_module.cpp +++ b/src/Backends/DirectX12/src/shader_module.cpp @@ -11,14 +11,13 @@ class DirectX12ShaderModule::DirectX12ShaderModuleImpl { friend class DirectX12ShaderModule; private: - WeakPtr m_device; ShaderStage m_type; String m_fileName, m_entryPoint; Optional m_shaderLocalDescriptor; public: - DirectX12ShaderModuleImpl(const DirectX12Device& device, ShaderStage type, String fileName, String entryPoint, const Optional& shaderLocalDescriptor) : - m_device(device.weak_from_this()), m_type(type), m_fileName(std::move(fileName)), m_entryPoint(std::move(entryPoint)), m_shaderLocalDescriptor(shaderLocalDescriptor) + DirectX12ShaderModuleImpl(ShaderStage type, String fileName, String entryPoint, const Optional& shaderLocalDescriptor) : + m_type(type), m_fileName(std::move(fileName)), m_entryPoint(std::move(entryPoint)), m_shaderLocalDescriptor(shaderLocalDescriptor) { } @@ -55,14 +54,14 @@ class DirectX12ShaderModule::DirectX12ShaderModuleImpl { // Interface. // ------------------------------------------------------------------------------------------------ -DirectX12ShaderModule::DirectX12ShaderModule(const DirectX12Device& device, ShaderStage type, const String& fileName, const String& entryPoint, const Optional& shaderLocalDescriptor) : - ComResource(nullptr), m_impl(device, type, fileName, entryPoint, shaderLocalDescriptor) +DirectX12ShaderModule::DirectX12ShaderModule(const DirectX12Device& /*device*/, ShaderStage type, const String& fileName, const String& entryPoint, const Optional& shaderLocalDescriptor) : + ComResource(nullptr), m_impl(type, fileName, entryPoint, shaderLocalDescriptor) { this->handle() = m_impl->initialize(); } -DirectX12ShaderModule::DirectX12ShaderModule(const DirectX12Device& device, ShaderStage type, std::istream& stream, const String& name, const String& entryPoint, const Optional& shaderLocalDescriptor) : - ComResource(nullptr), m_impl(device, type, name, entryPoint, shaderLocalDescriptor) +DirectX12ShaderModule::DirectX12ShaderModule(const DirectX12Device& /*device*/, ShaderStage type, std::istream& stream, const String& name, const String& entryPoint, const Optional& shaderLocalDescriptor) : + ComResource(nullptr), m_impl(type, name, entryPoint, shaderLocalDescriptor) { this->handle() = m_impl->initialize(stream); } diff --git a/src/Backends/DirectX12/src/shader_program.cpp b/src/Backends/DirectX12/src/shader_program.cpp index dcf3e022e..0c217d04f 100644 --- a/src/Backends/DirectX12/src/shader_program.cpp +++ b/src/Backends/DirectX12/src/shader_program.cpp @@ -37,7 +37,7 @@ class DirectX12ShaderProgram::DirectX12ShaderProgramImpl { private: Array> m_modules; - WeakPtr m_device; + SharedPtr m_device; private: struct DescriptorInfo { @@ -77,13 +77,13 @@ class DirectX12ShaderProgram::DirectX12ShaderProgramImpl { public: DirectX12ShaderProgramImpl(const DirectX12Device& device, Enumerable>&& modules) : - m_device(device.weak_from_this()) + m_device(device.shared_from_this()) { m_modules = std::move(modules) | std::views::as_rvalue | std::ranges::to(); } DirectX12ShaderProgramImpl(const DirectX12Device& device) : - m_device(device.weak_from_this()) + m_device(device.shared_from_this()) { } @@ -355,12 +355,6 @@ class DirectX12ShaderProgram::DirectX12ShaderProgramImpl { SharedPtr reflectPipelineLayout() { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot create pipeline layout on a released device instance."); - // First, filter the descriptor sets and push constant ranges. Dictionary descriptorSetLayouts; Array pushConstantRanges; @@ -502,9 +496,9 @@ class DirectX12ShaderProgram::DirectX12ShaderProgramImpl { } }(std::move(it->second)); - co_yield DirectX12DescriptorSetLayout::create(*device.get(), descriptors, space, stage); + co_yield DirectX12DescriptorSetLayout::create(*device, descriptors, space, stage); } - }(device, std::move(descriptorSetLayouts)) | std::views::as_rvalue | std::ranges::to>>(); + }(m_device, std::move(descriptorSetLayouts)) | std::views::as_rvalue | std::ranges::to>>(); // Create the push constants layout. auto overallSize = std::accumulate(pushConstantRanges.begin(), pushConstantRanges.end(), 0, [](UInt32 currentSize, const auto& range) { return currentSize + range.size; }); @@ -516,7 +510,7 @@ class DirectX12ShaderProgram::DirectX12ShaderProgramImpl { auto pushConstantsLayout = makeUnique(std::move(pushConstants), overallSize); // Return the pipeline layout. - return DirectX12PipelineLayout::create(*device.get(), std::move(descriptorSets), std::move(pushConstantsLayout)); + return DirectX12PipelineLayout::create(*m_device, std::move(descriptorSets), std::move(pushConstantsLayout)); } }; @@ -553,30 +547,12 @@ SharedPtr DirectX12ShaderProgram::reflectPipelineLayout } #if defined(LITEFX_BUILD_DEFINE_BUILDERS) -// ------------------------------------------------------------------------------------------------ -// Shader program builder implementation. -// ------------------------------------------------------------------------------------------------ - -class DirectX12ShaderProgramBuilder::DirectX12ShaderProgramBuilderImpl { -public: - friend class DirectX12ShaderProgramBuilder; - -private: - SharedPtr m_device; - -public: - DirectX12ShaderProgramBuilderImpl(const DirectX12Device& device) : - m_device(device.shared_from_this()) - { - } -}; - // ------------------------------------------------------------------------------------------------ // Shader program builder shared interface. // ------------------------------------------------------------------------------------------------ DirectX12ShaderProgramBuilder::DirectX12ShaderProgramBuilder(const DirectX12Device& device) : - ShaderProgramBuilder(DirectX12ShaderProgram::create(device)), m_impl(device) + ShaderProgramBuilder(DirectX12ShaderProgram::create(device)) { } @@ -590,11 +566,11 @@ void DirectX12ShaderProgramBuilder::build() UniquePtr DirectX12ShaderProgramBuilder::makeShaderModule(ShaderStage type, const String& fileName, const String& entryPoint, const Optional& shaderLocalDescriptor) { - return makeUnique(*m_impl->m_device.get(), type, fileName, entryPoint, shaderLocalDescriptor); + return makeUnique(*this->instance()->m_impl->m_device.get(), type, fileName, entryPoint, shaderLocalDescriptor); } UniquePtr DirectX12ShaderProgramBuilder::makeShaderModule(ShaderStage type, std::istream& stream, const String& name, const String& entryPoint, const Optional& shaderLocalDescriptor) { - return makeUnique(*m_impl->m_device.get(), type, stream, name, entryPoint, shaderLocalDescriptor); + return makeUnique(*this->instance()->m_impl->m_device.get(), type, stream, name, entryPoint, shaderLocalDescriptor); } #endif // defined(LITEFX_BUILD_DEFINE_BUILDERS) \ No newline at end of file diff --git a/src/Backends/Vulkan/include/litefx/backends/vulkan.hpp b/src/Backends/Vulkan/include/litefx/backends/vulkan.hpp index 26971c6cd..d842832d9 100644 --- a/src/Backends/Vulkan/include/litefx/backends/vulkan.hpp +++ b/src/Backends/Vulkan/include/litefx/backends/vulkan.hpp @@ -931,10 +931,10 @@ namespace LiteFX::Rendering::Backends { public: /// - /// Returns a pointer to the device, the pipeline layout has been created from or `nullptr`, if the device has already been released. + /// Returns a reference of the device, the pipeline layout has been created from. /// - /// A pointer to the device, the pipeline layout has been created from. - SharedPtr device() const noexcept; + /// A reference of the device, the pipeline layout has been created from. + const VulkanDevice& device() const noexcept; public: /// @@ -1181,15 +1181,11 @@ namespace LiteFX::Rendering::Backends { return SharedObject::create(device); } - public: - /// - /// Returns a pointer to the device that provides this layout or `nullptr`, if the device has already been released. - /// - /// A pointer to the layouts parent device. - SharedPtr device() const noexcept; - // PipelineLayout interface. public: + /// + const VulkanDevice& device() const noexcept /*override*/; + /// const VulkanDescriptorSetLayout& descriptorSet(UInt32 space) const override; @@ -2289,16 +2285,14 @@ namespace LiteFX::Rendering::Backends { return SharedObject::create(device, name); } - // Vulkan render pass interface. + // RenderPass interface. public: /// - /// Returns a pointer to the device that provides this queue or `nullptr`, if the device is already released. + /// Returns a reference of the device that provides this queue. /// - /// A pointer to the queue's parent device. - SharedPtr device() const noexcept; + /// A reference of the queue's parent device. + const VulkanDevice& device() const noexcept /*override*/; - // RenderPass interface. - public: /// SharedPtr activeFrameBuffer() const noexcept override; diff --git a/src/Backends/Vulkan/src/compute_pipeline.cpp b/src/Backends/Vulkan/src/compute_pipeline.cpp index 58c4972f8..1b9a98283 100644 --- a/src/Backends/Vulkan/src/compute_pipeline.cpp +++ b/src/Backends/Vulkan/src/compute_pipeline.cpp @@ -13,30 +13,24 @@ class VulkanComputePipeline::VulkanComputePipelineImpl { friend class VulkanComputePipeline; private: - WeakPtr m_device; + SharedPtr m_device; SharedPtr m_layout; SharedPtr m_program; public: VulkanComputePipelineImpl(const VulkanDevice& device, const SharedPtr& layout, const SharedPtr& shaderProgram) : - m_device(device.weak_from_this()), m_layout(layout), m_program(shaderProgram) + m_device(device.shared_from_this()), m_layout(layout), m_program(shaderProgram) { } VulkanComputePipelineImpl(const VulkanDevice& device) : - m_device(device.weak_from_this()) + m_device(device.shared_from_this()) { } public: VkPipeline initialize([[maybe_unused]] const VulkanComputePipeline& parent) { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot allocate pipeline from a released device instance."); - LITEFX_TRACE(VULKAN_LOG, "Creating compute pipeline (\"{1}\") for layout {0}...", static_cast(m_layout.get()), parent.name()); // Setup shader stages. @@ -57,10 +51,10 @@ class VulkanComputePipeline::VulkanComputePipelineImpl { pipelineInfo.stage = shaderStages.front(); VkPipeline pipeline{}; - raiseIfFailed(::vkCreateComputePipelines(device->handle(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline), "Unable to create compute pipeline."); + raiseIfFailed(::vkCreateComputePipelines(m_device->handle(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline), "Unable to create compute pipeline."); #ifndef NDEBUG - device->setDebugName(pipeline, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, parent.name()); + m_device->setDebugName(pipeline, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, parent.name()); #endif return pipeline; @@ -87,15 +81,10 @@ VulkanComputePipeline::VulkanComputePipeline(const VulkanDevice& device) noexcep VulkanComputePipeline::VulkanComputePipeline(VulkanComputePipeline&&) noexcept = default; VulkanComputePipeline& VulkanComputePipeline::operator=(VulkanComputePipeline&&) noexcept = default; + VulkanComputePipeline::~VulkanComputePipeline() noexcept { - // Check if the device is still valid. - auto device = m_impl->m_device.lock(); - - if (device == nullptr) [[unlikely]] - LITEFX_FATAL_ERROR(VULKAN_LOG, "Invalid attempt to release compute pipeline after parent device."); - else - ::vkDestroyPipeline(device->handle(), this->handle(), nullptr); + ::vkDestroyPipeline(m_impl->m_device->handle(), this->handle(), nullptr); } SharedPtr VulkanComputePipeline::program() const noexcept diff --git a/src/Backends/Vulkan/src/descriptor_set.cpp b/src/Backends/Vulkan/src/descriptor_set.cpp index 9b6d3cc3a..a6dceb683 100644 --- a/src/Backends/Vulkan/src/descriptor_set.cpp +++ b/src/Backends/Vulkan/src/descriptor_set.cpp @@ -34,18 +34,13 @@ VulkanDescriptorSet::VulkanDescriptorSet(const VulkanDescriptorSetLayout& layout VulkanDescriptorSet::~VulkanDescriptorSet() noexcept { - auto device = m_impl->m_layout->device(); + const auto& device = m_impl->m_layout->device(); - if (device == nullptr) [[unlikely]] - LITEFX_FATAL_ERROR(VULKAN_LOG, "Invalid attempt to release descriptor set after the parent device instance has been released."); - else - { - for (auto& bufferView : m_impl->m_bufferViews) - ::vkDestroyBufferView(device->handle(), bufferView.second, nullptr); + for (auto& bufferView : m_impl->m_bufferViews) + ::vkDestroyBufferView(device.handle(), bufferView.second, nullptr); - for (auto& imageView : m_impl->m_imageViews) - ::vkDestroyImageView(device->handle(), imageView.second, nullptr); - } + for (auto& imageView : m_impl->m_imageViews) + ::vkDestroyImageView(device.handle(), imageView.second, nullptr); m_impl->m_layout->free(*this); } @@ -133,7 +128,7 @@ void VulkanDescriptorSet::update(UInt32 binding, const IVulkanBuffer& buffer, UI }; VkBufferView bufferView{}; - raiseIfFailed(::vkCreateBufferView(m_impl->m_layout->device()->handle(), &bufferViewDesc, nullptr, &bufferView), "Unable to create buffer view."); + raiseIfFailed(::vkCreateBufferView(m_impl->m_layout->device().handle(), &bufferViewDesc, nullptr, &bufferView), "Unable to create buffer view."); m_impl->m_bufferViews[binding] = bufferView; descriptorWrite.pTexelBufferView = &bufferView; @@ -154,7 +149,7 @@ void VulkanDescriptorSet::update(UInt32 binding, const IVulkanBuffer& buffer, UI }; VkBufferView bufferView{}; - raiseIfFailed(::vkCreateBufferView(m_impl->m_layout->device()->handle(), &bufferViewDesc, nullptr, &bufferView), "Unable to create buffer view."); + raiseIfFailed(::vkCreateBufferView(m_impl->m_layout->device().handle(), &bufferViewDesc, nullptr, &bufferView), "Unable to create buffer view."); m_impl->m_bufferViews[binding] = bufferView; descriptorWrite.pTexelBufferView = &bufferView; @@ -167,12 +162,12 @@ void VulkanDescriptorSet::update(UInt32 binding, const IVulkanBuffer& buffer, UI // Remove the buffer view, if there is one bound to the current descriptor. if (m_impl->m_bufferViews.contains(binding)) { - ::vkDestroyBufferView(m_impl->m_layout->device()->handle(), m_impl->m_bufferViews[binding], nullptr); + ::vkDestroyBufferView(m_impl->m_layout->device().handle(), m_impl->m_bufferViews[binding], nullptr); m_impl->m_bufferViews.erase(binding); } // Update the descriptor set. - ::vkUpdateDescriptorSets(m_impl->m_layout->device()->handle(), 1, &descriptorWrite, 0, nullptr); + ::vkUpdateDescriptorSets(m_impl->m_layout->device().handle(), 1, &descriptorWrite, 0, nullptr); } void VulkanDescriptorSet::update(UInt32 binding, const IVulkanImage& texture, UInt32 descriptor, UInt32 firstLevel, UInt32 levels, UInt32 firstLayer, UInt32 layers) const @@ -219,7 +214,7 @@ void VulkanDescriptorSet::update(UInt32 binding, const IVulkanImage& texture, UI // Remove the image view, if there is one bound to the current descriptor. if (m_impl->m_imageViews.contains(binding)) { - ::vkDestroyImageView(m_impl->m_layout->device()->handle(), m_impl->m_imageViews[binding], nullptr); + ::vkDestroyImageView(m_impl->m_layout->device().handle(), m_impl->m_imageViews[binding], nullptr); m_impl->m_imageViews.erase(binding); } @@ -260,11 +255,11 @@ void VulkanDescriptorSet::update(UInt32 binding, const IVulkanImage& texture, UI } VkImageView imageView{}; - raiseIfFailed(::vkCreateImageView(m_impl->m_layout->device()->handle(), &imageViewDesc, nullptr, &imageView), "Unable to create image view."); + raiseIfFailed(::vkCreateImageView(m_impl->m_layout->device().handle(), &imageViewDesc, nullptr, &imageView), "Unable to create image view."); m_impl->m_imageViews[binding] = imageView; imageInfo.imageView = imageView; - ::vkUpdateDescriptorSets(m_impl->m_layout->device()->handle(), 1, &descriptorWrite, 0, nullptr); + ::vkUpdateDescriptorSets(m_impl->m_layout->device().handle(), 1, &descriptorWrite, 0, nullptr); } void VulkanDescriptorSet::update(UInt32 binding, const IVulkanSampler& sampler, UInt32 descriptor) const @@ -296,7 +291,7 @@ void VulkanDescriptorSet::update(UInt32 binding, const IVulkanSampler& sampler, descriptorWrite.descriptorCount = 1; descriptorWrite.pImageInfo = &imageInfo; - ::vkUpdateDescriptorSets(m_impl->m_layout->device()->handle(), 1, &descriptorWrite, 0, nullptr); + ::vkUpdateDescriptorSets(m_impl->m_layout->device().handle(), 1, &descriptorWrite, 0, nullptr); } void VulkanDescriptorSet::update(UInt32 binding, const IVulkanAccelerationStructure& accelerationStructure, UInt32 descriptor) const @@ -335,5 +330,5 @@ void VulkanDescriptorSet::update(UInt32 binding, const IVulkanAccelerationStruct .descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR }; - ::vkUpdateDescriptorSets(m_impl->m_layout->device()->handle(), 1, &descriptorWrite, 0, nullptr); + ::vkUpdateDescriptorSets(m_impl->m_layout->device().handle(), 1, &descriptorWrite, 0, nullptr); } \ No newline at end of file diff --git a/src/Backends/Vulkan/src/descriptor_set_layout.cpp b/src/Backends/Vulkan/src/descriptor_set_layout.cpp index 8e6dacd9f..fbe1aa93c 100644 --- a/src/Backends/Vulkan/src/descriptor_set_layout.cpp +++ b/src/Backends/Vulkan/src/descriptor_set_layout.cpp @@ -42,31 +42,25 @@ class VulkanDescriptorSetLayout::VulkanDescriptorSetLayoutImpl { ShaderStage m_stages{}; UInt32 m_space{}; mutable std::mutex m_mutex; - WeakPtr m_device; + SharedPtr m_device; bool m_usesDescriptorIndexing = false; Dictionary m_descriptorSetSources; public: VulkanDescriptorSetLayoutImpl(const VulkanDevice& device, const Enumerable& descriptorLayouts, UInt32 space, ShaderStage stages) : - m_stages(stages), m_space(space), m_device(device.weak_from_this()) + m_stages(stages), m_space(space), m_device(device.shared_from_this()) { m_descriptorLayouts = descriptorLayouts | std::ranges::to>(); } VulkanDescriptorSetLayoutImpl(const VulkanDevice& device) : - m_device(device.weak_from_this()) + m_device(device.shared_from_this()) { } public: VkDescriptorSetLayout initialize() { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot create descriptor set layout on a released device instance."); - LITEFX_TRACE(VULKAN_LOG, "Defining layout for descriptor set {0} {{ Stages: {1} }}...", m_space, m_stages); // Parse the shader stage descriptor. @@ -107,12 +101,12 @@ class VulkanDescriptorSetLayout::VulkanDescriptorSetLayoutImpl { Array bindingFlagCreateInfo; // Track maximum number of descriptors in unbounded arrays. - auto maxUniformBuffers = device->adapter().limits().maxDescriptorSetUniformBuffers; - auto maxStorageBuffers = device->adapter().limits().maxDescriptorSetStorageBuffers; - auto maxStorageImages = device->adapter().limits().maxDescriptorSetStorageImages; - auto maxSampledImages = device->adapter().limits().maxDescriptorSetSampledImages; - auto maxSamplers = device->adapter().limits().maxDescriptorSetSamplers; - auto maxAttachments = device->adapter().limits().maxDescriptorSetInputAttachments; + auto maxUniformBuffers = m_device->adapter().limits().maxDescriptorSetUniformBuffers; + auto maxStorageBuffers = m_device->adapter().limits().maxDescriptorSetStorageBuffers; + auto maxStorageImages = m_device->adapter().limits().maxDescriptorSetStorageImages; + auto maxSampledImages = m_device->adapter().limits().maxDescriptorSetSampledImages; + auto maxSamplers = m_device->adapter().limits().maxDescriptorSetSamplers; + auto maxAttachments = m_device->adapter().limits().maxDescriptorSetInputAttachments; std::ranges::for_each(m_descriptorLayouts, [&, i = 0](const auto& layout) mutable { auto bindingPoint = layout.binding(); @@ -246,19 +240,13 @@ class VulkanDescriptorSetLayout::VulkanDescriptorSetLayoutImpl { descriptorSetLayoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT; VkDescriptorSetLayout layout{}; - raiseIfFailed(::vkCreateDescriptorSetLayout(device->handle(), &descriptorSetLayoutInfo, nullptr, &layout), "Unable to create descriptor set layout."); + raiseIfFailed(::vkCreateDescriptorSetLayout(m_device->handle(), &descriptorSetLayoutInfo, nullptr, &layout), "Unable to create descriptor set layout."); return layout; } void reserve(UInt32 descriptorSets) { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot allocate descriptor pool from a released device instance."); - LITEFX_TRACE(VULKAN_LOG, "Allocating descriptor pool with {5} sets {{ Uniforms: {0}, Storages: {1}, Images: {2}, Samplers: {3}, Input attachments: {4} }}...", m_poolSizes[m_poolSizeMapping[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER]].descriptorCount, m_poolSizes[m_poolSizeMapping[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER]].descriptorCount, m_poolSizes[m_poolSizeMapping[VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE]].descriptorCount, m_poolSizes[m_poolSizeMapping[VK_DESCRIPTOR_TYPE_SAMPLER]].descriptorCount, m_poolSizes[m_poolSizeMapping[VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT]].descriptorCount, descriptorSets); // Filter pool sizes, since descriptorCount must be greater than 0, according to the specs. @@ -274,7 +262,7 @@ class VulkanDescriptorSetLayout::VulkanDescriptorSetLayoutImpl { poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT; VkDescriptorPool descriptorPool{}; - raiseIfFailed(::vkCreateDescriptorPool(device->handle(), &poolInfo, nullptr, &descriptorPool), "Unable to create buffer pool."); + raiseIfFailed(::vkCreateDescriptorPool(m_device->handle(), &poolInfo, nullptr, &descriptorPool), "Unable to create buffer pool."); m_descriptorPools.push_back(descriptorPool); } @@ -285,12 +273,6 @@ class VulkanDescriptorSetLayout::VulkanDescriptorSetLayoutImpl { Array tryAllocate(const VulkanDescriptorSetLayout& layout, UInt32 descriptorSets, UInt32 descriptorsPerSet) { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot allocate descriptor set from a released device instance."); - // NOTE: We're thread safe here, as the calls from the interface use a mutex to synchronize allocation. // If the descriptor set layout is empty, no descriptor set can be allocated. @@ -327,7 +309,7 @@ class VulkanDescriptorSetLayout::VulkanDescriptorSetLayoutImpl { // Try to allocate a new descriptor sets. Array descriptorSetHandles(descriptorSets, VK_NULL_HANDLE); - raiseIfFailed(::vkAllocateDescriptorSets(device->handle(), &descriptorSetInfo, descriptorSetHandles.data()), "Unable to allocate descriptor set."); + raiseIfFailed(::vkAllocateDescriptorSets(m_device->handle(), &descriptorSetInfo, descriptorSetHandles.data()), "Unable to allocate descriptor set."); for (auto handle : descriptorSetHandles) m_descriptorSetSources.emplace(&handle, &m_descriptorPools.back()); @@ -352,7 +334,7 @@ VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(const VulkanDevice& device) } VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(const VulkanDescriptorSetLayout& other) : - DescriptorSetLayout(other), Resource(VK_NULL_HANDLE), m_impl(*other.device()) + DescriptorSetLayout(other), Resource(VK_NULL_HANDLE), m_impl(other.device()) { m_impl->m_descriptorLayouts = other.m_impl->m_descriptorLayouts; m_impl->m_space = other.space(); @@ -362,22 +344,15 @@ VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(const VulkanDescriptorSetLa VulkanDescriptorSetLayout::~VulkanDescriptorSetLayout() noexcept { - // Check if the device is still valid. - auto device = m_impl->m_device.lock(); - - if (device == nullptr) [[unlikely]] - LITEFX_FATAL_ERROR(VULKAN_LOG, "Invalid attempt to release descriptor set layout after parent device."); - else - { - // Release descriptor pools and destroy the descriptor set layouts. Releasing the pools also frees the descriptor sets allocated from it. - std::ranges::for_each(m_impl->m_descriptorPools, [device](const VkDescriptorPool& pool) { ::vkDestroyDescriptorPool(device->handle(), pool, nullptr); }); - ::vkDestroyDescriptorSetLayout(device->handle(), this->handle(), nullptr); - } + // Release descriptor pools and destroy the descriptor set layouts. Releasing the pools also frees the descriptor sets allocated from it. + auto device = m_impl->m_device; + std::ranges::for_each(m_impl->m_descriptorPools, [&device](const VkDescriptorPool& pool) { ::vkDestroyDescriptorPool(device->handle(), pool, nullptr); }); + ::vkDestroyDescriptorSetLayout(device->handle(), this->handle(), nullptr); } -SharedPtr VulkanDescriptorSetLayout::device() const noexcept +const VulkanDevice& VulkanDescriptorSetLayout::device() const noexcept { - return m_impl->m_device.lock(); + return *m_impl->m_device; } Enumerable VulkanDescriptorSetLayout::descriptors() const @@ -577,12 +552,6 @@ Enumerable> VulkanDescriptorSetLayout::allocateMu void VulkanDescriptorSetLayout::free(const VulkanDescriptorSet& descriptorSet) const { - // Check if the device is still valid. - auto device = m_impl->m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot release descriptor set from a released device instance."); - std::lock_guard lock(m_impl->m_mutex); if (!m_impl->m_usesDescriptorIndexing) @@ -598,7 +567,7 @@ void VulkanDescriptorSetLayout::free(const VulkanDescriptorSet& descriptorSet) c if (m_impl->m_descriptorSetSources.contains(handle)) { auto pool = m_impl->m_descriptorSetSources[handle]; - auto result = ::vkFreeDescriptorSets(device->handle(), *pool, 1, handle); + auto result = ::vkFreeDescriptorSets(m_impl->m_device->handle(), *pool, 1, handle); if (result != VK_SUCCESS) [[unlikely]] LITEFX_WARNING(VULKAN_LOG, "Unable to properly release descriptor set: {0}.", result); @@ -608,7 +577,7 @@ void VulkanDescriptorSetLayout::free(const VulkanDescriptorSet& descriptorSet) c // We can even release the pool, if it isn't the current active one and no descriptor sets are in use anymore. if (pool != &m_impl->m_descriptorPools.back() && std::ranges::count_if(m_impl->m_descriptorSetSources, [&](const auto& sourceMapping) { return sourceMapping.second == pool; }) == 0) { - ::vkDestroyDescriptorPool(device->handle(), *pool, nullptr); + ::vkDestroyDescriptorPool(m_impl->m_device->handle(), *pool, nullptr); if (auto match = std::ranges::find(m_impl->m_descriptorPools, *pool); match != m_impl->m_descriptorPools.end()) [[likely]] m_impl->m_descriptorPools.erase(match); @@ -628,7 +597,7 @@ size_t VulkanDescriptorSetLayout::pools() const noexcept // ------------------------------------------------------------------------------------------------ VulkanDescriptorSetLayoutBuilder::VulkanDescriptorSetLayoutBuilder(VulkanPipelineLayoutBuilder& parent, UInt32 space, ShaderStage stages) : - DescriptorSetLayoutBuilder(parent, SharedPtr(new VulkanDescriptorSetLayout(*parent.instance()->device()))) + DescriptorSetLayoutBuilder(parent, SharedPtr(new VulkanDescriptorSetLayout(parent.instance()->device()))) { this->state().space = space; this->state().stages = stages; @@ -653,7 +622,7 @@ VulkanDescriptorLayout VulkanDescriptorSetLayoutBuilder::makeDescriptor(Descript VulkanDescriptorLayout VulkanDescriptorSetLayoutBuilder::makeDescriptor(UInt32 binding, FilterMode magFilter, FilterMode minFilter, BorderMode borderU, BorderMode borderV, BorderMode borderW, MipMapMode mipMapMode, Float mipMapBias, Float minLod, Float maxLod, Float anisotropy) { // TODO: This could be made more efficient if we provide a constructor that takes an rvalue shared-pointer sampler instead. - auto sampler = VulkanSampler::allocate(*this->parent().instance()->device(), magFilter, minFilter, borderU, borderV, borderW, mipMapMode, mipMapBias, minLod, maxLod, anisotropy); + auto sampler = VulkanSampler::allocate(this->parent().instance()->device(), magFilter, minFilter, borderU, borderV, borderW, mipMapMode, mipMapBias, minLod, maxLod, anisotropy); return { *sampler, binding }; } #endif // defined(LITEFX_BUILD_DEFINE_BUILDERS) \ No newline at end of file diff --git a/src/Backends/Vulkan/src/pipeline_layout.cpp b/src/Backends/Vulkan/src/pipeline_layout.cpp index f488e8ca4..2d5a95042 100644 --- a/src/Backends/Vulkan/src/pipeline_layout.cpp +++ b/src/Backends/Vulkan/src/pipeline_layout.cpp @@ -13,31 +13,25 @@ class VulkanPipelineLayout::VulkanPipelineLayoutImpl { friend class VulkanPipelineLayout; private: - WeakPtr m_device; + SharedPtr m_device; UniquePtr m_pushConstantsLayout; Array> m_descriptorSetLayouts; public: VulkanPipelineLayoutImpl(const VulkanDevice& device, Enumerable>&& descriptorLayouts, UniquePtr&& pushConstantsLayout) : - m_device(device.weak_from_this()), m_pushConstantsLayout(std::move(pushConstantsLayout)) + m_device(device.shared_from_this()), m_pushConstantsLayout(std::move(pushConstantsLayout)) { m_descriptorSetLayouts = std::move(descriptorLayouts) | std::views::as_rvalue | std::ranges::to(); } VulkanPipelineLayoutImpl(const VulkanDevice& device) : - m_device(device.weak_from_this()) + m_device(device.shared_from_this()) { } public: VkPipelineLayout initialize([[maybe_unused]] const VulkanPipelineLayout& pipelineLayout) { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot create pipeline layout on a released device instance."); - // Since Vulkan does not know spaces, descriptor sets are mapped to their indices based on the order they are defined. Hence we need to sort the descriptor set layouts accordingly. std::ranges::sort(m_descriptorSetLayouts, [](const SharedPtr& a, const SharedPtr& b) { return a->space() < b->space(); }); @@ -62,7 +56,7 @@ class VulkanPipelineLayout::VulkanPipelineLayoutImpl { if (!emptySets.empty()) { for (auto s : emptySets) - m_descriptorSetLayouts.push_back(VulkanDescriptorSetLayout::create(*device, { }, s, ShaderStage::Any)); // No descriptor can ever be allocated from an empty descriptor set. + m_descriptorSetLayouts.push_back(VulkanDescriptorSetLayout::create(*m_device, { }, s, ShaderStage::Any)); // No descriptor can ever be allocated from an empty descriptor set. // Re-order them. std::ranges::sort(m_descriptorSetLayouts, [](const SharedPtr& a, const SharedPtr& b) { return a->space() < b->space(); }); @@ -94,7 +88,7 @@ class VulkanPipelineLayout::VulkanPipelineLayoutImpl { pipelineLayoutInfo.pPushConstantRanges = rangeHandles.data(); VkPipelineLayout layout{}; - raiseIfFailed(::vkCreatePipelineLayout(device->handle(), &pipelineLayoutInfo, nullptr, &layout), "Unable to create pipeline layout."); + raiseIfFailed(::vkCreatePipelineLayout(m_device->handle(), &pipelineLayoutInfo, nullptr, &layout), "Unable to create pipeline layout."); return layout; } }; @@ -116,18 +110,12 @@ VulkanPipelineLayout::VulkanPipelineLayout(const VulkanDevice& device) noexcept VulkanPipelineLayout::~VulkanPipelineLayout() noexcept { - // Check if the device is still valid. - auto device = m_impl->m_device.lock(); - - if (device == nullptr) [[unlikely]] - LITEFX_FATAL_ERROR(VULKAN_LOG, "Invalid attempt to release pipeline layout after parent device."); - else - ::vkDestroyPipelineLayout(device->handle(), this->handle(), nullptr); + ::vkDestroyPipelineLayout(m_impl->m_device->handle(), this->handle(), nullptr); } -SharedPtr VulkanPipelineLayout::device() const noexcept +const VulkanDevice& VulkanPipelineLayout::device() const noexcept { - return m_impl->m_device.lock(); + return *m_impl->m_device; } const VulkanDescriptorSetLayout& VulkanPipelineLayout::descriptorSet(UInt32 space) const diff --git a/src/Backends/Vulkan/src/ray_tracing_pipeline.cpp b/src/Backends/Vulkan/src/ray_tracing_pipeline.cpp index ade8acd32..400d41a9b 100644 --- a/src/Backends/Vulkan/src/ray_tracing_pipeline.cpp +++ b/src/Backends/Vulkan/src/ray_tracing_pipeline.cpp @@ -18,7 +18,7 @@ class VulkanRayTracingPipeline::VulkanRayTracingPipelineImpl { friend class VulkanRayTracingPipeline; private: - WeakPtr m_device; + SharedPtr m_device; SharedPtr m_layout; SharedPtr m_program; const ShaderRecordCollection m_shaderRecordCollection; @@ -26,12 +26,12 @@ class VulkanRayTracingPipeline::VulkanRayTracingPipelineImpl { public: VulkanRayTracingPipelineImpl(const VulkanDevice& device, const SharedPtr& layout, const SharedPtr& shaderProgram, UInt32 maxRecursionDepth, UInt32 maxPayloadSize, UInt32 maxAttributeSize, ShaderRecordCollection&& shaderRecords) : - m_device(device.weak_from_this()), m_layout(layout), m_program(shaderProgram), m_shaderRecordCollection(std::move(shaderRecords)), m_maxRecursionDepth(maxRecursionDepth), m_maxPayloadSize(maxPayloadSize), m_maxAttributeSize(maxAttributeSize) + m_device(device.shared_from_this()), m_layout(layout), m_program(shaderProgram), m_shaderRecordCollection(std::move(shaderRecords)), m_maxRecursionDepth(maxRecursionDepth), m_maxPayloadSize(maxPayloadSize), m_maxAttributeSize(maxAttributeSize) { } VulkanRayTracingPipelineImpl(const VulkanDevice& device, ShaderRecordCollection&& shaderRecords) : - m_device(device.weak_from_this()), m_shaderRecordCollection(std::move(shaderRecords)) + m_device(device.shared_from_this()), m_shaderRecordCollection(std::move(shaderRecords)) { m_program = std::dynamic_pointer_cast(m_shaderRecordCollection.program()); } @@ -39,12 +39,6 @@ class VulkanRayTracingPipeline::VulkanRayTracingPipelineImpl { public: VkPipeline initialize([[maybe_unused]] const VulkanRayTracingPipeline& parent) { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot create pipeline on a released device instance."); - if (m_program == nullptr) [[unlikely]] throw ArgumentNotInitializedException("shaderProgram", "The shader program must be initialized."); if (m_layout == nullptr) [[unlikely]] @@ -138,10 +132,10 @@ class VulkanRayTracingPipeline::VulkanRayTracingPipelineImpl { }; VkPipeline pipeline{}; - raiseIfFailed(::vkCreateRayTracingPipelines(device->handle(), VK_NULL_HANDLE, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline), "Unable to create render pipeline."); + raiseIfFailed(::vkCreateRayTracingPipelines(m_device->handle(), VK_NULL_HANDLE, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline), "Unable to create render pipeline."); #ifndef NDEBUG - device->setDebugName(pipeline, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, parent.name()); + m_device->setDebugName(pipeline, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, parent.name()); #endif return pipeline; @@ -149,19 +143,13 @@ class VulkanRayTracingPipeline::VulkanRayTracingPipelineImpl { SharedPtr allocateShaderBindingTable(const VulkanRayTracingPipeline& parent, ShaderBindingTableOffsets& offsets, ShaderBindingGroup groups) { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot allocate shader binding table from a released device instance."); - // NOTE: It is assumed that the shader record collection did not change between pipeline creation and SBT allocation (hence its const-ness)! offsets = { }; // Get the physical device properties, as they dictate alignment rules. VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingProperties { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR }; VkPhysicalDeviceProperties2 deviceProperties { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = &rayTracingProperties }; - ::vkGetPhysicalDeviceProperties2(device->adapter().handle(), &deviceProperties); + ::vkGetPhysicalDeviceProperties2(m_device->adapter().handle(), &deviceProperties); // Find the maximum payload size amongst the included shader records. auto filterByGroupType = [groups](auto& record) -> bool { @@ -203,7 +191,7 @@ class VulkanRayTracingPipeline::VulkanRayTracingPipelineImpl { // Allocate a buffer for the shader binding table. // NOTE: Updating the SBT to change shader-local data is currently unsupported. Instead, bind-less resources should be used. - auto result = device->factory().createBuffer(BufferType::ShaderBindingTable, ResourceHeap::Dynamic, recordSize, static_cast(totalRecordCount), ResourceUsage::TransferSource); + auto result = m_device->factory().createBuffer(BufferType::ShaderBindingTable, ResourceHeap::Dynamic, recordSize, static_cast(totalRecordCount), ResourceUsage::TransferSource); // Write each record group by group. UInt32 record{ 0 }; @@ -261,7 +249,7 @@ class VulkanRayTracingPipeline::VulkanRayTracingPipelineImpl { { // Get the shader group handle for the current record. auto id = static_cast(shaderRecordIds.at(currentRecord.get())); - raiseIfFailed(::vkGetRayTracingShaderGroupHandles(device->handle(), parent.handle(), id, 1, rayTracingProperties.shaderGroupHandleSize, recordData.data()), "Unable to query shader record handle."); + raiseIfFailed(::vkGetRayTracingShaderGroupHandles(m_device->handle(), parent.handle(), id, 1, rayTracingProperties.shaderGroupHandleSize, recordData.data()), "Unable to query shader record handle."); // Write the payload and map everything into the buffer. std::memcpy(recordData.data() + rayTracingProperties.shaderGroupHandleSize, currentRecord->localData(), currentRecord->localDataSize()); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) @@ -300,13 +288,7 @@ VulkanRayTracingPipeline& VulkanRayTracingPipeline::operator=(VulkanRayTracingPi VulkanRayTracingPipeline::~VulkanRayTracingPipeline() noexcept { - // Check if the device is still valid. - auto device = m_impl->m_device.lock(); - - if (device == nullptr) [[unlikely]] - LITEFX_FATAL_ERROR(VULKAN_LOG, "Invalid attempt to release ray tracing pipeline after parent device."); - else - ::vkDestroyPipeline(device->handle(), this->handle(), nullptr); + ::vkDestroyPipeline(m_impl->m_device->handle(), this->handle(), nullptr); } SharedPtr VulkanRayTracingPipeline::program() const noexcept diff --git a/src/Backends/Vulkan/src/render_pass.cpp b/src/Backends/Vulkan/src/render_pass.cpp index 7d3284330..c678564a9 100644 --- a/src/Backends/Vulkan/src/render_pass.cpp +++ b/src/Backends/Vulkan/src/render_pass.cpp @@ -25,13 +25,13 @@ class VulkanRenderPass::VulkanRenderPassImpl { const RenderTarget* m_presentTarget = nullptr; const RenderTarget* m_depthStencilTarget = nullptr; Optional m_inputAttachmentSamplerBinding{ }; - WeakPtr m_device; + SharedPtr m_device; SharedPtr m_queue; bool m_onDefaultGraphicsQueue = false; public: VulkanRenderPassImpl(const VulkanDevice& device, const VulkanQueue& queue, Span renderTargets, Span inputAttachments, Optional inputAttachmentSamplerBinding, UInt32 secondaryCommandBuffers) : - m_secondaryCommandBufferCount(secondaryCommandBuffers), m_inputAttachmentSamplerBinding(inputAttachmentSamplerBinding), m_device(device.weak_from_this()), m_queue(queue.shared_from_this()), m_onDefaultGraphicsQueue(std::addressof(queue) == std::addressof(device.defaultQueue(QueueType::Graphics))) + m_secondaryCommandBufferCount(secondaryCommandBuffers), m_inputAttachmentSamplerBinding(inputAttachmentSamplerBinding), m_device(device.shared_from_this()), m_queue(queue.shared_from_this()), m_onDefaultGraphicsQueue(std::addressof(queue) == std::addressof(device.defaultQueue(QueueType::Graphics))) { this->mapRenderTargets(renderTargets); this->mapInputAttachments(inputAttachments); @@ -41,7 +41,7 @@ class VulkanRenderPass::VulkanRenderPassImpl { } VulkanRenderPassImpl(const VulkanDevice& device) : - m_device(device.weak_from_this()), m_queue(device.defaultQueue(QueueType::Graphics).shared_from_this()), m_onDefaultGraphicsQueue(true) + m_device(device.shared_from_this()), m_queue(device.defaultQueue(QueueType::Graphics).shared_from_this()), m_onDefaultGraphicsQueue(true) { } @@ -52,36 +52,22 @@ class VulkanRenderPass::VulkanRenderPassImpl { ~VulkanRenderPassImpl() noexcept { - // Check if the device is still valid. - auto device = m_device.lock(); + // Stop listening to frame buffer events. + for (auto [frameBuffer, token] : m_frameBufferTokens) + frameBuffer->released -= token; - if (device == nullptr) [[unlikely]] - LITEFX_FATAL_ERROR(VULKAN_LOG, "Invalid attempt to release render pass after parent device."); - else - { - // Stop listening to frame buffer events. - for (auto [frameBuffer, token] : m_frameBufferTokens) - frameBuffer->released -= token; - - // Stop listening to swap chain events. - for (auto token : m_swapChainTokens) - device->swapChain().reseted -= token; + // Stop listening to swap chain events. + for (auto token : m_swapChainTokens) + m_device->swapChain().reseted -= token; - // Release swap chain image views if there are any. - for (auto view : m_swapChainViews | std::views::values) - ::vkDestroyImageView(device->handle(), view, nullptr); - } + // Release swap chain image views if there are any. + for (auto view : m_swapChainViews | std::views::values) + ::vkDestroyImageView(m_device->handle(), view, nullptr); } public: void mapRenderTargets(Span renderTargets) { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot map render targets to render pass on a released device instance."); - m_renderTargets.assign(std::begin(renderTargets), std::end(renderTargets)); std::ranges::sort(m_renderTargets, [](const auto& a, const auto& b) { return a.location() < b.location(); }); @@ -102,7 +88,7 @@ class VulkanRenderPass::VulkanRenderPassImpl { // Listen to swap chain resets in order to clear back buffer image views. if (m_presentTarget != nullptr) - m_swapChainTokens.push_back(device->swapChain().reseted.add(std::bind(&VulkanRenderPassImpl::onSwapChainReset, this, std::placeholders::_1, std::placeholders::_2))); + m_swapChainTokens.push_back(m_device->swapChain().reseted.add(std::bind(&VulkanRenderPassImpl::onSwapChainReset, this, std::placeholders::_1, std::placeholders::_2))); } void mapInputAttachments(Span inputAttachments) @@ -112,14 +98,6 @@ class VulkanRenderPass::VulkanRenderPassImpl { void registerFrameBuffer([[maybe_unused]] const VulkanRenderPass& renderPass, const VulkanQueue& queue, const VulkanFrameBuffer& frameBuffer) { -#ifndef NDEBUG - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot register frame buffer on a released device instance."); -#endif - // If the frame buffer is not yet registered, do so by listening for its release. auto interfacePointer = static_cast(&frameBuffer); @@ -131,7 +109,7 @@ class VulkanRenderPass::VulkanRenderPassImpl { { auto commandBuffer = queue.createCommandBuffer(false); #ifndef NDEBUG - device->setDebugName(std::as_const(*commandBuffer).handle(), VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, + m_device->setDebugName(std::as_const(*commandBuffer).handle(), VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, std::format("{0} Primary Commands {1}", renderPass.name(), m_primaryCommandBuffers.size()).c_str()); #endif m_primaryCommandBuffers[interfacePointer] = commandBuffer; @@ -142,7 +120,7 @@ class VulkanRenderPass::VulkanRenderPassImpl { std::views::transform([&]([[maybe_unused]] UInt32 i) { auto commandBuffer = queue.createCommandBuffer(false, true); #ifndef NDEBUG - device->setDebugName(std::as_const(*commandBuffer).handle(), VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, + m_device->setDebugName(std::as_const(*commandBuffer).handle(), VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, std::format("{0} Secondary Commands {1}", renderPass.name(), i).c_str()); #endif return commandBuffer; @@ -170,29 +148,17 @@ class VulkanRenderPass::VulkanRenderPassImpl { void onSwapChainReset([[maybe_unused]] const void* sender, const ISwapChain::ResetEventArgs& /*args*/) { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot reset render pass on a released device instance."); - // Release swap chain image views if there are any, so that they need to be re-created with the next context. for (auto view : m_swapChainViews | std::views::values) - ::vkDestroyImageView(device->handle(), view, nullptr); + ::vkDestroyImageView(m_device->handle(), view, nullptr); m_swapChainViews.clear(); } Array colorTargetContext(const VulkanFrameBuffer& frameBuffer) { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot allocate render pass targets on a released device instance."); - return m_renderTargets | std::views::filter([](const RenderTarget& renderTarget) { return renderTarget.type() != RenderTargetType::DepthStencil; }) | - std::views::transform([this, &frameBuffer, device](const RenderTarget& renderTarget) { + std::views::transform([this, &frameBuffer](const RenderTarget& renderTarget) { // Create an attachment info. VkRenderingAttachmentInfo attachmentInfo = { .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, @@ -226,12 +192,12 @@ class VulkanRenderPass::VulkanRenderPassImpl { }; VkImageView imageView{}; - raiseIfFailed(::vkCreateImageView(device->handle(), &createInfo, nullptr, &imageView), "Unable to create image view for swap chain back buffer."); + raiseIfFailed(::vkCreateImageView(m_device->handle(), &createInfo, nullptr, &imageView), "Unable to create image view for swap chain back buffer."); return imageView; }; // Create an image view if we don't already have one for the swap chain image. - auto& backBuffer = device->swapChain().image(); + auto& backBuffer = m_device->swapChain().image(); if (!m_swapChainViews.contains(&backBuffer)) m_swapChainViews[&backBuffer] = getImageView(backBuffer); @@ -321,9 +287,9 @@ VulkanRenderPass::VulkanRenderPass(const VulkanDevice& device, const String& nam VulkanRenderPass::~VulkanRenderPass() noexcept = default; -SharedPtr VulkanRenderPass::device() const noexcept +const VulkanDevice& VulkanRenderPass::device() const noexcept { - return m_impl->m_device.lock(); + return *m_impl->m_device; } SharedPtr VulkanRenderPass::activeFrameBuffer() const noexcept @@ -398,12 +364,6 @@ const Optional& VulkanRenderPass::inputAttachmentSampler void VulkanRenderPass::begin(const VulkanFrameBuffer& frameBuffer) const { - // Check if the device and the parent queue are still valid. - auto device = m_impl->m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot begin render pass on a released device instance."); - // Only begin, if we are currently not running. if (m_impl->m_activeFrameBuffer != nullptr) [[unlikely]] throw RuntimeException("Unable to begin a render pass, that is already running. End the current pass first."); @@ -447,7 +407,7 @@ void VulkanRenderPass::begin(const VulkanFrameBuffer& frameBuffer) const }); // If the present target is multi-sampled, transition the back buffer image into resolve state. - const auto& backBufferImage = device->swapChain().image(); + const auto& backBufferImage = m_impl->m_device->swapChain().image(); bool requiresResolve{ this->hasPresentTarget() && frameBuffer[*m_impl->m_presentTarget].samples() > MultiSamplingLevel::x1 }; if (requiresResolve) @@ -473,12 +433,6 @@ void VulkanRenderPass::begin(const VulkanFrameBuffer& frameBuffer) const UInt64 VulkanRenderPass::end() const { - // Check if the device is still valid. - auto device = m_impl->m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot end render pass on a released device instance."); - // Check if we are running. if (m_impl->m_activeFrameBuffer == nullptr) throw RuntimeException("Unable to end a render pass, that has not been begun. Start the render pass first."); @@ -488,7 +442,7 @@ UInt64 VulkanRenderPass::end() const // Get frame buffer and swap chain references. auto& frameBuffer = *m_impl->m_activeFrameBuffer; - const auto& swapChain = device->swapChain(); + const auto& swapChain = m_impl->m_device->swapChain(); // End secondary command buffers and end rendering. auto primaryCommandBuffer = m_impl->getPrimaryCommandBuffer(frameBuffer); diff --git a/src/Backends/Vulkan/src/render_pipeline.cpp b/src/Backends/Vulkan/src/render_pipeline.cpp index f1b8c161d..414533e3c 100644 --- a/src/Backends/Vulkan/src/render_pipeline.cpp +++ b/src/Backends/Vulkan/src/render_pipeline.cpp @@ -30,19 +30,15 @@ class VulkanRenderPipeline::VulkanRenderPipelineImpl { VulkanRenderPipelineImpl(const VulkanRenderPass& renderPass, bool alphaToCoverage, const SharedPtr& layout, const SharedPtr& shaderProgram, const SharedPtr& inputAssembler, const SharedPtr& rasterizer) : m_layout(layout), m_program(shaderProgram), m_inputAssembler(inputAssembler), m_rasterizer(rasterizer), m_alphaToCoverage(alphaToCoverage), m_renderPass(renderPass.shared_from_this()) { - auto device = renderPass.device(); - if (renderPass.inputAttachmentSamplerBinding().has_value()) - m_inputAttachmentSampler = device->factory().createSampler(); + m_inputAttachmentSampler = renderPass.device().factory().createSampler(); } VulkanRenderPipelineImpl(const VulkanRenderPass& renderPass) : m_renderPass(renderPass.shared_from_this()) { - auto device = renderPass.device(); - if (renderPass.inputAttachmentSamplerBinding().has_value()) - m_inputAttachmentSampler = device->factory().createSampler(); + m_inputAttachmentSampler = renderPass.device().factory().createSampler(); } VulkanRenderPipelineImpl(VulkanRenderPipelineImpl&&) noexcept = delete; @@ -92,7 +88,7 @@ class VulkanRenderPipeline::VulkanRenderPipelineImpl { auto pipeline = this->initializeGraphicsPipeline(parent, dynamicState, shaderStages); #ifndef NDEBUG - m_renderPass->device()->setDebugName(pipeline, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, parent.name()); + m_renderPass->device().setDebugName(pipeline, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, parent.name()); #endif // Return the pipeline instance. @@ -284,7 +280,7 @@ class VulkanRenderPipeline::VulkanRenderPipelineImpl { }; VkPipeline pipeline{}; - raiseIfFailed(::vkCreateGraphicsPipelines(m_renderPass->device()->handle(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline), "Unable to create render pipeline."); + raiseIfFailed(::vkCreateGraphicsPipelines(m_renderPass->device().handle(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline), "Unable to create render pipeline."); return pipeline; } @@ -471,7 +467,7 @@ VulkanRenderPipeline::VulkanRenderPipeline(const VulkanRenderPass& renderPass, c VulkanRenderPipeline::~VulkanRenderPipeline() noexcept { - ::vkDestroyPipeline(m_impl->m_renderPass->device()->handle(), this->handle(), nullptr); + ::vkDestroyPipeline(m_impl->m_renderPass->device().handle(), this->handle(), nullptr); } SharedPtr VulkanRenderPipeline::program() const noexcept @@ -510,7 +506,7 @@ void VulkanRenderPipeline::updateSamples(MultiSamplingLevel samples) m_impl->m_inputAttachmentBindings.clear(); // Release current pipeline state. - ::vkDestroyPipeline(m_impl->m_renderPass->device()->handle(), this->handle(), nullptr); + ::vkDestroyPipeline(m_impl->m_renderPass->device().handle(), this->handle(), nullptr); // Rebuild the pipeline. this->handle() = m_impl->initialize(*this, samples); diff --git a/src/Backends/Vulkan/src/shader_module.cpp b/src/Backends/Vulkan/src/shader_module.cpp index 3d683b567..7a20eda07 100644 --- a/src/Backends/Vulkan/src/shader_module.cpp +++ b/src/Backends/Vulkan/src/shader_module.cpp @@ -12,14 +12,14 @@ class VulkanShaderModule::VulkanShaderModuleImpl { friend class VulkanShaderModule; private: - WeakPtr m_device; + SharedPtr m_device; ShaderStage m_type; String m_fileName, m_entryPoint, m_bytecode; Optional m_shaderLocalDescriptor; public: VulkanShaderModuleImpl(const VulkanDevice& device, ShaderStage type, String fileName, String entryPoint, const Optional& shaderLocalDescriptor) : - m_device(device.weak_from_this()), m_type(type), m_fileName(std::move(fileName)), m_entryPoint(std::move(entryPoint)), m_shaderLocalDescriptor(shaderLocalDescriptor) + m_device(device.shared_from_this()), m_type(type), m_fileName(std::move(fileName)), m_entryPoint(std::move(entryPoint)), m_shaderLocalDescriptor(shaderLocalDescriptor) { } @@ -52,12 +52,6 @@ class VulkanShaderModule::VulkanShaderModuleImpl { VkShaderModule initialize(const String& fileContents) { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot create shader module on a released device instance."); - VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = fileContents.size(); @@ -65,11 +59,11 @@ class VulkanShaderModule::VulkanShaderModuleImpl { VkShaderModule module{}; - if (::vkCreateShaderModule(device->handle(), &createInfo, nullptr, &module) != VK_SUCCESS) + if (::vkCreateShaderModule(m_device->handle(), &createInfo, nullptr, &module) != VK_SUCCESS) throw std::runtime_error("Unable to compile shader file."); #ifndef NDEBUG - device->setDebugName(module, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, std::format("{0}: {1}", m_fileName, m_entryPoint)); + m_device->setDebugName(module, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, std::format("{0}: {1}", m_fileName, m_entryPoint)); #endif m_bytecode = fileContents; @@ -98,13 +92,7 @@ VulkanShaderModule& VulkanShaderModule::operator=(VulkanShaderModule&&) noexcept VulkanShaderModule::~VulkanShaderModule() noexcept { - // Check if the device is still valid. - auto device = m_impl->m_device.lock(); - - if (device == nullptr) [[unlikely]] - LITEFX_FATAL_ERROR(VULKAN_LOG, "Invalid attempt to release shader module after parent device."); - else - ::vkDestroyShaderModule(device->handle(), this->handle(), nullptr); + ::vkDestroyShaderModule(m_impl->m_device->handle(), this->handle(), nullptr); } ShaderStage VulkanShaderModule::type() const noexcept diff --git a/src/Backends/Vulkan/src/shader_program.cpp b/src/Backends/Vulkan/src/shader_program.cpp index baa0230d7..18c10fc98 100644 --- a/src/Backends/Vulkan/src/shader_program.cpp +++ b/src/Backends/Vulkan/src/shader_program.cpp @@ -56,7 +56,7 @@ class VulkanShaderProgram::VulkanShaderProgramImpl { private: Array> m_modules; - WeakPtr m_device; + SharedPtr m_device; private: struct DescriptorInfo { @@ -93,13 +93,13 @@ class VulkanShaderProgram::VulkanShaderProgramImpl { public: VulkanShaderProgramImpl(const VulkanDevice& device, Enumerable>&& modules) : - m_device(device.weak_from_this()) + m_device(device.shared_from_this()) { m_modules = std::move(modules) | std::views::as_rvalue | std::ranges::to(); } VulkanShaderProgramImpl(const VulkanDevice& device) : - m_device(device.weak_from_this()) + m_device(device.shared_from_this()) { } @@ -196,18 +196,12 @@ class VulkanShaderProgram::VulkanShaderProgramImpl { SharedPtr reflectPipelineLayout() { - // Check if the device is still valid. - auto device = m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot create pipeline layout from a released device instance."); - // First, filter the descriptor sets and push constant ranges. Dictionary descriptorSetLayouts; Array pushConstantRanges; // Extract reflection data from all shader modules. - std::ranges::for_each(m_modules, [&descriptorSetLayouts, &pushConstantRanges, device](UniquePtr& shaderModule) { + std::ranges::for_each(m_modules, [&](UniquePtr& shaderModule) { // Read the file and initialize a reflection module. auto bytecode = shaderModule->bytecode(); spv_reflect::ShaderModule reflection(bytecode.size(), bytecode.c_str()); @@ -351,7 +345,7 @@ class VulkanShaderProgram::VulkanShaderProgramImpl { co_yield VulkanDescriptorSetLayout::create(*device, descriptorLayouts, space, stage); } - }(device, std::move(descriptorSetLayouts)) | std::views::as_rvalue | std::ranges::to>>(); + }(m_device, std::move(descriptorSetLayouts)) | std::views::as_rvalue | std::ranges::to>>(); // Create the push constants layout. auto overallSize = std::accumulate(pushConstantRanges.begin(), pushConstantRanges.end(), 0, [](UInt32 currentSize, const auto& range) { return currentSize + range.size; }); @@ -363,7 +357,7 @@ class VulkanShaderProgram::VulkanShaderProgramImpl { auto pushConstantsLayout = makeUnique(std::move(pushConstants), overallSize); // Return the pipeline layout. - return VulkanPipelineLayout::create(*device, std::move(descriptorSets), std::move(pushConstantsLayout)); + return VulkanPipelineLayout::create(*m_device, std::move(descriptorSets), std::move(pushConstantsLayout)); } }; @@ -414,23 +408,11 @@ void VulkanShaderProgramBuilder::build() UniquePtr VulkanShaderProgramBuilder::makeShaderModule(ShaderStage type, const String& fileName, const String& entryPoint, const Optional& shaderLocalDescriptor) { - // Check if the device is still valid. - auto device = this->instance()->m_impl->m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot allocate shader module from a released device instance."); - - return makeUnique(*device, type, fileName, entryPoint, shaderLocalDescriptor); + return makeUnique(*this->instance()->m_impl->m_device, type, fileName, entryPoint, shaderLocalDescriptor); } UniquePtr VulkanShaderProgramBuilder::makeShaderModule(ShaderStage type, std::istream& stream, const String& name, const String& entryPoint, const Optional& shaderLocalDescriptor) { - // Check if the device is still valid. - auto device = this->instance()->m_impl->m_device.lock(); - - if (device == nullptr) [[unlikely]] - throw RuntimeException("Cannot allocate shader module from a released device instance."); - - return makeUnique(*device, type, stream, name, entryPoint, shaderLocalDescriptor); + return makeUnique(*this->instance()->m_impl->m_device, type, stream, name, entryPoint, shaderLocalDescriptor); } #endif // defined(LITEFX_BUILD_DEFINE_BUILDERS) \ No newline at end of file diff --git a/src/Rendering/include/litefx/rendering_api.hpp b/src/Rendering/include/litefx/rendering_api.hpp index 50c04feac..f460f071d 100644 --- a/src/Rendering/include/litefx/rendering_api.hpp +++ b/src/Rendering/include/litefx/rendering_api.hpp @@ -6348,6 +6348,12 @@ namespace LiteFX::Rendering { ~IPipelineLayout() noexcept override = default; public: + ///// + ///// Returns a reference to the parent device. + ///// + ///// A reference to the parent device. + //virtual const IGraphicsDevice& device() const noexcept; + /// /// Returns the descriptor set layout for the descriptor set that is bound to the space provided by . /// @@ -8028,6 +8034,12 @@ namespace LiteFX::Rendering { mutable Event ending; public: + ///// + ///// Returns a reference to the parent device. + ///// + ///// A reference to the parent device. + //virtual const IGraphicsDevice& device() const noexcept; + /// /// Returns the current frame buffer from of the render pass. /// From a4657c7b97614775d47fdc2a25e9366b5a4b9d25 Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Sun, 15 Dec 2024 09:43:35 +0100 Subject: [PATCH 10/11] Remove `PUBLIC_HEADER` from library properties. --- src/Graphics/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Graphics/CMakeLists.txt b/src/Graphics/CMakeLists.txt index 241ff604a..d1ea542ab 100644 --- a/src/Graphics/CMakeLists.txt +++ b/src/Graphics/CMakeLists.txt @@ -43,7 +43,6 @@ SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES FOLDER "SDK" VERSION ${LITEFX_VERSION} SOVERSION ${LITEFX_YEAR} - PUBLIC_HEADER "${GRAPHICS_HEADERS}" MAP_IMPORTED_CONFIG_RELWITHDEBINFO "Debug" MAP_IMPORTED_CONFIG_MINSIZEREL "Release" ) From b699b14025bf802b5fe2d12ef49823a6db7eb338 Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Sun, 15 Dec 2024 10:17:28 +0100 Subject: [PATCH 11/11] Port and optimize blitter implementation. --- src/Graphics/src/blitter_d3d12.cpp | 16 +++++++++------- src/Graphics/src/blitter_vk.cpp | 4 +++- src/Samples/Textures/src/sample.cpp | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Graphics/src/blitter_d3d12.cpp b/src/Graphics/src/blitter_d3d12.cpp index 4169f966b..2d6d0caec 100644 --- a/src/Graphics/src/blitter_d3d12.cpp +++ b/src/Graphics/src/blitter_d3d12.cpp @@ -30,10 +30,10 @@ class Blitter::BlitImpl { void initialize(const DirectX12Device& device) { // Allocate shader module. - Array> modules; auto blitShader = LiteFX::Graphics::Shaders::blit_dxi::open(); - modules.push_back(std::move(makeUnique(device, ShaderStage::Compute, blitShader, LiteFX::Graphics::Shaders::blit_dxi::name(), "main"))); - auto shaderProgram = DirectX12ShaderProgram::create(device, std::move(modules | std::views::as_rvalue)); + Array> modules; + modules.push_back(makeUnique(device, ShaderStage::Compute, blitShader, LiteFX::Graphics::Shaders::blit_dxi::name(), "main")); + auto shaderProgram = DirectX12ShaderProgram::create(device, modules | std::views::as_rvalue); // Allocate descriptor set layouts. // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers) @@ -58,12 +58,14 @@ class Blitter::BlitImpl { // Shared interface. // ------------------------------------------------------------------------------------------------ +template <> Blitter::Blitter(const DirectX12Device& device) : m_impl(device) { m_impl->initialize(device); } +template <> void Blitter::generateMipMaps(IDirectX12Image& image, DirectX12CommandBuffer& commandBuffer) { auto device = m_impl->m_device.lock(); @@ -81,7 +83,7 @@ void Blitter::generateMipMaps(IDirectX12Image& image, DirectX1 // Create the array of parameter data. Array parametersData(image.levels()); - std::ranges::generate(parametersData, [this, &image, i = 0]() mutable { + std::ranges::generate(parametersData, [&image, i = 0]() mutable { auto level = i++; return Parameters{ @@ -92,7 +94,7 @@ void Blitter::generateMipMaps(IDirectX12Image& image, DirectX1 }); auto parametersBlock = parametersData | - std::views::transform([](const Parameters& parameters) { return reinterpret_cast(¶meters); }) | + std::views::transform([](const Parameters& parameters) { return static_cast(¶meters); }) | std::ranges::to>(); // Set the active pipeline state. @@ -135,7 +137,7 @@ void Blitter::generateMipMaps(IDirectX12Image& image, DirectX1 // Dispatch the pipeline. commandBuffer.bind(*(*resource), pipeline); - commandBuffer.dispatch({ std::max(static_cast(size.width()) / 8, 1), std::max(static_cast(size.height()) / 8, 1), 1 }); + commandBuffer.dispatch({ std::max(static_cast(size.width()) / 8, 1), std::max(static_cast(size.height()) / 8, 1), 1 }); // NOLINT(cppcoreguidelines-avoid-magic-numbers) // Wait for all writes. DirectX12Barrier subBarrier(PipelineStage::Compute, PipelineStage::Compute); @@ -155,6 +157,6 @@ void Blitter::generateMipMaps(IDirectX12Image& image, DirectX1 // Export definition. // ------------------------------------------------------------------------------------------------ -template class LITEFX_GRAPHICS_API Blitter; +template class LITEFX_GRAPHICS_API LiteFX::Graphics::Blitter; #endif // LITEFX_BUILD_DIRECTX_12_BACKEND \ No newline at end of file diff --git a/src/Graphics/src/blitter_vk.cpp b/src/Graphics/src/blitter_vk.cpp index 0b23a076e..52ce80380 100644 --- a/src/Graphics/src/blitter_vk.cpp +++ b/src/Graphics/src/blitter_vk.cpp @@ -22,11 +22,13 @@ class Blitter::BlitImpl { // Shared interface. // ------------------------------------------------------------------------------------------------ +template <> Blitter::Blitter(const VulkanDevice& /*device*/) : m_impl() { } +template <> void Blitter::generateMipMaps(IVulkanImage& image, VulkanCommandBuffer& commandBuffer) { VulkanBarrier startBarrier(PipelineStage::None, PipelineStage::Transfer); @@ -89,6 +91,6 @@ void Blitter::generateMipMaps(IVulkanImage& image, VulkanCommandB // Export definition. // ------------------------------------------------------------------------------------------------ -template class LITEFX_GRAPHICS_API Blitter; +template class LITEFX_GRAPHICS_API LiteFX::Graphics::Blitter; #endif // LITEFX_BUILD_VULKAN_BACKEND \ No newline at end of file diff --git a/src/Samples/Textures/src/sample.cpp b/src/Samples/Textures/src/sample.cpp index 36d99d606..61afb191c 100644 --- a/src/Samples/Textures/src/sample.cpp +++ b/src/Samples/Textures/src/sample.cpp @@ -159,7 +159,7 @@ void loadTexture(TDevice& device, SharedPtr& textu } template -UInt64 initBuffers(SampleApp& app, TDevice& device, SharedPtr inputAssembler) +UInt64 initBuffers(SampleApp& app, TDevice& device, const SharedPtr& inputAssembler) { using image_type = TDevice::image_type; using sampler_type = TDevice::sampler_type;