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:**
diff --git a/src/AppModel/include/litefx/app.hpp b/src/AppModel/include/litefx/app.hpp
index 0574be9b5..c6b4ab540 100644
--- a/src/AppModel/include/litefx/app.hpp
+++ b/src/AppModel/include/litefx/app.hpp
@@ -73,6 +73,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/Backends/DirectX12/CMakeLists.txt b/src/Backends/DirectX12/CMakeLists.txt
index 715160770..2f86db42a 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 7e45ea899..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;
@@ -1446,7 +1442,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;
@@ -1526,9 +1521,6 @@ namespace LiteFX::Rendering::Backends {
///
UInt64 submit() const override;
- ///
- void generateMipMaps(IDirectX12Image& image) override;
-
///
[[nodiscard]] UniquePtr makeBarrier(PipelineStage syncBefore, PipelineStage syncAfter) const override;
@@ -2298,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;
@@ -2715,16 +2705,6 @@ namespace LiteFX::Rendering::Backends {
/// The command buffer to issue the bind command on.
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.
- ///
- DirectX12ComputePipeline& blitPipeline() const noexcept;
-
///
/// Returns the command signatures for indirect dispatch and draw calls.
///
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/command_buffer.cpp b/src/Backends/DirectX12/src/command_buffer.cpp
index a0d84df1f..5527905cf 100644
--- a/src/Backends/DirectX12/src/command_buffer.cpp
+++ b/src/Backends/DirectX12/src/command_buffer.cpp
@@ -264,95 +264,6 @@ UInt64 DirectX12CommandBuffer::submit() const
return queue->submit(this->shared_from_this());
}
-void DirectX12CommandBuffer::generateMipMaps(IDirectX12Image& image)
-{
- // Check if the device is still valid.
- auto device = m_impl->m_device.lock();
-
- if (device == nullptr) [[unlikely]]
- throw RuntimeException("Cannot generate mip maps on a released device instance.");
-
- struct Parameters {
- Float sizeX;
- Float sizeY;
- Float sRGB;
- Float padding;
- };
-
- // Create the array of parameter data.
- Array parametersData(image.levels());
-
- std::ranges::generate(parametersData, [&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 static_cast(¶meters); }) |
- std::ranges::to>();
-
- // Set the active pipeline state.
- auto& pipeline = 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 = 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 = 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 (UInt32 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(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);
- 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
{
// Check if the device is still valid.
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/device.cpp b/src/Backends/DirectX12/src/device.cpp
index 9950afc1a..455cca289 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;
@@ -19,7 +18,6 @@ class DirectX12Device::DirectX12DeviceImpl {
SharedPtr m_graphicsQueue{}, m_transferQueue{}, m_computeQueue{};
Array> m_queues;
SharedPtr m_factory;
- UniquePtr m_blitPipeline;
ComPtr m_eventQueue;
UniquePtr m_swapChain;
DWORD m_debugCallbackCookie = 0;
@@ -194,36 +192,6 @@ class DirectX12Device::DirectX12DeviceImpl {
return m_queues.emplace_back(DirectX12Queue::create(device, type, priority));
}
- void createBlitPipeline(const DirectX12Device& device)
- {
- try
- {
- // Allocate shader module.
- Array> modules;
- auto blitShader = LiteFX::Backends::DirectX12::Shaders::blit_dxi::open();
- modules.push_back(makeUnique(device, ShaderStage::Compute, blitShader, LiteFX::Backends::DirectX12::Shaders::blit_dxi::name(), "main"));
- auto shaderProgram = DirectX12ShaderProgram::create(device, modules | std::views::as_rvalue);
-
- // Allocate descriptor set layouts.
- // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers)
- UniquePtr pushConstantsLayout = nullptr;
- auto bufferLayouts = Enumerable(DirectX12DescriptorLayout { DescriptorType::ConstantBuffer, 0, 16 }, DirectX12DescriptorLayout { DescriptorType::Texture, 1, 0 }, DirectX12DescriptorLayout { DescriptorType::RWTexture, 2, 0 });
- auto samplerLayouts = Enumerable(DirectX12DescriptorLayout { DescriptorType::Sampler, 0, 0 } );
- auto descriptorSetLayouts = Enumerable>(DirectX12DescriptorSetLayout::create(device, bufferLayouts, 0, ShaderStage::Compute), DirectX12DescriptorSetLayout::create(device, samplerLayouts, 1, ShaderStage::Compute));
- // NOLINTEND(cppcoreguidelines-avoid-magic-numbers)
-
- // Create a pipeline layout.
- auto pipelineLayout = DirectX12PipelineLayout::create(device, std::move(descriptorSetLayouts), std::move(pushConstantsLayout));
-
- // Create the pipeline.
- m_blitPipeline = makeUnique(device, 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
{
@@ -267,7 +235,6 @@ SharedPtr DirectX12Device::initialize(const DirectX12Backend& b
m_impl->createQueues(*this);
m_impl->m_factory = DirectX12GraphicsFactory::create(*this);
m_impl->m_swapChain = UniquePtr(new DirectX12SwapChain(*this, backend, format, renderArea, backBuffers, enableVsync));
- m_impl->createBlitPipeline(*this);
return this->shared_from_this();
}
@@ -281,7 +248,6 @@ void DirectX12Device::release() noexcept
if (m_impl->m_eventQueue != nullptr && m_impl->m_debugCallbackCookie != 0)
m_impl->m_eventQueue->UnregisterMessageCallback(m_impl->m_debugCallbackCookie);
- m_impl->m_blitPipeline.reset();
m_impl->m_swapChain.reset();
m_impl->m_queues.clear();
m_impl->m_transferQueue.reset();
@@ -449,11 +415,6 @@ void DirectX12Device::bindGlobalDescriptorHeaps(const DirectX12CommandBuffer& co
commandBuffer.handle()->SetDescriptorHeaps(static_cast(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/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 470054be1..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;
@@ -1424,7 +1420,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;
@@ -1513,9 +1508,6 @@ namespace LiteFX::Rendering::Backends {
///
UInt64 submit() const override;
- ///
- void generateMipMaps(IVulkanImage& image) noexcept override;
-
///
[[nodiscard]] UniquePtr makeBarrier(PipelineStage syncBefore, PipelineStage syncAfter) const override;
@@ -2293,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/command_buffer.cpp b/src/Backends/Vulkan/src/command_buffer.cpp
index 9b9ae6c05..6b73b1b28 100644
--- a/src/Backends/Vulkan/src/command_buffer.cpp
+++ b/src/Backends/Vulkan/src/command_buffer.cpp
@@ -372,64 +372,6 @@ UInt64 VulkanCommandBuffer::submit() const
return 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
{
// Check if the device is still valid.
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/Graphics/CMakeLists.txt b/src/Graphics/CMakeLists.txt
index 09ff70394..d1ea542ab 100644
--- a/src/Graphics/CMakeLists.txt
+++ b/src/Graphics/CMakeLists.txt
@@ -10,22 +10,31 @@ 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/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.
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})
@@ -34,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"
)
@@ -50,9 +58,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=)
@@ -68,7 +95,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/blitter.hpp b/src/Graphics/include/litefx/gfx/blitter.hpp
new file mode 100644
index 000000000..dede6f91b
--- /dev/null
+++ b/src/Graphics/include/litefx/gfx/blitter.hpp
@@ -0,0 +1,85 @@
+#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 LITEFX_GRAPHICS_API Blitter : public LiteFX::SharedObject {
+ LITEFX_IMPLEMENTATION(BlitImpl);
+ friend struct SharedObject::Allocator;
+
+ 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;
+
+ public:
+ ///
+ ~Blitter() noexcept override = default;
+
+ 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);
+ }
+
+ 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
+#ifndef LiteFX_Graphics_EXPORTS
+ template class LITEFX_GRAPHICS_API Blitter;
+#endif // !LiteFX_Graphics_EXPORTS
+#endif // LITEFX_BUILD_VULKAN_BACKEND
+
+#ifdef LITEFX_BUILD_DIRECTX_12_BACKEND
+#ifndef LiteFX_Graphics_EXPORTS
+ template class LITEFX_GRAPHICS_API Blitter;
+#endif // !LiteFX_Graphics_EXPORTS
+#endif // LITEFX_BUILD_DIRECTX_12_BACKEND
+
+}
\ 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 24a8c43e9..4f3c7e743 100644
--- a/src/Graphics/include/litefx/graphics.hpp
+++ b/src/Graphics/include/litefx/graphics.hpp
@@ -1,19 +1,5 @@
#pragma once
#include
-#include
-#include
-
-namespace LiteFX::Graphics {
- using namespace LiteFX;
- using namespace LiteFX::Math;
-
- struct LITEFX_GRAPHICS_API Vertex {
- public:
- Vector3f Position;
- Vector4f Color;
- Vector3f Normal;
- 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 da4298c41..de616fe0f 100644
--- a/src/Graphics/include/litefx/graphics_api.hpp
+++ b/src/Graphics/include/litefx/graphics_api.hpp
@@ -16,17 +16,4 @@
# define LITEFX_GRAPHICS_API
#endif
-#include
-
-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
+#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 76ce86b10..000000000
--- a/src/Graphics/include/litefx/graphics_formatters.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#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
diff --git a/src/Backends/DirectX12/shaders/blit.hlsl b/src/Graphics/shaders/blit.hlsl
similarity index 100%
rename from src/Backends/DirectX12/shaders/blit.hlsl
rename to src/Graphics/shaders/blit.hlsl
diff --git a/src/Graphics/src/blitter_d3d12.cpp b/src/Graphics/src/blitter_d3d12.cpp
new file mode 100644
index 000000000..2d6d0caec
--- /dev/null
+++ b/src/Graphics/src/blitter_d3d12.cpp
@@ -0,0 +1,162 @@
+#include
+
+using namespace LiteFX::Graphics;
+using namespace LiteFX::Rendering::Backends;
+
+#ifdef LITEFX_BUILD_DIRECTX_12_BACKEND
+
+#include