Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
865e606
Merge branch 'mesh_loaders' into new_debug_draw
keptsecret Jul 1, 2025
ca86128
latest example
keptsecret Jul 1, 2025
0ae3da2
merge master, fix conflicts
keptsecret Jul 2, 2025
cd2ef95
latest example
keptsecret Jul 4, 2025
fe55bd7
merge master
keptsecret Jul 4, 2025
98ccfb2
added debug draw aabb extension, moved from ex
keptsecret Jul 8, 2025
a755514
removed todos
keptsecret Jul 8, 2025
473592b
support hlsl AABBs, also OBBs with transform
keptsecret Jul 9, 2025
f68f9c5
merge master, fix conflicts
keptsecret Aug 18, 2025
daf34e0
minor syntax changes
keptsecret Aug 18, 2025
33692fd
use hlsl cpp compat matrices, aabb
keptsecret Aug 19, 2025
72e3569
change batch render to use indexed draw
keptsecret Aug 19, 2025
5285e78
simplified single AABB draw
keptsecret Aug 20, 2025
328aa34
change batch render to take span of InstanceData
keptsecret Aug 20, 2025
a14c9dc
latest example
keptsecret Aug 20, 2025
9a35c9f
removed vertex buffer, use const vertex array in shader instead
keptsecret Aug 20, 2025
c6bd10b
validate creation params, added draw modes at create time
keptsecret Aug 20, 2025
1cb4c14
merge master, fix conflicts
keptsecret Aug 21, 2025
31e93f0
merge master, fix conflicts
keptsecret Sep 8, 2025
e5ceb1b
enable debug draw by default
keptsecret Sep 8, 2025
fe0a438
merge master, fix conflicts
keptsecret Sep 16, 2025
bfa233f
fix embed builtin resource build
keptsecret Sep 16, 2025
3b67580
resolve https://github.com/Devsh-Graphics-Programming/Nabla/pull/900#…
AnastaZIuk Sep 16, 2025
aae42fa
merge master, fix conflicts
keptsecret Nov 27, 2025
0879ce7
fix + optimize aabb vertex calc, includes
keptsecret Nov 27, 2025
1f73ca9
changed debug_draw library target usage
keptsecret Nov 27, 2025
a40f540
some fixes to draw aabb
keptsecret Nov 27, 2025
f0f9957
removed commented out bit
keptsecret Nov 28, 2025
fdd675b
create pipelineLayout util can takes mode, also create layout if miss…
keptsecret Nov 28, 2025
37cc551
aabb local transform is 3x4, common draw param struct between single …
keptsecret Nov 28, 2025
ba2860f
write instances data directly to streaming buffer mem
keptsecret Nov 28, 2025
2678ffc
use single use cmdbuf to fill indices buffer
keptsecret Dec 1, 2025
bde9dfb
merge master
keptsecret Dec 17, 2025
5aee002
roll constructor params into own struct, fix assert in validation
keptsecret Dec 18, 2025
a1bd026
adds a check against double mounting same archive
keptsecret Dec 18, 2025
0c17074
Merge branch 'master' into new_debug_draw
keptsecret Dec 19, 2025
fcee6ed
return false if the streaming buffer is too small
keptsecret Dec 19, 2025
cdd362b
some fixes to using/filling streaming buffer
keptsecret Dec 22, 2025
82f6f59
Merge branch 'master' into new_debug_draw
keptsecret Dec 22, 2025
4ae7d89
combined draw aabb shaders into unified, added precompile shaders to …
keptsecret Dec 22, 2025
9513614
restore ifdef for mounting builtin resources, minor fixes to mounting
keptsecret Dec 23, 2025
ac51887
simplified usage of streaming buffer alignments, flush unused memory …
keptsecret Dec 23, 2025
5ebfc65
fix calculating remaining instances bytes
keptsecret Dec 23, 2025
38b305b
merge master, fix conflicts
keptsecret Dec 23, 2025
6f4ef5b
check whether spirv exists
keptsecret Dec 23, 2025
1e8171c
try to fit as much as possible even when fail to allocate, go down by…
keptsecret Dec 23, 2025
84a2e1e
Merge branch 'master' into new_debug_draw
keptsecret Dec 23, 2025
a70a863
update include paths for debug draw ext
AnastaZIuk Dec 23, 2025
1badc7a
fix mount logic for debug draw ext, perform tests on builtins on/off
AnastaZIuk Dec 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ option(NBL_FAST_MATH "Enable fast low-precision math" OFF) # the reason OFF is b
option(NBL_BUILD_EXAMPLES "Enable building examples" ON)
option(NBL_BUILD_MITSUBA_LOADER "Enable nbl::ext::MitsubaLoader?" OFF) # TODO: once it compies turn this ON by default!
option(NBL_BUILD_IMGUI "Enable nbl::ext::ImGui?" ON)
option(NBL_BUILD_DEBUG_DRAW "Enable Nabla Debug Draw extension?" ON)

option(NBL_BUILD_OPTIX "Enable nbl::ext::OptiX?" OFF)
if(NBL_COMPILE_WITH_CUDA)
Expand Down
2 changes: 2 additions & 0 deletions include/nbl/config/BuildConfigOptions.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@

#cmakedefine _NBL_BUILD_DPL_

#cmakedefine NBL_BUILD_DEBUG_DRAW

// !
// TODO: This has to disapppear from the main header and go to the OptiX extension header + config
#cmakedefine OPTIX_INCLUDE_DIR "@OPTIX_INCLUDE_DIR@"
Expand Down
242 changes: 242 additions & 0 deletions include/nbl/ext/DebugDraw/CDrawAABB.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O.
// This file is part of the "Nabla Engine".
// For conditions of distribution and use, see copyright notice in nabla.h

#ifndef _NBL_EXT_DEBUG_DRAW_DRAW_AABB_H_
#define _NBL_EXT_DEBUG_DRAW_DRAW_AABB_H_

#include "nbl/video/declarations.h"
#include "nbl/builtin/hlsl/cpp_compat.hlsl"
#include "nbl/builtin/hlsl/shapes/aabb.hlsl"
#include "nbl/builtin/hlsl/math/linalg/fast_affine.hlsl"
#include "nbl/ext/DebugDraw/builtin/hlsl/common.hlsl"

namespace nbl::ext::debug_draw
{
class DrawAABB final : public core::IReferenceCounted
{
public:
static constexpr inline uint32_t IndicesCount = 24u;

enum DrawMode : uint16_t
{
ADM_DRAW_SINGLE = 0b01,
ADM_DRAW_BATCH = 0b10,
ADM_DRAW_BOTH = 0b11
};

struct SCachedCreationParameters
{
using streaming_buffer_t = video::StreamingTransientDataBufferST<core::allocator<uint8_t>>;

static constexpr inline auto RequiredAllocateFlags = core::bitflag<video::IDeviceMemoryAllocation::E_MEMORY_ALLOCATE_FLAGS>(video::IDeviceMemoryAllocation::EMAF_DEVICE_ADDRESS_BIT);
static constexpr inline auto RequiredUsageFlags = core::bitflag(asset::IBuffer::EUF_STORAGE_BUFFER_BIT) | asset::IBuffer::EUF_SHADER_DEVICE_ADDRESS_BIT;

DrawMode drawMode = ADM_DRAW_BOTH;

core::smart_refctd_ptr<video::IUtilities> utilities;

//! optional, default MDI buffer allocated if not provided
core::smart_refctd_ptr<streaming_buffer_t> streamingBuffer = nullptr;
};

struct SCreationParameters : SCachedCreationParameters
{
video::IQueue* transfer = nullptr; // only used to make the 24 element index buffer and instanced pipeline on create
core::smart_refctd_ptr<asset::IAssetManager> assetManager = nullptr;

core::smart_refctd_ptr<video::IGPUPipelineLayout> singlePipelineLayout = nullptr;
core::smart_refctd_ptr<video::IGPUPipelineLayout> batchPipelineLayout = nullptr;
core::smart_refctd_ptr<video::IGPURenderpass> renderpass = nullptr;

inline bool validate() const
{
const auto validation = std::to_array
({
std::make_pair(bool(assetManager), "Invalid `creationParams.assetManager` is nullptr!"),
std::make_pair(bool(utilities), "Invalid `creationParams.utilities` is nullptr!"),
std::make_pair(bool(transfer), "Invalid `creationParams.transfer` is nullptr!"),
std::make_pair(bool(renderpass), "Invalid `creationParams.renderpass` is nullptr!"),
std::make_pair(bool(utilities->getLogicalDevice()->getPhysicalDevice()->getQueueFamilyProperties()[transfer->getFamilyIndex()].queueFlags.hasFlags(video::IQueue::FAMILY_FLAGS::TRANSFER_BIT)), "Invalid `creationParams.transfer` is not capable of transfer operations!")
});

system::logger_opt_ptr logger = utilities->getLogger();
for (const auto& [ok, error] : validation)
if (!ok)
{
logger.log(error, system::ILogger::ELL_ERROR);
return false;
}

assert(bool(assetManager->getSystem()));

return true;
}
};

struct DrawParameters
{
video::IGPUCommandBuffer* commandBuffer = nullptr;
hlsl::float32_t4x4 cameraMat;
float lineWidth = 1.f;
};

// creates an instance that can draw one AABB via push constant or multiple using streaming buffer
static core::smart_refctd_ptr<DrawAABB> create(SCreationParameters&& params);

// creates pipeline layout from push constant range
static core::smart_refctd_ptr<video::IGPUPipelineLayout> createPipelineLayoutFromPCRange(video::ILogicalDevice* device, const asset::SPushConstantRange& pcRange);

// creates default pipeline layout for pipeline specified by draw mode (note: if mode==BOTH, returns layout for BATCH mode)
static core::smart_refctd_ptr<video::IGPUPipelineLayout> createDefaultPipelineLayout(video::ILogicalDevice* device, DrawMode mode = ADM_DRAW_BATCH);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the last argument should not be defaulted!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


//! mounts the extension's archive to given system - useful if you want to create your own shaders with common header included
static const core::smart_refctd_ptr<system::IFileArchive> mount(core::smart_refctd_ptr<system::ILogger> logger, system::ISystem* system, video::ILogicalDevice* device, const std::string_view archiveAlias = "");

inline const SCachedCreationParameters& getCreationParameters() const { return m_cachedCreationParams; }

// records draw command for single AABB, user has to set pipeline outside
bool renderSingle(const DrawParameters& params, const hlsl::shapes::AABB<3, float>& aabb, const hlsl::float32_t4& color);

// records draw command for rendering batch of AABB instances as InstanceData
// user has to set span of filled-in InstanceData; camera matrix used in push constant
inline bool render(const DrawParameters& params, video::ISemaphore::SWaitInfo waitInfo, std::span<const InstanceData> aabbInstances)
{
system::logger_opt_ptr logger = m_cachedCreationParams.utilities->getLogger();
if (!(m_cachedCreationParams.drawMode & ADM_DRAW_BATCH))
{
logger.log("DrawAABB has not been enabled for draw batches!", system::ILogger::ELL_ERROR);
return false;
}

using offset_t = SCachedCreationParameters::streaming_buffer_t::size_type;
constexpr offset_t MaxAlignment = sizeof(InstanceData);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alignof not sizeof

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should also assert that the alignment is <= sizeof(InstanceData) due to the roundups and divisions you do later in the loop

// allocator initialization needs us to round up to PoT
const auto MaxPOTAlignment = hlsl::roundUpToPoT(MaxAlignment);
auto* streaming = m_cachedCreationParams.streamingBuffer.get();
if (streaming->getAddressAllocator().max_alignment() < MaxPOTAlignment)
{
logger.log("Draw AABB Streaming Buffer cannot guarantee the alignments we require!");
return false;
}

auto* const streamingPtr = reinterpret_cast<uint8_t*>(streaming->getBufferPointer());
assert(streamingPtr);

auto& commandBuffer = params.commandBuffer;
commandBuffer->bindGraphicsPipeline(m_batchPipeline.get());
commandBuffer->setLineWidth(params.lineWidth);
asset::SBufferBinding<video::IGPUBuffer> indexBinding = { .offset = 0, .buffer = m_indicesBuffer };
commandBuffer->bindIndexBuffer(indexBinding, asset::EIT_32BIT);

auto srcIt = aabbInstances.begin();
auto setInstancesRange = [&](InstanceData* data, uint32_t count) -> void {
for (uint32_t i = 0; i < count; i++)
{
auto inst = data + i;
*inst = *srcIt;
inst->transform = hlsl::mul(params.cameraMat, inst->transform);
srcIt++;

if (srcIt == aabbInstances.end())
break;
Comment on lines +141 to +142

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should never happen, assert srcIt<=end() before the ++ or srcIt<end()

}
};

const uint32_t numInstances = aabbInstances.size();
uint32_t remainingInstancesBytes = numInstances * sizeof(InstanceData);
while (srcIt != aabbInstances.end())
{
uint32_t blockByteSize = core::alignUp(remainingInstancesBytes, MaxAlignment);
bool allocated = false;

offset_t blockOffset = SCachedCreationParameters::streaming_buffer_t::invalid_value;
const uint32_t smallestAlloc = hlsl::max<uint32_t>(core::alignUp(sizeof(InstanceData), MaxAlignment), streaming->getAddressAllocator().min_size());
while (blockByteSize >= smallestAlloc)
{
std::chrono::steady_clock::time_point waitTill = std::chrono::steady_clock::now() + std::chrono::milliseconds(1u);
if (streaming->multi_allocate(waitTill, 1, &blockOffset, &blockByteSize, &MaxAlignment) == 0u)
{
allocated = true;
break;
}

streaming->cull_frees();
blockByteSize >>= 1;
}

if (!allocated)
{
logger.log("Failed to allocate a chunk from streaming buffer for the next drawcall batch.", system::ILogger::ELL_ERROR);
return false;
}

const uint32_t instanceCount = blockByteSize / sizeof(InstanceData);
auto* const streamingInstancesPtr = reinterpret_cast<InstanceData*>(streamingPtr + blockOffset);
setInstancesRange(streamingInstancesPtr, instanceCount);

if (streaming->needsManualFlushOrInvalidate())
{
const video::ILogicalDevice::MappedMemoryRange flushRange(streaming->getBuffer()->getBoundMemory().memory, blockOffset, blockByteSize);
m_cachedCreationParams.utilities->getLogicalDevice()->flushMappedMemoryRanges(1, &flushRange);
}

remainingInstancesBytes -= instanceCount * sizeof(InstanceData);

SInstancedPC pc;
pc.pInstanceBuffer = m_cachedCreationParams.streamingBuffer->getBuffer()->getDeviceAddress() + blockOffset;

commandBuffer->pushConstants(m_batchPipeline->getLayout(), asset::IShader::E_SHADER_STAGE::ESS_VERTEX, offsetof(ext::debug_draw::PushConstants, ipc), sizeof(SInstancedPC), &pc);
commandBuffer->drawIndexed(IndicesCount, instanceCount, 0, 0, 0);

streaming->multi_deallocate(1, &blockOffset, &blockByteSize, waitInfo);
}

return true;
}

static inline hlsl::float32_t3x4 getTransformFromAABB(const hlsl::shapes::AABB<3, float>& aabb)
{
const auto diagonal = aabb.getExtent();
hlsl::float32_t3x4 transform;
transform[0][3] = aabb.minVx.x;
transform[1][3] = aabb.minVx.y;
transform[2][3] = aabb.minVx.z;
transform[0][0] = diagonal.x;
transform[1][1] = diagonal.y;
transform[2][2] = diagonal.z;
return transform;
}

protected:
struct ConstructorParams
{
SCachedCreationParameters creationParams;
core::smart_refctd_ptr<video::IGPUGraphicsPipeline> singlePipeline = nullptr;
core::smart_refctd_ptr<video::IGPUGraphicsPipeline> batchPipeline = nullptr;
core::smart_refctd_ptr<video::IGPUBuffer> indicesBuffer = nullptr;
};

DrawAABB(ConstructorParams&& params) :
m_cachedCreationParams(std::move(params.creationParams)),
m_singlePipeline(std::move(params.singlePipeline)),
m_batchPipeline(std::move(params.batchPipeline)),
m_indicesBuffer(std::move(params.indicesBuffer))
{}
~DrawAABB() override {}

private:
static core::smart_refctd_ptr<video::IGPUGraphicsPipeline> createPipeline(SCreationParameters& params, const video::IGPUPipelineLayout* pipelineLayout, const DrawMode mode);
static bool createStreamingBuffer(SCreationParameters& params);
static core::smart_refctd_ptr<video::IGPUBuffer> createIndicesBuffer(SCreationParameters& params);

core::smart_refctd_ptr<video::IGPUBuffer> m_indicesBuffer;

SCachedCreationParameters m_cachedCreationParams;

core::smart_refctd_ptr<video::IGPUGraphicsPipeline> m_singlePipeline;
core::smart_refctd_ptr<video::IGPUGraphicsPipeline> m_batchPipeline;
};
}

#endif
56 changes: 56 additions & 0 deletions include/nbl/ext/DebugDraw/builtin/hlsl/common.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#ifndef _NBL_DEBUG_DRAW_EXT_COMMON_HLSL
#define _NBL_DEBUG_DRAW_EXT_COMMON_HLSL

#include "nbl/builtin/hlsl/cpp_compat.hlsl"
#ifdef __HLSL_VERSION
#include "nbl/builtin/hlsl/math/linalg/fast_affine.hlsl"
#include "nbl/builtin/hlsl/glsl_compat/core.hlsl"
#include "nbl/builtin/hlsl/bda/__ptr.hlsl"
#endif

namespace nbl
{
namespace ext
{
namespace debug_draw
{

struct InstanceData
{
hlsl::float32_t4x4 transform;
hlsl::float32_t4 color;
};

struct SSinglePC
{
InstanceData instance;
};

struct SInstancedPC
{
uint64_t pInstanceBuffer;
};

struct PushConstants
{
SSinglePC spc;
SInstancedPC ipc;
};

#ifdef __HLSL_VERSION
struct PSInput
{
float32_t4 position : SV_Position;
nointerpolation float32_t4 color : TEXCOORD0;
};

float32_t3 getUnitAABBVertex()
{
return (hlsl::promote<uint32_t3>(hlsl::glsl::gl_VertexIndex()) >> uint32_t3(0,2,1)) & 0x1u;
}
#endif

}
}
}
#endif
39 changes: 39 additions & 0 deletions include/nbl/ext/DebugDraw/builtin/hlsl/draw_aabb.unified.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "nbl/ext/DebugDraw/builtin/hlsl/common.hlsl"

using namespace nbl::hlsl;
using namespace nbl::ext::debug_draw;

[[vk::push_constant]] PushConstants pc;

[shader("vertex")]
PSInput aabb_vertex_single()
{
PSInput output;
float32_t3 vertex = getUnitAABBVertex();

output.position = math::linalg::promoted_mul(pc.spc.instance.transform, vertex);
output.color = pc.spc.instance.color;

return output;
}

[shader("vertex")]
PSInput aabb_vertex_instances()
{
PSInput output;
const float32_t3 vertex = getUnitAABBVertex();
InstanceData instance = vk::BufferPointer<InstanceData>(pc.ipc.pInstanceBuffer + sizeof(InstanceData) * glsl::gl_InstanceIndex()).Get();

output.position = math::linalg::promoted_mul(instance.transform, vertex);
output.color = instance.color;

return output;
}

[shader("pixel")]
float32_t4 aabb_fragment(PSInput input) : SV_TARGET
{
float32_t4 outColor = input.color;

return outColor;
}
1 change: 1 addition & 0 deletions include/nbl/system/ISystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class NBL_API2 ISystem : public core::IReferenceCounted
//
virtual inline bool isDirectory(const system::path& p) const
{
// TODO: fix bug, input "nbl/ext/DebugDraw/builtin/hlsl" -> returs true when no such dir present in mounted stuff due to how it uses parent paths in loop (goes up up till matches "nbl" builtin archive and thinks it resolved the requested dir)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnastaZIuk open an issue about it

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnastaZIuk do you think you could make a fix for this?

if (isPathReadOnly(p))
return p.extension()==""; // TODO: this is a temporary decision until we figure out how to check if a file is directory in android APK
else
Expand Down
12 changes: 12 additions & 0 deletions src/nbl/ext/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ if(NBL_BUILD_TEXT_RENDERING)
add_subdirectory(TextRendering)
endif()

if(NBL_BUILD_DEBUG_DRAW)
add_subdirectory(DebugDraw)
set(NBL_EXT_DEBUG_DRAW_INCLUDE_DIRS
${NBL_EXT_DEBUG_DRAW_INCLUDE_DIRS}
PARENT_SCOPE
)
set(NBL_EXT_DEBUG_DRAW_LIB
${NBL_EXT_DEBUG_DRAW_LIB}
PARENT_SCOPE
)
endif()

Comment on lines +57 to +68

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnastaZIuk review please

Copy link
Member

@AnastaZIuk AnastaZIuk Dec 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approved,

although this snippet which is copied for each ext requires rewrite at some point (dirty), the rewrite shall not be part of this PR

propagate_changed_variables_to_parent_scope()

NBL_ADJUST_FOLDERS(ext)
Loading
Loading