Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate mip-map generation into graphics utilities. #144

Merged
merged 12 commits into from
Dec 15, 2024
1 change: 1 addition & 0 deletions docs/release-logs/0.4.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:**

Expand Down
6 changes: 6 additions & 0 deletions src/AppModel/include/litefx/app.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ namespace LiteFX {
std::type_index typeId() const noexcept { return typeid(*this); }
};

/// <summary>
/// Concept that can be used to refer to backend implementations.
/// </summary>
template <typename T>
concept backend = meta::implements<T, IBackend>;

/// <summary>
/// Base class for additional event arguments.
/// </summary>
Expand Down
10 changes: 0 additions & 10 deletions src/Backends/DirectX12/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 7 additions & 27 deletions src/Backends/DirectX12/include/litefx/backends/dx12.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1225,15 +1225,11 @@ namespace LiteFX::Rendering::Backends {
return SharedObject::create<DirectX12PipelineLayout>(device);
}

public:
/// <summary>
/// Returns a pointer to the device that provides this layout or `nullptr` if the device is already released.
/// </summary>
/// <returns>A reference to the layouts parent device or `nullptr` if the device is already released.</returns>
virtual SharedPtr<const DirectX12Device> device() const noexcept;

// PipelineLayout interface.
public:
/// <inheritdoc />
const DirectX12Device& device() const noexcept /*override*/;

/// <inheritdoc />
const DirectX12DescriptorSetLayout& descriptorSet(UInt32 space) const override;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1526,9 +1521,6 @@ namespace LiteFX::Rendering::Backends {
/// <inheritdoc />
UInt64 submit() const override;

/// <inheritdoc />
void generateMipMaps(IDirectX12Image& image) override;

/// <inheritdoc />
[[nodiscard]] UniquePtr<DirectX12Barrier> makeBarrier(PipelineStage syncBefore, PipelineStage syncAfter) const override;

Expand Down Expand Up @@ -2298,16 +2290,14 @@ namespace LiteFX::Rendering::Backends {
return SharedObject::create<DirectX12RenderPass>(device, name);
}

// DirectX 12 render pass.
// RenderPass interface.
public:
/// <summary>
/// Returns a reference to the device that provides this queue.
/// Returns a reference of the device that provides this queue.
/// </summary>
/// <returns>A reference to the queue's parent device.</returns>
virtual SharedPtr<const DirectX12Device> device() const noexcept;
/// <returns>A reference of the queue's parent device.</returns>
const DirectX12Device& device() const noexcept /*override*/;

// RenderPass interface.
public:
/// <inheritdoc />
SharedPtr<const DirectX12FrameBuffer> activeFrameBuffer() const noexcept override;

Expand Down Expand Up @@ -2715,16 +2705,6 @@ namespace LiteFX::Rendering::Backends {
/// <param name="commandBuffer">The command buffer to issue the bind command on.</param>
void bindGlobalDescriptorHeaps(const DirectX12CommandBuffer& commandBuffer) const noexcept;

/// <summary>
/// Returns the compute pipeline that can be invoked to blit an image resource.
/// </summary>
/// <remarks>
/// Blitting is used by <see cref="DirectX12Texture" /> to generate mip maps.
/// </remarks>
/// <returns>The compute pipeline that can be invoked to blit an image resource.</returns>
/// <seealso cref="DirectX12Texture::generateMipMaps" />
DirectX12ComputePipeline& blitPipeline() const noexcept;

/// <summary>
/// Returns the command signatures for indirect dispatch and draw calls.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ namespace LiteFX::Rendering::Backends {
/// </summary>
/// <seealso cref="DirectX12ShaderProgram" />
class LITEFX_DIRECTX12_API [[nodiscard]] DirectX12ShaderProgramBuilder final : public ShaderProgramBuilder<DirectX12ShaderProgram> {
LITEFX_IMPLEMENTATION(DirectX12ShaderProgramBuilderImpl);

public:
/// <summary>
/// Initializes a DirectX 12 shader program builder.
Expand Down
89 changes: 0 additions & 89 deletions src/Backends/DirectX12/src/command_buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Parameters> parametersData(image.levels());

std::ranges::generate(parametersData, [&image, i = 0]() mutable {
auto level = i++;

return Parameters {
.sizeX = 1.f / static_cast<Float>(std::max<size_t>(image.extent(level).width(), 1)),
.sizeY = 1.f / static_cast<Float>(std::max<size_t>(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<const void*>(&parameters); }) |
std::ranges::to<Array<const void*>>();

// 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<UInt32>(static_cast<UInt32>(size.width() / 8), 1), std::max<UInt32>(static_cast<UInt32>(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<DirectX12Barrier> DirectX12CommandBuffer::makeBarrier(PipelineStage syncBefore, PipelineStage syncAfter) const
{
// Check if the device is still valid.
Expand Down
23 changes: 8 additions & 15 deletions src/Backends/DirectX12/src/compute_pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ class DirectX12ComputePipeline::DirectX12ComputePipelineImpl {
friend class DirectX12ComputePipeline;

private:
WeakPtr<const DirectX12Device> m_device;
SharedPtr<const DirectX12Device> m_device;
SharedPtr<DirectX12PipelineLayout> m_layout;
SharedPtr<DirectX12ShaderProgram> m_program;

public:
DirectX12ComputePipelineImpl(const DirectX12Device& device, const SharedPtr<DirectX12PipelineLayout>& layout, const SharedPtr<DirectX12ShaderProgram>& 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())
{
}

Expand Down Expand Up @@ -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<ID3D12PipelineState> pipelineState;
raiseIfFailed(device->handle()->CreateComputePipelineState(&pipelineStateDescription, IID_PPV_ARGS(&pipelineState)), "Unable to create compute pipeline state.");
// Create the pipeline state instance.
ComPtr<ID3D12PipelineState> 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;
}
};

Expand Down
39 changes: 0 additions & 39 deletions src/Backends/DirectX12/src/device.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include <litefx/backends/dx12.hpp>
#include <litefx/backends/dx12_builders.hpp>
#include <shader_resources.hpp>

using namespace LiteFX::Rendering::Backends;

Expand All @@ -19,7 +18,6 @@ class DirectX12Device::DirectX12DeviceImpl {
SharedPtr<DirectX12Queue> m_graphicsQueue{}, m_transferQueue{}, m_computeQueue{};
Array<SharedPtr<DirectX12Queue>> m_queues;
SharedPtr<DirectX12GraphicsFactory> m_factory;
UniquePtr<DirectX12ComputePipeline> m_blitPipeline;
ComPtr<ID3D12InfoQueue1> m_eventQueue;
UniquePtr<DirectX12SwapChain> m_swapChain;
DWORD m_debugCallbackCookie = 0;
Expand Down Expand Up @@ -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<UniquePtr<DirectX12ShaderModule>> modules;
auto blitShader = LiteFX::Backends::DirectX12::Shaders::blit_dxi::open();
modules.push_back(makeUnique<DirectX12ShaderModule>(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<DirectX12PushConstantsLayout> pushConstantsLayout = nullptr;
auto bufferLayouts = Enumerable<DirectX12DescriptorLayout>(DirectX12DescriptorLayout { DescriptorType::ConstantBuffer, 0, 16 }, DirectX12DescriptorLayout { DescriptorType::Texture, 1, 0 }, DirectX12DescriptorLayout { DescriptorType::RWTexture, 2, 0 });
auto samplerLayouts = Enumerable<DirectX12DescriptorLayout>(DirectX12DescriptorLayout { DescriptorType::Sampler, 0, 0 } );
auto descriptorSetLayouts = Enumerable<SharedPtr<DirectX12DescriptorSetLayout>>(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<DirectX12ComputePipeline>(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<Format> getSurfaceFormats() const
{
Expand Down Expand Up @@ -267,7 +235,6 @@ SharedPtr<DirectX12Device> DirectX12Device::initialize(const DirectX12Backend& b
m_impl->createQueues(*this);
m_impl->m_factory = DirectX12GraphicsFactory::create(*this);
m_impl->m_swapChain = UniquePtr<DirectX12SwapChain>(new DirectX12SwapChain(*this, backend, format, renderArea, backBuffers, enableVsync));
m_impl->createBlitPipeline(*this);

return this->shared_from_this();
}
Expand All @@ -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();
Expand Down Expand Up @@ -449,11 +415,6 @@ void DirectX12Device::bindGlobalDescriptorHeaps(const DirectX12CommandBuffer& co
commandBuffer.handle()->SetDescriptorHeaps(static_cast<UINT>(globalHeaps.size()), globalHeaps.data());
}

DirectX12ComputePipeline& DirectX12Device::blitPipeline() const noexcept
{
return *m_impl->m_blitPipeline;
}

void DirectX12Device::indirectDrawSignatures(ComPtr<ID3D12CommandSignature>& dispatchSignature, ComPtr<ID3D12CommandSignature>& dispatchMeshSignature, ComPtr<ID3D12CommandSignature>& drawSignature, ComPtr<ID3D12CommandSignature>& drawIndexedSignature) const noexcept
{
dispatchSignature = m_impl->m_dispatchSignature;
Expand Down
Loading