-
Notifications
You must be signed in to change notification settings - Fork 67
New debug draw extension for AABBs #900
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
Changes from all commits
865e606
ca86128
0ae3da2
cd2ef95
fe55bd7
98ccfb2
a755514
473592b
f68f9c5
daf34e0
33692fd
72e3569
5285e78
328aa34
a14c9dc
9a35c9f
c6bd10b
1cb4c14
31e93f0
e5ceb1b
fe0a438
bfa233f
3b67580
aae42fa
0879ce7
1f73ca9
a40f540
f0f9957
fdd675b
37cc551
ba2860f
2678ffc
bde9dfb
5aee002
a1bd026
0c17074
fcee6ed
cdd362b
82f6f59
4ae7d89
9513614
ac51887
5ebfc65
38b305b
6f4ef5b
1e8171c
84a2e1e
a70a863
1badc7a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| +5 −0 | 12_MeshLoaders/CMakeLists.txt | |
| +0 −28 | 12_MeshLoaders/config.json.template | |
| +121 −68 | 12_MeshLoaders/main.cpp | |
| +0 −50 | 12_MeshLoaders/pipeline.groovy | |
| +9 −0 | 34_DebugDraw/CMakeLists.txt | |
| +22 −0 | 34_DebugDraw/include/common.hpp | |
| +369 −0 | 34_DebugDraw/main.cpp | |
| +2 −0 | CMakeLists.txt |
| 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); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the last argument should not be defaulted!
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. alignof not sizeof
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should never happen, assert |
||
| } | ||
| }; | ||
|
|
||
| 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 | ||
| 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 |
| 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; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AnastaZIuk open an issue about it
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AnastaZIuk review please
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
Uh oh!
There was an error while loading. Please reload this page.