Skip to content

Commit

Permalink
Merge pull request #144 from crud89/gfx-utils
Browse files Browse the repository at this point in the history
Separate mip-map generation into graphics utilities.
  • Loading branch information
crud89 authored Dec 15, 2024
2 parents ef2704e + b699b14 commit c18042b
Show file tree
Hide file tree
Showing 66 changed files with 707 additions and 720 deletions.
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

0 comments on commit c18042b

Please sign in to comment.