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 + +// ------------------------------------------------------------------------------------------------ +// Implementation. +// ------------------------------------------------------------------------------------------------ + +template<> +class Blitter::BlitImpl { + friend class Blitter; + +private: + WeakPtr m_device; + UniquePtr m_pipeline; + SharedPtr m_sampler; + +public: + BlitImpl(const DirectX12Device& device) : + m_device(device.weak_from_this()) + { + } + +public: + void initialize(const DirectX12Device& device) + { + // Allocate shader module. + auto blitShader = LiteFX::Graphics::Shaders::blit_dxi::open(); + Array> modules; + modules.push_back(makeUnique(device, ShaderStage::Compute, blitShader, LiteFX::Graphics::Shaders::blit_dxi::name(), "main")); + auto shaderProgram = DirectX12ShaderProgram::create(device, modules | std::views::as_rvalue); + + // Allocate descriptor set layouts. + // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers) + 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_pipeline = makeUnique(device, pipelineLayout, shaderProgram, "Blit"); + + // Create the sampler state. + m_sampler = device.factory().createSampler(FilterMode::Linear, FilterMode::Linear, BorderMode::ClampToEdge, BorderMode::ClampToEdge, BorderMode::ClampToEdge); + } +}; + +// ------------------------------------------------------------------------------------------------ +// Shared interface. +// ------------------------------------------------------------------------------------------------ + +template <> +Blitter::Blitter(const DirectX12Device& device) : + m_impl(device) +{ + m_impl->initialize(device); +} + +template <> +void Blitter::generateMipMaps(IDirectX12Image& image, DirectX12CommandBuffer& commandBuffer) +{ + auto device = m_impl->m_device.lock(); + + if (device == nullptr) [[unlikely]] + throw RuntimeException("Unable to generate mip maps on a device that has been released."); + + struct Parameters { + Float sizeX; + Float sizeY; + Float sRGB; + Float padding; + }; + + // Create the array of parameter data. + Array parametersData(image.levels()); + + std::ranges::generate(parametersData, [&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 = *m_impl->m_pipeline; + commandBuffer.use(pipeline); + + // Create and bind the parameters. + const auto& resourceBindingsLayout = pipeline.layout()->descriptorSet(0); + auto resourceBindings = resourceBindingsLayout.allocateMultiple(image.levels() * image.layers()); + const auto& parametersLayout = resourceBindingsLayout.descriptor(0); + auto parameters = device->factory().createBuffer(parametersLayout.type(), ResourceHeap::Dynamic, parametersLayout.elementSize(), image.levels()); + parameters->map(parametersBlock, sizeof(Parameters)); + + // Create and bind the sampler. + const auto& samplerBindingsLayout = pipeline.layout()->descriptorSet(1); + auto samplerBindings = samplerBindingsLayout.allocate(); + samplerBindings->update(0, *m_impl->m_sampler); + commandBuffer.bind(*samplerBindings, pipeline); + + // Transition the texture into a read/write state. + DirectX12Barrier startBarrier(PipelineStage::All, PipelineStage::Compute); + startBarrier.transition(image, ResourceAccess::None, ResourceAccess::ShaderReadWrite, ImageLayout::Undefined, ImageLayout::ReadWrite); + commandBuffer.barrier(startBarrier); + auto resource = resourceBindings.begin(); + + for (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. + commandBuffer.bind(*(*resource), pipeline); + commandBuffer.dispatch({ std::max(static_cast(size.width()) / 8, 1), std::max(static_cast(size.height()) / 8, 1), 1 }); // NOLINT(cppcoreguidelines-avoid-magic-numbers) + + // Wait for all writes. + DirectX12Barrier subBarrier(PipelineStage::Compute, PipelineStage::Compute); + subBarrier.transition(image, i, 1, l, 1, 0, ResourceAccess::ShaderReadWrite, ResourceAccess::ShaderRead, ImageLayout::ReadWrite, ImageLayout::ShaderResource); + commandBuffer.barrier(subBarrier); + resource++; + } + + // Original sub-resource also needs to be transitioned. + DirectX12Barrier endBarrier(PipelineStage::Compute, PipelineStage::All); + endBarrier.transition(image, 0, 1, l, 1, 0, ResourceAccess::ShaderReadWrite, ResourceAccess::ShaderRead, ImageLayout::ReadWrite, ImageLayout::ShaderResource); + commandBuffer.barrier(endBarrier); + } +} + +// ------------------------------------------------------------------------------------------------ +// Export definition. +// ------------------------------------------------------------------------------------------------ + +template class LITEFX_GRAPHICS_API LiteFX::Graphics::Blitter; + +#endif // LITEFX_BUILD_DIRECTX_12_BACKEND \ No newline at end of file diff --git a/src/Graphics/src/blitter_vk.cpp b/src/Graphics/src/blitter_vk.cpp new file mode 100644 index 000000000..52ce80380 --- /dev/null +++ b/src/Graphics/src/blitter_vk.cpp @@ -0,0 +1,96 @@ +#include + +using namespace LiteFX::Graphics; +using namespace LiteFX::Rendering::Backends; + +#ifdef LITEFX_BUILD_VULKAN_BACKEND + +// ------------------------------------------------------------------------------------------------ +// Implementation. +// ------------------------------------------------------------------------------------------------ + +template<> +class Blitter::BlitImpl { + friend class Blitter; + +public: + +private: +}; + +// ------------------------------------------------------------------------------------------------ +// Shared interface. +// ------------------------------------------------------------------------------------------------ + +template <> +Blitter::Blitter(const VulkanDevice& /*device*/) : + m_impl() +{ +} + +template <> +void Blitter::generateMipMaps(IVulkanImage& image, VulkanCommandBuffer& commandBuffer) +{ + VulkanBarrier startBarrier(PipelineStage::None, PipelineStage::Transfer); + startBarrier.transition(image, ResourceAccess::None, ResourceAccess::TransferWrite, ImageLayout::Undefined, ImageLayout::CopyDestination); + commandBuffer.barrier(startBarrier); + + for (UInt32 layer(0); layer < image.layers(); ++layer) + { + Int32 mipWidth = static_cast(image.extent().width()); + Int32 mipHeight = static_cast(image.extent().height()); + Int32 mipDepth = static_cast(image.extent().depth()); + + for (UInt32 level(1); level < image.levels(); ++level) + { + VulkanBarrier subBarrier(PipelineStage::Transfer, PipelineStage::Transfer); + subBarrier.transition(image, level - 1, 1, layer, 1, 0, ResourceAccess::TransferWrite, ResourceAccess::TransferRead, ImageLayout::CopySource); + commandBuffer.barrier(subBarrier); + + // Blit the image of the previous level into the current level. + VkImageBlit blit{ + .srcSubresource = VkImageSubresourceLayers { + .aspectMask = image.aspectMask(), + .mipLevel = level - 1, + .baseArrayLayer = layer, + .layerCount = 1 + }, + .dstSubresource = VkImageSubresourceLayers { + .aspectMask = image.aspectMask(), + .mipLevel = level, + .baseArrayLayer = layer, + .layerCount = 1 + } + }; + + blit.srcOffsets[0] = { 0, 0, 0 }; + blit.srcOffsets[1] = { mipWidth, mipHeight, mipDepth }; + blit.dstOffsets[0] = { 0, 0, 0 }; + blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, mipDepth > 1 ? mipDepth / 2 : 1 }; + + ::vkCmdBlitImage(std::as_const(commandBuffer).handle(), std::as_const(image).handle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, std::as_const(image).handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR); + + // Compute the new size. + mipWidth = std::max(mipWidth / 2, 1); + mipHeight = std::max(mipHeight / 2, 1); + mipDepth = std::max(mipDepth / 2, 1); + } + + VulkanBarrier subBarrier(PipelineStage::Transfer, PipelineStage::Transfer); + subBarrier.transition(image, image.levels() - 1, 1, layer, 1, 0, ResourceAccess::TransferWrite, ResourceAccess::TransferRead, ImageLayout::CopySource); + subBarrier.transition(image, 0, 1, layer, 1, 0, ResourceAccess::TransferWrite, ResourceAccess::TransferRead, ImageLayout::CopySource); + commandBuffer.barrier(subBarrier); + } + + VulkanBarrier endBarrier(PipelineStage::Transfer, PipelineStage::All); + endBarrier.transition(image, ResourceAccess::TransferRead | ResourceAccess::TransferWrite, ResourceAccess::ShaderRead, ImageLayout::ShaderResource); + commandBuffer.barrier(endBarrier); +} + +// ------------------------------------------------------------------------------------------------ +// Export definition. +// ------------------------------------------------------------------------------------------------ + +template class LITEFX_GRAPHICS_API LiteFX::Graphics::Blitter; + +#endif // LITEFX_BUILD_VULKAN_BACKEND \ No newline at end of file diff --git a/src/Graphics/src/vertex.cpp b/src/Graphics/src/vertex.cpp deleted file mode 100644 index cf8a71b32..000000000 --- a/src/Graphics/src/vertex.cpp +++ /dev/null @@ -1 +0,0 @@ -#include \ No newline at end of file diff --git a/src/Rendering/CMakeLists.txt b/src/Rendering/CMakeLists.txt index ea034af32..d5bb30a2a 100644 --- a/src/Rendering/CMakeLists.txt +++ b/src/Rendering/CMakeLists.txt @@ -60,7 +60,7 @@ TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} # Link project dependencies. TARGET_LINK_LIBRARIES(${PROJECT_NAME} - PUBLIC LiteFX.Core LiteFX.Math LiteFX.Graphics LiteFX.AppModel LiteFX.Logging + PUBLIC LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Logging ) # Pre-define export specifier, to prevent dllimport/dllexport from being be emitted. diff --git a/src/Rendering/include/litefx/rendering.hpp b/src/Rendering/include/litefx/rendering.hpp index 5c505814d..8ccdb3c2b 100644 --- a/src/Rendering/include/litefx/rendering.hpp +++ b/src/Rendering/include/litefx/rendering.hpp @@ -559,7 +559,6 @@ namespace LiteFX::Rendering { using ICommandBuffer::drawIndexedIndirect; using ICommandBuffer::barrier; using ICommandBuffer::transfer; - using ICommandBuffer::generateMipMaps; using ICommandBuffer::bind; using ICommandBuffer::use; using ICommandBuffer::pushConstants; @@ -601,9 +600,6 @@ namespace LiteFX::Rendering { /// virtual void barrier(const barrier_type& barrier) const noexcept = 0; - /// - virtual void generateMipMaps(image_type& image) = 0; - /// virtual void transfer(const buffer_type& source, const buffer_type& target, UInt32 sourceElement = 0, UInt32 targetElement = 0, UInt32 elements = 1) const = 0; @@ -745,10 +741,6 @@ namespace LiteFX::Rendering { this->barrier(dynamic_cast(barrier)); } - inline void cmdGenerateMipMaps(IImage& image) override { - this->generateMipMaps(dynamic_cast(image)); - } - inline void cmdTransfer(const IBuffer& source, const IBuffer& target, UInt32 sourceElement, UInt32 targetElement, UInt32 elements) const override { this->transfer(dynamic_cast(source), dynamic_cast(target), sourceElement, targetElement, elements); } diff --git a/src/Rendering/include/litefx/rendering_api.hpp b/src/Rendering/include/litefx/rendering_api.hpp index 9eaeac2f9..f460f071d 100644 --- a/src/Rendering/include/litefx/rendering_api.hpp +++ b/src/Rendering/include/litefx/rendering_api.hpp @@ -18,7 +18,6 @@ #include #include -#include namespace LiteFX::Rendering { using namespace LiteFX; @@ -760,6 +759,40 @@ namespace LiteFX::Rendering { UInt32 = 0x00000020 }; + /// + /// Describes the topology of a mesh primitive. + /// + enum class PrimitiveTopology { + /// + /// A list of points where each vertex refers to an individual point. + /// + PointList = 0x00010001, + + /// + /// A list of lines where each vertex pair refers to the start and end points of a line. + /// + /// + LineList = 0x00020001, + + /// + /// A list of triangles, where each triplet of vertices refers to a whole triangle. + /// + /// + TriangleList = 0x00040001, + + /// + /// A strip of lines where each vertex (except the first one) refers to the end point for the next line segment. + /// + /// + LineStrip = 0x00020002, + + /// + /// A strip of triangles, where each vertex (except the first two) refers to the third vertex of the next triangle segment. + /// + /// + TriangleStrip = 0x00040002 + }; + /// /// Describes the valid shader stages of a graphics pipeline. /// @@ -6315,6 +6348,12 @@ namespace LiteFX::Rendering { ~IPipelineLayout() noexcept override = default; public: + ///// + ///// Returns a reference to the parent device. + ///// + ///// A reference to the parent device. + //virtual const IGraphicsDevice& device() const noexcept; + /// /// Returns the descriptor set layout for the descriptor set that is bound to the space provided by . /// @@ -6501,23 +6540,6 @@ namespace LiteFX::Rendering { this->cmdBarrier(barrier); } - /// - /// Uses the image at level *0* to generate mip-maps for the remaining levels. - /// - /// - /// It is strongly advised, not to generate mip maps at runtime. Instead, prefer using a format that supports pre-computed mip maps. If you have to, prefer computing - /// mip maps in a pre-process. - /// - /// Note that not all texture formats and sizes are supported for mip map generation and the result might not be satisfactory. For example, it is not possible to compute - /// proper mip maps for pre-compressed formats. Textures should have power of two sizes in order to not appear under-sampled. - /// - /// Note that generating mip maps might require the texture to be writable. You can transfer the texture into a non-writable resource afterwards to improve performance. - /// - /// The command buffer used to issue the transition and transfer operations. - inline void generateMipMaps(IImage& image) { - this->cmdGenerateMipMaps(image); - } - /// /// Performs a buffer-to-buffer transfer from to . /// @@ -7391,7 +7413,6 @@ namespace LiteFX::Rendering { virtual SharedPtr getQueue() const noexcept = 0; virtual UniquePtr getBarrier(PipelineStage syncBefore, PipelineStage syncAfter) const = 0; virtual void cmdBarrier(const IBarrier& barrier) const noexcept = 0; - virtual void cmdGenerateMipMaps(IImage& image) = 0; virtual void cmdTransfer(const IBuffer& source, const IBuffer& target, UInt32 sourceElement, UInt32 targetElement, UInt32 elements) const = 0; virtual void cmdTransfer(const IBuffer& source, const IImage& target, UInt32 sourceElement, UInt32 firstSubresource, UInt32 elements) const = 0; virtual void cmdTransfer(const IImage& source, const IImage& target, UInt32 sourceSubresource, UInt32 targetSubresource, UInt32 subresources) const = 0; @@ -8013,6 +8034,12 @@ namespace LiteFX::Rendering { mutable Event ending; public: + ///// + ///// Returns a reference to the parent device. + ///// + ///// A reference to the parent device. + //virtual const IGraphicsDevice& device() const noexcept; + /// /// Returns the current frame buffer from of the render pass. /// @@ -9337,4 +9364,10 @@ namespace LiteFX::Rendering { virtual Enumerable getAdapters() const = 0; }; + /// + /// Concept that can be used to refer to render backend implementations. + /// + template + concept render_backend = meta::implements; + } \ No newline at end of file diff --git a/src/Rendering/include/litefx/rendering_formatters.hpp b/src/Rendering/include/litefx/rendering_formatters.hpp index 226d35470..a52e58deb 100644 --- a/src/Rendering/include/litefx/rendering_formatters.hpp +++ b/src/Rendering/include/litefx/rendering_formatters.hpp @@ -316,6 +316,22 @@ struct LITEFX_RENDERING_API std::formatter : std::formatter +struct LITEFX_RENDERING_API std::formatter : std::formatter { + auto format(LiteFX::Rendering::PrimitiveTopology t, std::format_context& ctx) const { + string_view name = "Invalid"; + switch (t) { + using enum PrimitiveTopology; + case PointList: name = "PointList"; break; + case LineList: name = "LineList"; break; + case TriangleList: name = "TriangleList"; break; + case LineStrip: name = "LineStrip"; break; + case TriangleStrip: name = "TriangleStrip"; break; + } + return formatter::format(name, ctx); + } +}; + template <> struct LITEFX_RENDERING_API std::formatter : std::formatter { auto format(ShaderStage t, std::format_context& ctx) const { diff --git a/src/Samples/BasicRendering/CMakeLists.txt b/src/Samples/BasicRendering/CMakeLists.txt index 09f8ac25a..0fb1ee8e0 100644 --- a/src/Samples/BasicRendering/CMakeLists.txt +++ b/src/Samples/BasicRendering/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/BasicRendering/src/sample.h b/src/Samples/BasicRendering/src/sample.h index 272f90d30..361eda357 100644 --- a/src/Samples/BasicRendering/src/sample.h +++ b/src/Samples/BasicRendering/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/Bindless/CMakeLists.txt b/src/Samples/Bindless/CMakeLists.txt index c31843a96..9474ee794 100644 --- a/src/Samples/Bindless/CMakeLists.txt +++ b/src/Samples/Bindless/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/Bindless/src/sample.h b/src/Samples/Bindless/src/sample.h index a0076e518..7a02eeaac 100644 --- a/src/Samples/Bindless/src/sample.h +++ b/src/Samples/Bindless/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/Compute/CMakeLists.txt b/src/Samples/Compute/CMakeLists.txt index e5c66c5f1..4bebfc14b 100644 --- a/src/Samples/Compute/CMakeLists.txt +++ b/src/Samples/Compute/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/Compute/src/sample.h b/src/Samples/Compute/src/sample.h index 62ad390df..5b202db0d 100644 --- a/src/Samples/Compute/src/sample.h +++ b/src/Samples/Compute/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/Indirect/CMakeLists.txt b/src/Samples/Indirect/CMakeLists.txt index c99695a8e..be3b2ac24 100644 --- a/src/Samples/Indirect/CMakeLists.txt +++ b/src/Samples/Indirect/CMakeLists.txt @@ -69,7 +69,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/Indirect/src/sample.h b/src/Samples/Indirect/src/sample.h index 6d8cb17a4..eebc84ccc 100644 --- a/src/Samples/Indirect/src/sample.h +++ b/src/Samples/Indirect/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/MeshShader/CMakeLists.txt b/src/Samples/MeshShader/CMakeLists.txt index d17eb4411..0c6739d8b 100644 --- a/src/Samples/MeshShader/CMakeLists.txt +++ b/src/Samples/MeshShader/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/MeshShader/src/sample.h b/src/Samples/MeshShader/src/sample.h index 5f877f88b..f3532073e 100644 --- a/src/Samples/MeshShader/src/sample.h +++ b/src/Samples/MeshShader/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/Multisampling/CMakeLists.txt b/src/Samples/Multisampling/CMakeLists.txt index 91323b6b6..dffee14e0 100644 --- a/src/Samples/Multisampling/CMakeLists.txt +++ b/src/Samples/Multisampling/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/Multisampling/src/sample.h b/src/Samples/Multisampling/src/sample.h index a9cbcf0c8..1fbcc560d 100644 --- a/src/Samples/Multisampling/src/sample.h +++ b/src/Samples/Multisampling/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/Multithreading/CMakeLists.txt b/src/Samples/Multithreading/CMakeLists.txt index 666c80bea..0f4542b06 100644 --- a/src/Samples/Multithreading/CMakeLists.txt +++ b/src/Samples/Multithreading/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/Multithreading/src/sample.h b/src/Samples/Multithreading/src/sample.h index 9db90e3ec..f2b52c50b 100644 --- a/src/Samples/Multithreading/src/sample.h +++ b/src/Samples/Multithreading/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -25,6 +26,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/PushConstants/CMakeLists.txt b/src/Samples/PushConstants/CMakeLists.txt index 40bc4cdf5..501446e5d 100644 --- a/src/Samples/PushConstants/CMakeLists.txt +++ b/src/Samples/PushConstants/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/PushConstants/src/sample.h b/src/Samples/PushConstants/src/sample.h index 9bc33a001..5c1cd1558 100644 --- a/src/Samples/PushConstants/src/sample.h +++ b/src/Samples/PushConstants/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/RayQueries/CMakeLists.txt b/src/Samples/RayQueries/CMakeLists.txt index 1cc9c090f..4e394055b 100644 --- a/src/Samples/RayQueries/CMakeLists.txt +++ b/src/Samples/RayQueries/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/RayQueries/src/sample.h b/src/Samples/RayQueries/src/sample.h index 045cd7bb3..59c2451a8 100644 --- a/src/Samples/RayQueries/src/sample.h +++ b/src/Samples/RayQueries/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/RayTracing/CMakeLists.txt b/src/Samples/RayTracing/CMakeLists.txt index 8cdeebe99..33b08348e 100644 --- a/src/Samples/RayTracing/CMakeLists.txt +++ b/src/Samples/RayTracing/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/RayTracing/src/sample.h b/src/Samples/RayTracing/src/sample.h index 9bd74e4a1..beb6d21de 100644 --- a/src/Samples/RayTracing/src/sample.h +++ b/src/Samples/RayTracing/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/RenderPasses/CMakeLists.txt b/src/Samples/RenderPasses/CMakeLists.txt index ee6b4da28..d5ec83e59 100644 --- a/src/Samples/RenderPasses/CMakeLists.txt +++ b/src/Samples/RenderPasses/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/RenderPasses/src/sample.h b/src/Samples/RenderPasses/src/sample.h index ecc8c705a..084562cfa 100644 --- a/src/Samples/RenderPasses/src/sample.h +++ b/src/Samples/RenderPasses/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/Textures/CMakeLists.txt b/src/Samples/Textures/CMakeLists.txt index 676a85a00..7615444cb 100644 --- a/src/Samples/Textures/CMakeLists.txt +++ b/src/Samples/Textures/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/Textures/src/sample.cpp b/src/Samples/Textures/src/sample.cpp index 4ba8d184c..61afb191c 100644 --- a/src/Samples/Textures/src/sample.cpp +++ b/src/Samples/Textures/src/sample.cpp @@ -108,9 +108,8 @@ void initRenderGraph(TRenderBackend* backend, SharedPtr& inputA std::ranges::for_each(frameBuffers, [device](auto& frameBuffer) { device->state().add(std::move(frameBuffer)); }); } -template requires - meta::implements -void loadTexture(TDevice& device, SharedPtr& texture, SharedPtr& sampler) +template +void loadTexture(TDevice& device, SharedPtr& texture, SharedPtr& sampler) { using TBarrier = typename TDevice::barrier_type; @@ -138,7 +137,8 @@ void loadTexture(TDevice& device, SharedPtr& texture, SharedPtrtransfer(imageData.get(), texture->size(0), *texture); // Generate the rest of the mip maps. - commandBuffer->generateMipMaps(*texture); + auto blitter = Blitter::create(device); + blitter->generateMipMaps(*texture, *commandBuffer); // Create a barrier to ensure the texture is readable. barrier = device.buildBarrier() @@ -158,10 +158,12 @@ void loadTexture(TDevice& device, SharedPtr& texture, SharedPtr requires - meta::implements +template UInt64 initBuffers(SampleApp& app, TDevice& device, const SharedPtr& inputAssembler) { + using image_type = TDevice::image_type; + using sampler_type = TDevice::sampler_type; + // Get a command buffer auto commandBuffer = device.defaultQueue(QueueType::Transfer).createCommandBuffer(true); @@ -183,9 +185,9 @@ UInt64 initBuffers(SampleApp& app, TDevice& device, const SharedPtr texture; - SharedPtr sampler; - ::loadTexture(device, texture, sampler); + SharedPtr texture; + SharedPtr sampler; + ::loadTexture(device, texture, sampler); // Allocate the descriptor sets. auto staticBindings = staticBindingLayout.allocate({ { 0, *cameraBuffer }, { 1, *texture } }); @@ -289,7 +291,7 @@ void SampleApp::onInit() // Initialize resources. ::initRenderGraph(backend, m_inputAssembler); - m_transferFence = ::initBuffers(*this, device, m_inputAssembler); + m_transferFence = ::initBuffers(*this, device, m_inputAssembler); return true; }; diff --git a/src/Samples/Textures/src/sample.h b/src/Samples/Textures/src/sample.h index 2edb577ea..4949bb254 100644 --- a/src/Samples/Textures/src/sample.h +++ b/src/Samples/Textures/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept { diff --git a/src/Samples/UniformArrays/CMakeLists.txt b/src/Samples/UniformArrays/CMakeLists.txt index c4f2b12f5..89bd39e89 100644 --- a/src/Samples/UniformArrays/CMakeLists.txt +++ b/src/Samples/UniformArrays/CMakeLists.txt @@ -58,7 +58,7 @@ IF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) ENDIF(LITEFX_BUILD_EXAMPLES_RENDERDOC_LOADER) # Link project dependencies. -TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering glfw CLI11::CLI11) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Core LiteFX.Math LiteFX.AppModel LiteFX.Rendering LiteFX.Graphics glfw CLI11::CLI11) IF(LITEFX_BUILD_VULKAN_BACKEND) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE LiteFX.Backends.Vulkan) diff --git a/src/Samples/UniformArrays/src/sample.h b/src/Samples/UniformArrays/src/sample.h index 57bab2a05..6bb35c704 100644 --- a/src/Samples/UniformArrays/src/sample.h +++ b/src/Samples/UniformArrays/src/sample.h @@ -2,6 +2,7 @@ #define LITEFX_AUTO_IMPORT_BACKEND_HEADERS #include +#include #if (defined _WIN32 || defined WINCE) # define GLFW_EXPOSE_NATIVE_WIN32 @@ -24,6 +25,7 @@ extern RENDERDOC_API_1_5_0* renderDoc; using namespace LiteFX; using namespace LiteFX::Rendering; using namespace LiteFX::Rendering::Backends; +using namespace LiteFX::Graphics; struct GlfwWindowDeleter { void operator()(GLFWwindow* ptr) noexcept {