diff --git a/AGENT.md b/AGENT.md index 4b55dc93..16fb63e1 100644 --- a/AGENT.md +++ b/AGENT.md @@ -61,6 +61,10 @@ xmake r -g test/* # for all groups To build only a specific target group: `xmake build -g test/math`, `xmake build -g test/gfx`, etc. +### Test Artifacts + +Save any test artifacts generated during agent runs under ./temp. + ## Architecture Overview ### Module System @@ -154,3 +158,4 @@ int main() { - Vulkan backend works on Windows and Linux (Wayland). - Runtime is set to `MD`/`MDd` (dynamic CRT) on Windows. - Source files use UTF-8 encoding (`set_encodings("utf-8")`). + diff --git a/hitagi/asset/material_parser.cpp b/hitagi/asset/material_parser.cpp index 405eeb4b..e2443a99 100644 --- a/hitagi/asset/material_parser.cpp +++ b/hitagi/asset/material_parser.cpp @@ -82,13 +82,17 @@ auto MaterialJSONParser::Parse(const core::Buffer& buffer) -> std::shared_ptr std::shared_ptr(param["default"]); + else + mat_param.value = std::shared_ptr{}; } else { logger->error("Unkown parameter type: {}", param["type"].get()); return nullptr; diff --git a/hitagi/core/file_io_manager.cppm b/hitagi/core/file_io_manager.cppm index a24d0abd..9fe90aa6 100644 --- a/hitagi/core/file_io_manager.cppm +++ b/hitagi/core/file_io_manager.cppm @@ -1,3 +1,7 @@ +module; + +#include + export module core:file_io_manager; import std; import :runtime_module; @@ -24,7 +28,7 @@ private: using PathHash = std::size_t; - std::mutex m_CacheMutex; + TracyLockableN(std::mutex, m_CacheMutex, "FileIO Cache Mutex"); std::pmr::unordered_map m_FileStateCache; std::pmr::unordered_map m_FileCache; Buffer m_EmptyBuffer; diff --git a/hitagi/core/memory_manager.cppm b/hitagi/core/memory_manager.cppm index c2120d38..06ee48b0 100644 --- a/hitagi/core/memory_manager.cppm +++ b/hitagi/core/memory_manager.cppm @@ -1,5 +1,6 @@ module; #include +#include export module core:memory_manager; import std; @@ -63,7 +64,7 @@ private: }; struct Pool { - std::mutex mutex{}; + TracyLockableN(std::mutex, mutex, "MemoryPool Mutex"); std::list pages{}; Block* free_list = nullptr; std::size_t page_size = 8_kB; diff --git a/hitagi/core/thread_manager.cpp b/hitagi/core/thread_manager.cpp index bbdc0bfb..b3e8caf1 100644 --- a/hitagi/core/thread_manager.cpp +++ b/hitagi/core/thread_manager.cpp @@ -1,5 +1,6 @@ module; #include +#include module core; @@ -7,9 +8,12 @@ namespace hitagi::core { ThreadManager::ThreadManager(std::uint8_t num_threads) : RuntimeModule("ThreadManager"), m_Stop(false) { m_Logger->trace("create thread pool({})", num_threads); + TracyPlotConfig("Thread Tasks Pending", tracy::PlotFormatType::Number, true, true, 0); for (std::uint8_t i = 0; i < num_threads; i++) { - m_ThreadPools.emplace_back([this] { + m_ThreadPools.emplace_back([this, i] { + const auto thread_name = std::format("Hitagi/Worker-{}", i); + tracy::SetThreadName(thread_name.c_str()); while (true) { std::packaged_task task; { @@ -19,6 +23,7 @@ ThreadManager::ThreadManager(std::uint8_t num_threads) : RuntimeModule("ThreadMa return; task = std::move(m_Tasks.front()); m_Tasks.pop(); + TracyPlot("Thread Tasks Pending", static_cast(m_Tasks.size())); } task(); } diff --git a/hitagi/core/thread_manager.cppm b/hitagi/core/thread_manager.cppm index 92a6ce45..c93f6174 100644 --- a/hitagi/core/thread_manager.cppm +++ b/hitagi/core/thread_manager.cppm @@ -1,3 +1,7 @@ +module; + +#include + export module core:thread_manager; import std; import :runtime_module; @@ -25,6 +29,8 @@ private: bool m_Stop; }; +auto CreateThreadManager(std::uint8_t num_threads = 8) -> std::unique_ptr; + template decltype(auto) ThreadManager::RunTask(Func&& func, Args&&... args) { using return_type = std::invoke_result_t; @@ -37,10 +43,15 @@ decltype(auto) ThreadManager::RunTask(Func&& func, Args&&... args) { { std::unique_lock lock(m_QueueMutex); m_Tasks.emplace([task] { (*task)(); }); + TracyPlot("Thread Tasks Pending", static_cast(m_Tasks.size())); } m_ConditionForTask.notify_one(); return res; } +inline auto CreateThreadManager(std::uint8_t num_threads) -> std::unique_ptr { + return std::make_unique(num_threads); +} + } // namespace hitagi::core diff --git a/hitagi/ecs/schedule.cpp b/hitagi/ecs/schedule.cpp index 6b82bf4e..dd283bc8 100644 --- a/hitagi/ecs/schedule.cpp +++ b/hitagi/ecs/schedule.cpp @@ -1,6 +1,7 @@ module; #include #include +#include module ecs; import std; @@ -42,7 +43,20 @@ void Schedule::Run(tf::Executor& executor) { direct_graph[i] = {}; for (const auto& task : m_Tasks) { - tasks.emplace_back(taskflow.emplace([&]() { task->Run(world); }).name(task->name.data())); + tasks.emplace_back(taskflow.emplace([this, &executor, task]() { + thread_local bool tracy_thread_named = false; + if (!tracy_thread_named) { + if (const auto worker_id = executor.this_worker_id(); worker_id >= 0) { + const auto thread_name = std::format("Hitagi/ECS/{}/Worker-{}", world.GetName(), worker_id); + tracy::SetThreadName(thread_name.c_str()); + tracy_thread_named = true; + } + } + + ZoneScoped; + ZoneName(task->name.data(), task->name.size()); + task->Run(world); + }).name(task->name.data())); } for (const auto& [component, task_indices] : m_ReadBeforeWriteSet) { @@ -97,6 +111,7 @@ void Schedule::Run(tf::Executor& executor) { return; } + ZoneScopedN("ECSFrame"); executor.run(taskflow).wait(); } diff --git a/hitagi/ecs/world.cpp b/hitagi/ecs/world.cpp index 545f3e13..b4c4699c 100644 --- a/hitagi/ecs/world.cpp +++ b/hitagi/ecs/world.cpp @@ -1,5 +1,6 @@ module; #include +#include module ecs; import std; @@ -14,6 +15,7 @@ World::World(std::string_view name) } void World::Update() { + ZoneScopedN("ECS::World::Update"); Schedule schedule(*this); m_SystemManager.Update(schedule); schedule.Run(m_Executor); diff --git a/hitagi/ecs/world.cppm b/hitagi/ecs/world.cppm index 08b53021..47b5f31b 100644 --- a/hitagi/ecs/world.cppm +++ b/hitagi/ecs/world.cppm @@ -16,6 +16,7 @@ public: void Update(); + inline auto GetName() const noexcept -> std::string_view { return m_Name; } inline auto& GetEntityManager() noexcept { return m_EntityManager; } inline auto& GetSystemManager() noexcept { return m_SystemManager; } inline auto& GetEntityManager() const noexcept { return m_EntityManager; } diff --git a/hitagi/engine/engine.cpp b/hitagi/engine/engine.cpp index 469be4dc..2f88416a 100644 --- a/hitagi/engine/engine.cpp +++ b/hitagi/engine/engine.cpp @@ -26,13 +26,15 @@ class OutLogicArea : public RuntimeModule { }; Engine::Engine(const std::filesystem::path& config_path) : RuntimeModule("Engine") { + tracy::SetThreadName("Hitagi/Main"); + auto add_inner_module = [&](std::unique_ptr module) -> T* { return static_cast(RuntimeModule::AddSubModule(std::unique_ptr{module.release()})); }; add_inner_module(std::make_unique()); add_inner_module(std::make_unique()); - add_inner_module(std::make_unique()); + add_inner_module(core::CreateThreadManager()); // Input m_App = add_inner_module(Application::CreateApp(config_path)); // input manager is created here @@ -56,6 +58,13 @@ void Engine::Tick() { ZoneScopedN("Engine"); RuntimeModule::Tick(); m_Clock.Tick(); + + static bool tracy_plot_configured = false; + if (!tracy_plot_configured) { + TracyPlotConfig("CPU Frame Time (ms)", tracy::PlotFormatType::Number, false, true, 0); + tracy_plot_configured = true; + } + TracyPlot("CPU Frame Time (ms)", m_Clock.DeltaTime().count() * 1000.0); FrameMark; } @@ -74,4 +83,4 @@ auto Engine::AddSubModule(std::unique_ptr module, RuntimeModule* return OutLogicArea::Get()->AddSubModule(std::move(module), after); } -} // namespace hitagi \ No newline at end of file +} // namespace hitagi diff --git a/hitagi/gfx/dx12/dx12_bindless.cpp b/hitagi/gfx/dx12/dx12_bindless.cpp index 0f327a63..10145859 100644 --- a/hitagi/gfx/dx12/dx12_bindless.cpp +++ b/hitagi/gfx/dx12/dx12_bindless.cpp @@ -148,7 +148,6 @@ auto DX12BindlessUtils::CreateBindlessHandle(GPUBuffer& buffer, std::size_t inde descriptor_increment_size)); } - TracyAllocN((void*)handle.index, 1, "CBV_SRV_UAV_Bindless"); return handle; } @@ -204,7 +203,6 @@ auto DX12BindlessUtils::CreateBindlessHandle(Texture& texture, bool writable) -> descriptor_increment_size)); } - TracyAllocN((void*)handle.index, 1, "CBV_SRV_UAV_Bindless"); return handle; } @@ -230,7 +228,6 @@ auto DX12BindlessUtils::CreateBindlessHandle(Sampler& sampler) -> BindlessHandle handle.index, descriptor_increment_size)); - TracyAllocN((void*)handle.index, 1, "Sampler_Bindless"); return handle; } @@ -240,12 +237,6 @@ void DX12BindlessUtils::DiscardBindlessHandle(BindlessHandle handle) { std::scoped_lock lock{m_Mutex}; auto& pool = handle.type == BindlessHandleType::Sampler ? m_Available_Sampler_BindlessHandlePool : m_Available_CBV_SRV_UAV_BindlessHandlePool; - if (handle.type == BindlessHandleType::Sampler) { - TracyFreeN((void*)handle.index, "Sampler_Bindless"); - } else { - TracyFreeN((void*)handle.index, "CBV_SRV_UAV_Bindless"); - } - handle.type = BindlessHandleType::Invalid; handle.writable = 0; handle.version++; @@ -253,4 +244,4 @@ void DX12BindlessUtils::DiscardBindlessHandle(BindlessHandle handle) { pool.emplace_back(handle); } -} // namespace hitagi::gfx \ No newline at end of file +} // namespace hitagi::gfx diff --git a/hitagi/gfx/dx12/dx12_bindless.cppm b/hitagi/gfx/dx12/dx12_bindless.cppm index dd6ad435..b8ecf558 100644 --- a/hitagi/gfx/dx12/dx12_bindless.cppm +++ b/hitagi/gfx/dx12/dx12_bindless.cppm @@ -1,6 +1,7 @@ module; #include +#include #include export module gfx.dx12:bindless; @@ -30,7 +31,7 @@ public: inline auto GetBindlessRootSignature() const noexcept { return m_RootSignature; } private: - std::mutex m_Mutex; + TracyLockableN(std::mutex, m_Mutex, "DX12 Bindless Mutex"); ComPtr m_RootSignature; @@ -40,4 +41,4 @@ private: std::pmr::deque m_Available_CBV_SRV_UAV_BindlessHandlePool; std::pmr::deque m_Available_Sampler_BindlessHandlePool; }; -} // namespace hitagi::gfx \ No newline at end of file +} // namespace hitagi::gfx diff --git a/hitagi/gfx/dx12/dx12_command_list.cpp b/hitagi/gfx/dx12/dx12_command_list.cpp index 397839e7..f05deba2 100644 --- a/hitagi/gfx/dx12/dx12_command_list.cpp +++ b/hitagi/gfx/dx12/dx12_command_list.cpp @@ -1,12 +1,15 @@ module; +#include #include #include #include +#include module gfx.dx12; import std; import :command_list; +import :command_queue; import :utils; namespace hitagi::gfx { @@ -102,10 +105,26 @@ void DX12GraphicsCommandList::Begin() { command_list->SetDescriptorHeaps(descriptor_heaps.size(), descriptor_heaps.data()); command_list->SetGraphicsRootSignature(dx12_bindless_utils.GetBindlessRootSignature().Get()); +#ifdef TRACY_ENABLE + auto& queue = static_cast(m_Device.GetCommandQueue(CommandType::Graphics)); + m_TracyZone = std::make_unique( + queue.GetTracyCtx(), + __LINE__, + __FILE__, + sizeof(__FILE__) - 1, + __FUNCTION__, + std::strlen(__FUNCTION__), + m_Name.data(), + m_Name.size(), + command_list.Get(), + true); +#endif + m_Pipeline = nullptr; } void DX12GraphicsCommandList::End() { + m_TracyZone.reset(); command_list->Close(); } @@ -261,10 +280,26 @@ void DX12ComputeCommandList::Begin() { command_list->SetDescriptorHeaps(descriptor_heaps.size(), descriptor_heaps.data()); command_list->SetComputeRootSignature(dx12_bindless_utils.GetBindlessRootSignature().Get()); +#ifdef TRACY_ENABLE + auto& queue = static_cast(m_Device.GetCommandQueue(CommandType::Compute)); + m_TracyZone = std::make_unique( + queue.GetTracyCtx(), + __LINE__, + __FILE__, + sizeof(__FILE__) - 1, + __FUNCTION__, + std::strlen(__FUNCTION__), + m_Name.data(), + m_Name.size(), + command_list.Get(), + true); +#endif + m_Pipeline = nullptr; } void DX12ComputeCommandList::End() { + m_TracyZone.reset(); command_list->Close(); } @@ -298,9 +333,24 @@ DX12CopyCommandList::DX12CopyCommandList(DX12Device& device, std::string_view na } void DX12CopyCommandList::Begin() { +#ifdef TRACY_ENABLE + auto& queue = static_cast(m_Device.GetCommandQueue(CommandType::Copy)); + m_TracyZone = std::make_unique( + queue.GetTracyCtx(), + __LINE__, + __FILE__, + sizeof(__FILE__) - 1, + __FUNCTION__, + std::strlen(__FUNCTION__), + m_Name.data(), + m_Name.size(), + command_list.Get(), + true); +#endif } void DX12CopyCommandList::End() { + m_TracyZone.reset(); command_list->Close(); } @@ -394,4 +444,4 @@ void DX12CopyCommandList::CopyTextureRegion(const Texture& src, copy_texture_region(command_list, src, src_offset, dst, dst_offset, extent, src_layer, dst_layer); } -} // namespace hitagi::gfx \ No newline at end of file +} // namespace hitagi::gfx diff --git a/hitagi/gfx/dx12/dx12_command_list.cppm b/hitagi/gfx/dx12/dx12_command_list.cppm index 12ee3698..452395cf 100644 --- a/hitagi/gfx/dx12/dx12_command_list.cppm +++ b/hitagi/gfx/dx12/dx12_command_list.cppm @@ -1,5 +1,6 @@ module; #include +#include #include export module gfx.dx12:command_list; @@ -60,6 +61,7 @@ public: ComPtr command_list; ComPtr command_allocator; const RenderPipeline* m_Pipeline = nullptr; + std::unique_ptr m_TracyZone; }; class DX12ComputeCommandList : public ComputeCommandContext { @@ -79,6 +81,7 @@ public: ComPtr command_list; ComPtr command_allocator; const ComputePipeline* m_Pipeline = nullptr; + std::unique_ptr m_TracyZone; }; class DX12CopyCommandList : public CopyCommandContext { @@ -120,6 +123,7 @@ public: ComPtr command_list; ComPtr command_allocator; + std::unique_ptr m_TracyZone; }; -} // namespace hitagi::gfx \ No newline at end of file +} // namespace hitagi::gfx diff --git a/hitagi/gfx/dx12/dx12_command_queue.cpp b/hitagi/gfx/dx12/dx12_command_queue.cpp index 581bbe58..3296e43a 100644 --- a/hitagi/gfx/dx12/dx12_command_queue.cpp +++ b/hitagi/gfx/dx12/dx12_command_queue.cpp @@ -3,6 +3,7 @@ module; #include #include #include +#include module gfx.dx12; import std; @@ -31,6 +32,12 @@ DX12CommandQueue::DX12CommandQueue(DX12Device& device, CommandType type, std::st throw std::runtime_error(error_message); } m_Queue->SetName(std::wstring(name.begin(), name.end()).c_str()); + m_TracyCtx = TracyD3D12Context(device.GetDevice().Get(), m_Queue.Get()); + TracyD3D12ContextName(m_TracyCtx, m_Name.data(), static_cast(m_Name.size())); +} + +DX12CommandQueue::~DX12CommandQueue() { + TracyD3D12Destroy(m_TracyCtx); } void DX12CommandQueue::Submit(std::span> contexts, @@ -73,6 +80,7 @@ void DX12CommandQueue::Submit(std::spanExecuteCommandLists(command_lists.size(), command_lists.data()); + TracyD3D12Collect(m_TracyCtx); } for (const auto& signal_fence : signal_fences) { @@ -87,4 +95,8 @@ void DX12CommandQueue::WaitIdle() { m_Fence.Wait(m_SubmitCount); } -} // namespace hitagi::gfx \ No newline at end of file +void DX12CommandQueue::NewFrame() { + TracyD3D12NewFrame(m_TracyCtx); +} + +} // namespace hitagi::gfx diff --git a/hitagi/gfx/dx12/dx12_command_queue.cppm b/hitagi/gfx/dx12/dx12_command_queue.cppm index 1ba6ba00..ce9b9a3f 100644 --- a/hitagi/gfx/dx12/dx12_command_queue.cppm +++ b/hitagi/gfx/dx12/dx12_command_queue.cppm @@ -1,5 +1,6 @@ module; #include +#include #include export module gfx.dx12:command_queue; @@ -16,6 +17,7 @@ class DX12Device; class DX12CommandQueue : public CommandQueue { public: DX12CommandQueue(DX12Device& device, CommandType type, std::string_view name); + ~DX12CommandQueue() override; void Submit( std::span> contexts, @@ -23,12 +25,15 @@ public: std::span signal_fences = {}) final; void WaitIdle() final; + void NewFrame(); inline auto GetDX12Queue() const noexcept { return m_Queue; } + inline auto GetTracyCtx() const noexcept { return m_TracyCtx; } private: ComPtr m_Queue; DX12Fence m_Fence; std::uint64_t m_SubmitCount = 0; + TracyD3D12Ctx m_TracyCtx = nullptr; }; -} // namespace hitagi::gfx \ No newline at end of file +} // namespace hitagi::gfx diff --git a/hitagi/gfx/dx12/dx12_device.cpp b/hitagi/gfx/dx12/dx12_device.cpp index cd3e6e13..7f2c9d62 100644 --- a/hitagi/gfx/dx12/dx12_device.cpp +++ b/hitagi/gfx/dx12/dx12_device.cpp @@ -203,13 +203,18 @@ DX12Device::~DX12Device() { void DX12Device::Tick() { Device::Tick(); +#ifdef TRACY_ENABLE + for (const auto& queue : m_CommandQueues) { + queue->NewFrame(); + } +#endif if (m_EnableProfile) { Profile(); } } void DX12Device::WaitIdle() { - ZoneScopedN("DX12Device::WaitIdle"); + ZoneScopedNS("DX12Device::WaitIdle", 8); for (auto& queue : m_CommandQueues) { queue->WaitIdle(); } @@ -331,4 +336,4 @@ void DX12Device::UnregisterIntegratedD3D12Logger() { } } -} // namespace hitagi::gfx \ No newline at end of file +} // namespace hitagi::gfx diff --git a/hitagi/gfx/dx12/dx12_sync.cpp b/hitagi/gfx/dx12/dx12_sync.cpp index a2501d83..a58deb7b 100644 --- a/hitagi/gfx/dx12/dx12_sync.cpp +++ b/hitagi/gfx/dx12/dx12_sync.cpp @@ -3,6 +3,7 @@ module; #include #include #include +#include module gfx.dx12; import :sync; @@ -45,7 +46,7 @@ void DX12Fence::Signal(std::uint64_t value) { } bool DX12Fence::Wait(std::uint64_t value, std::chrono::milliseconds timeout) { - auto dx12_device = static_cast(m_Device).GetDevice(); + ZoneScopedNS("DX12Fence::Wait", 8); if (m_Fence->GetCompletedValue() < value) { m_Fence->SetEventOnCompletion(value, m_EventHandle); return WAIT_TIMEOUT != WaitForSingleObject(m_EventHandle, timeout.count()); @@ -58,4 +59,4 @@ auto DX12Fence::GetCurrentValue() -> std::uint64_t { return output; } -} // namespace hitagi::gfx \ No newline at end of file +} // namespace hitagi::gfx diff --git a/hitagi/gfx/gfx.cpp b/hitagi/gfx/gfx.cpp index e6e4d6a4..cebb0176 100644 --- a/hitagi/gfx/gfx.cpp +++ b/hitagi/gfx/gfx.cpp @@ -48,6 +48,20 @@ auto readback_texture(Device& device, Texture& texture, TextureSubresourceLayer device.WaitIdle(); + if (texture.GetCurrentLayout() != TextureLayout::Common && + texture.GetCurrentLayout() != TextureLayout::Unkown) { + auto gfx_ctx = device.CreateGraphicsContext("readback_transition"); + gfx_ctx->Begin(); + gfx_ctx->ResourceBarrier( + {}, {}, + {{texture.Transition(BarrierAccess::None, TextureLayout::Common, PipelineStage::None)}}); + gfx_ctx->End(); + + auto& gfx_queue = device.GetCommandQueue(CommandType::Graphics); + gfx_queue.Submit({{*gfx_ctx}}); + gfx_queue.WaitIdle(); + } + auto ctx = device.CreateCopyContext("readback_copy"); ctx->Begin(); ctx->ResourceBarrier( diff --git a/hitagi/gfx/render_graph/render_graph.cpp b/hitagi/gfx/render_graph/render_graph.cpp index 37059e22..307e7da4 100644 --- a/hitagi/gfx/render_graph/render_graph.cpp +++ b/hitagi/gfx/render_graph/render_graph.cpp @@ -324,7 +324,8 @@ bool RenderGraph::Compile() { for (auto node : start_nodes) { num_visited_nodes++; for (auto output_node : node->m_OutputNodes) { - if (--in_degrees.at(output_node) == 0) { + auto it = in_degrees.find(output_node); + if (it != in_degrees.end() && --it->second == 0) { new_start_nodes.emplace_back(output_node); } } @@ -334,6 +335,8 @@ bool RenderGraph::Compile() { if (num_visited_nodes != essential_nodes.size()) { m_Logger->error("RenderGraph has cycle"); + const auto message = std::format("{} has cycle", m_Name); + TracyMessageCS(message.data(), message.size(), tracy::Color::Red3, 8); m_ExecuteLayers.clear(); return false; } @@ -353,6 +356,7 @@ auto RenderGraph::Execute() -> std::uint64_t { if (!m_Compiled) { m_Logger->warn("RenderGraph has not been compiled"); + TracyMessageLCS("RenderGraph execute skipped because it is not compiled", tracy::Color::OrangeRed3, 8); return m_FrameIndex; } @@ -399,6 +403,7 @@ auto RenderGraph::Execute() -> std::uint64_t { } RetireNodes(); + Profile(); Reset(); return m_FrameIndex++; } @@ -643,9 +648,15 @@ void RenderGraph::Profile() const noexcept { static bool configured = false; if (!configured) { TracyPlotConfig("Retired Resource Counts", tracy::PlotFormatType::Number, true, true, 0); + TracyPlotConfig("Transient Buffer Count", tracy::PlotFormatType::Number, true, true, 0); + TracyPlotConfig("Transient Texture Count", tracy::PlotFormatType::Number, true, true, 0); + TracyPlotConfig("RenderGraph Execute Layers", tracy::PlotFormatType::Number, true, true, 0); configured = true; } TracyPlot("Retired Resource Counts", static_cast(m_RetiredNodes.size())); + TracyPlot("Transient Buffer Count", static_cast(m_TransientPool.buffers.size())); + TracyPlot("Transient Texture Count", static_cast(m_TransientPool.textures.size())); + TracyPlot("RenderGraph Execute Layers", static_cast(m_ExecuteLayers.size())); } -} // namespace hitagi::rg \ No newline at end of file +} // namespace hitagi::rg diff --git a/hitagi/gfx/vulkan/vk_bindless.cpp b/hitagi/gfx/vulkan/vk_bindless.cpp index 24fa459a..f11a5d82 100644 --- a/hitagi/gfx/vulkan/vk_bindless.cpp +++ b/hitagi/gfx/vulkan/vk_bindless.cpp @@ -10,13 +10,7 @@ import std; namespace hitagi::gfx { VulkanBindlessUtils::VulkanBindlessUtils(VulkanDevice& device, std::string_view name) - : BindlessUtils(device, name), - m_BindlessHandlePools{{ - {{}, std::mutex{}}, - {{}, std::mutex{}}, - {{}, std::mutex{}}, - {{}, std::mutex{}}, - }} { + : BindlessUtils(device, name) { const auto logger = device.GetLogger(); const auto limits = device.GetPhysicalDevice().getProperties().limits; @@ -193,7 +187,6 @@ auto VulkanBindlessUtils::CreateBindlessHandle(GPUBuffer& buffer, std::uint64_t vk_device.GetDevice().updateDescriptorSets(write_info, {}); - TracyAllocN((void*)static_cast(handle.index), 1, "GPUBuffer_bindless"); return handle; } @@ -251,12 +244,6 @@ auto VulkanBindlessUtils::CreateBindlessHandle(Texture& texture, bool writable) vk_device.GetDevice().updateDescriptorSets(write_info, {}); - if (writable) { - TracyAllocN((void*)static_cast(handle.index), 1, "StorageTexture_bindless"); - } else { - TracyAllocN((void*)static_cast(handle.index), 1, "SampledTexture_bindless"); - } - return handle; } @@ -287,8 +274,6 @@ auto VulkanBindlessUtils::CreateBindlessHandle(Sampler& sampler) -> BindlessHand vk_device.GetDevice().updateDescriptorSets(write_info, {}); - TracyAllocN((void*)static_cast(handle.index), 1, "Sampler_bindless"); - return handle; } @@ -299,17 +284,13 @@ void VulkanBindlessUtils::DiscardBindlessHandle(BindlessHandle handle) { if (handle.type == BindlessHandleType::Buffer) { pool_index = 0; - TracyFreeN((void*)static_cast(handle.index), "GPUBuffer_bindless"); } else if (handle.type == BindlessHandleType::Texture) { if (handle.writable) { pool_index = 2; - TracyFreeN((void*)static_cast(handle.index), "StorageTexture_bindless"); } else { pool_index = 1; - TracyFreeN((void*)static_cast(handle.index), "SampledTexture_bindless"); } } else if (handle.type == BindlessHandleType::Sampler) { - TracyFreeN((void*)static_cast(handle.index), "Sampler_bindless"); pool_index = 3; } else { return; @@ -323,4 +304,4 @@ void VulkanBindlessUtils::DiscardBindlessHandle(BindlessHandle handle) { pool.emplace_back(handle); } -} // namespace hitagi::gfx \ No newline at end of file +} // namespace hitagi::gfx diff --git a/hitagi/gfx/vulkan/vk_bindless.cppm b/hitagi/gfx/vulkan/vk_bindless.cppm index 4127a49b..e69af104 100644 --- a/hitagi/gfx/vulkan/vk_bindless.cppm +++ b/hitagi/gfx/vulkan/vk_bindless.cppm @@ -1,5 +1,6 @@ module; +#include #include export module gfx.vulkan:bindless; @@ -27,8 +28,8 @@ struct VulkanBindlessUtils : public BindlessUtils { private: struct BindlessHandlePool { std::pmr::vector pool; - std::mutex mutex{}; + TracyLockableN(std::mutex, mutex, "Vulkan Bindless Pool Mutex"); }; std::array m_BindlessHandlePools{}; }; -} // namespace hitagi::gfx \ No newline at end of file +} // namespace hitagi::gfx diff --git a/hitagi/gfx/vulkan/vk_command_buffer.cpp b/hitagi/gfx/vulkan/vk_command_buffer.cpp index 364446d6..685a7990 100644 --- a/hitagi/gfx/vulkan/vk_command_buffer.cpp +++ b/hitagi/gfx/vulkan/vk_command_buffer.cpp @@ -1,11 +1,14 @@ module; +#include #include #include #include +#include module gfx.vulkan; import :command_buffer; +import :command_queue; import std; import :resource; @@ -117,9 +120,26 @@ void VulkanGraphicsCommandBuffer::Begin() { 0, descriptor_sets, {}); + +#ifdef TRACY_ENABLE + auto& queue = static_cast(m_Device.GetCommandQueue(CommandType::Graphics)); + m_TracyZone = std::make_unique( + queue.GetTracyCtx(), + __LINE__, + __FILE__, + sizeof(__FILE__) - 1, + __FUNCTION__, + std::strlen(__FUNCTION__), + m_Name.data(), + m_Name.size(), + *command_buffer, + true); +#endif } void VulkanGraphicsCommandBuffer::End() { + m_TracyZone.reset(); + TracyVkCollect(static_cast(m_Device.GetCommandQueue(CommandType::Graphics)).GetTracyCtx(), *command_buffer); command_buffer.end(); } @@ -334,9 +354,26 @@ void VulkanComputeCommandBuffer::Begin() { 0, descriptor_sets, {}); + +#ifdef TRACY_ENABLE + auto& queue = static_cast(m_Device.GetCommandQueue(CommandType::Compute)); + m_TracyZone = std::make_unique( + queue.GetTracyCtx(), + __LINE__, + __FILE__, + sizeof(__FILE__) - 1, + __FUNCTION__, + std::strlen(__FUNCTION__), + m_Name.data(), + m_Name.size(), + *command_buffer, + true); +#endif } void VulkanComputeCommandBuffer::End() { + m_TracyZone.reset(); + TracyVkCollect(static_cast(m_Device.GetCommandQueue(CommandType::Compute)).GetTracyCtx(), *command_buffer); command_buffer.end(); } @@ -374,9 +411,26 @@ void VulkanTransferCommandBuffer::Begin() { command_buffer.begin(vk::CommandBufferBeginInfo{ .flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit, }); + +#ifdef TRACY_ENABLE + auto& queue = static_cast(m_Device.GetCommandQueue(CommandType::Copy)); + m_TracyZone = std::make_unique( + queue.GetTracyCtx(), + __LINE__, + __FILE__, + sizeof(__FILE__) - 1, + __FUNCTION__, + std::strlen(__FUNCTION__), + m_Name.data(), + m_Name.size(), + *command_buffer, + true); +#endif } void VulkanTransferCommandBuffer::End() { + m_TracyZone.reset(); + TracyVkCollect(static_cast(m_Device.GetCommandQueue(CommandType::Copy)).GetTracyCtx(), *command_buffer); command_buffer.end(); } @@ -470,4 +524,4 @@ void VulkanTransferCommandBuffer::CopyTextureRegion(const Texture& src, copy_texture_region(command_buffer, src, src_offset, dst, dst_offset, extent, src_layer, dst_layer); } -} // namespace hitagi::gfx \ No newline at end of file +} // namespace hitagi::gfx diff --git a/hitagi/gfx/vulkan/vk_command_buffer.cppm b/hitagi/gfx/vulkan/vk_command_buffer.cppm index 12789e44..02d87949 100644 --- a/hitagi/gfx/vulkan/vk_command_buffer.cppm +++ b/hitagi/gfx/vulkan/vk_command_buffer.cppm @@ -1,6 +1,7 @@ module; #include +#include export module gfx.vulkan:command_buffer; import gfx.base; @@ -68,6 +69,7 @@ public: private: const VulkanRenderPipeline* m_Pipeline = nullptr; + std::unique_ptr m_TracyZone; }; class VulkanComputeCommandBuffer final : public ComputeCommandContext { @@ -90,6 +92,7 @@ public: private: const VulkanComputePipeline* m_Pipeline = nullptr; + std::unique_ptr m_TracyZone; }; class VulkanTransferCommandBuffer final : public CopyCommandContext { @@ -135,6 +138,7 @@ public: std::shared_ptr swap_chain_image_available_semaphore; // get swapchain semaphore whose image whose layout transition to present std::pmr::vector> swap_chain_presentable_semaphores; + std::unique_ptr m_TracyZone; }; -} // namespace hitagi::gfx \ No newline at end of file +} // namespace hitagi::gfx diff --git a/hitagi/gfx/vulkan/vk_command_queue.cpp b/hitagi/gfx/vulkan/vk_command_queue.cpp index 87467240..e90675b5 100644 --- a/hitagi/gfx/vulkan/vk_command_queue.cpp +++ b/hitagi/gfx/vulkan/vk_command_queue.cpp @@ -3,6 +3,7 @@ module; #include #include #include +#include module gfx.vulkan; import :command_queue; @@ -19,6 +20,24 @@ VulkanCommandQueue::VulkanCommandQueue(VulkanDevice& device, CommandType type, s create_vk_debug_object_info(m_Queue, m_Name, device.GetDevice()); } +VulkanCommandQueue::~VulkanCommandQueue() { + TracyVkDestroy(m_TracyCtx); +} + +void VulkanCommandQueue::InitializeTracyContext() { + auto& device = static_cast(m_Device); + auto setup_command_buffer = std::move(vk::raii::CommandBuffers( + device.GetDevice(), + vk::CommandBufferAllocateInfo{ + .commandPool = *device.GetCommandPool(m_Type), + .level = vk::CommandBufferLevel::ePrimary, + .commandBufferCount = 1, + }).front()); + + m_TracyCtx = TracyVkContext(*device.GetPhysicalDevice(), *device.GetDevice(), *m_Queue, *setup_command_buffer); + TracyVkContextName(m_TracyCtx, m_Name.data(), static_cast(m_Name.size())); +} + void VulkanCommandQueue::Submit(std::span> contexts, std::span wait_fences, std::span signal_fences) { @@ -132,4 +151,4 @@ void VulkanCommandQueue::WaitIdle() { m_Queue.waitIdle(); } -} // namespace hitagi::gfx \ No newline at end of file +} // namespace hitagi::gfx diff --git a/hitagi/gfx/vulkan/vk_command_queue.cppm b/hitagi/gfx/vulkan/vk_command_queue.cppm index e13dacc7..f450c72c 100644 --- a/hitagi/gfx/vulkan/vk_command_queue.cppm +++ b/hitagi/gfx/vulkan/vk_command_queue.cppm @@ -1,6 +1,7 @@ module; #include +#include export module gfx.vulkan:command_queue; import gfx.base; import :resource; @@ -12,7 +13,7 @@ class VulkanDevice; class VulkanCommandQueue final : public CommandQueue { public: VulkanCommandQueue(VulkanDevice& device, CommandType type, std::string_view name, std::uint32_t queue_family_index); - ~VulkanCommandQueue() final = default; + ~VulkanCommandQueue() final; void Submit( std::span> commands, @@ -20,13 +21,16 @@ public: std::span signal_fences = {}) final; void WaitIdle() final; + void InitializeTracyContext(); inline auto GetFamilyIndex() const noexcept { return m_FamilyIndex; } inline auto& GetVkQueue() const noexcept { return m_Queue; } + inline auto GetTracyCtx() const noexcept { return m_TracyCtx; } private: std::uint32_t m_FamilyIndex; vk::raii::Queue m_Queue; + TracyVkCtx m_TracyCtx = nullptr; }; } // namespace hitagi::gfx diff --git a/hitagi/gfx/vulkan/vk_device.cpp b/hitagi/gfx/vulkan/vk_device.cpp index 859404f2..7184b84d 100644 --- a/hitagi/gfx/vulkan/vk_device.cpp +++ b/hitagi/gfx/vulkan/vk_device.cpp @@ -159,6 +159,10 @@ VulkanDevice::VulkanDevice(std::string_view name) command_pool_create_info, GetCustomAllocator()); }); + + for (const auto& queue : m_CommandQueues) { + queue->InitializeTracyContext(); + } } m_Logger->trace("Create Bindless..."); @@ -180,6 +184,7 @@ void VulkanDevice::Tick() { } void VulkanDevice::WaitIdle() { + ZoneScopedNS("VulkanDevice::WaitIdle", 8); m_Device->waitIdle(); } @@ -251,4 +256,4 @@ void VulkanDevice::Profile() const { TracyPlot("GPU Memory", static_cast(statics.total.statistics.allocationBytes)); } -} // namespace hitagi::gfx \ No newline at end of file +} // namespace hitagi::gfx diff --git a/hitagi/gfx/vulkan/vk_sync.cpp b/hitagi/gfx/vulkan/vk_sync.cpp index e6da2734..d2fea346 100644 --- a/hitagi/gfx/vulkan/vk_sync.cpp +++ b/hitagi/gfx/vulkan/vk_sync.cpp @@ -1,4 +1,5 @@ module; +#include #include module gfx.vulkan; @@ -36,6 +37,7 @@ void VulkanTimelineSemaphore::Signal(std::uint64_t value) { } bool VulkanTimelineSemaphore::Wait(std::uint64_t value, std::chrono::milliseconds timeout) { + ZoneScopedNS("VulkanTimelineSemaphore::Wait", 8); return static_cast(m_Device).GetDevice().waitSemaphores( vk::SemaphoreWaitInfo{ .semaphoreCount = 1, @@ -49,4 +51,4 @@ auto VulkanTimelineSemaphore::GetCurrentValue() -> std::uint64_t { return timeline_semaphore.getCounterValue(); } -} // namespace hitagi::gfx \ No newline at end of file +} // namespace hitagi::gfx diff --git a/hitagi/render/forward_renderer.cpp b/hitagi/render/forward_renderer.cpp index 5944f28e..e92a263f 100644 --- a/hitagi/render/forward_renderer.cpp +++ b/hitagi/render/forward_renderer.cpp @@ -2,7 +2,7 @@ module; #include #include -#include +#include #include #undef near @@ -32,6 +32,8 @@ ForwardRenderer::ForwardRenderer(gfx::Device& device, const Application& app, gu } void ForwardRenderer::Tick() { + ZoneScopedN("RenderFrame"); + if (m_App.WindowSizeChanged()) { m_SwapChain->Resize(); } @@ -49,6 +51,13 @@ void ForwardRenderer::Tick() { m_SwapChain->Present(); m_Clock.Tick(); + + static bool tracy_plot_configured = false; + if (!tracy_plot_configured) { + TracyPlotConfig("Render Frame Time (ms)", tracy::PlotFormatType::Number, false, true, 0); + tracy_plot_configured = true; + } + TracyPlot("Render Frame Time (ms)", m_Clock.DeltaTime().count() * 1000.0); } void ForwardRenderer::RenderScene(std::shared_ptr scene, const asset::Camera& camera, math::mat4f camera_transform, rg::TextureHandle target) { @@ -185,14 +194,20 @@ void ForwardRenderer::RenderScene(std::shared_ptr scene, const ass }; for (auto [texture_bindless, texture_handle] : ranges::views::zip(bindless_infos[draw_index].textures, material_instance_info.textures)) { - texture_bindless = pass.GetBindless(texture_handle); + if (texture_handle) { + texture_bindless = pass.GetBindless(texture_handle); + } } cmd.PushBindlessMetaInfo({ .handle = pass.GetBindless(m_BindlessInfoConstantBuffer, draw_index), }); for (const auto& vertex_attr : pipeline.GetDesc().vertex_input_layout) { - cmd.SetVertexBuffers(vertex_attr.binding, {{pass.Resolve(mesh_info.vertices.at(vertex_attr.binding))}}, {{0}}); + auto mesh_attr = asset::semantic_to_vertex_attribute(vertex_attr.semantic); + auto attr_handle = mesh_info.vertices[mesh_attr]; + if (attr_handle) { + cmd.SetVertexBuffers(vertex_attr.binding, {{pass.Resolve(attr_handle)}}, {{0}}); + } } cmd.SetIndexBuffer(pass.Resolve(mesh_info.indices), 0); @@ -217,6 +232,11 @@ void ForwardRenderer::ToSwapChain(rg::TextureHandle from) { } void ForwardRenderer::RecordMaterialInstance(rg::RenderPassBuilder& builder, const std::shared_ptr& material_instance) { + if (!material_instance) return; + if (!material_instance->GetMaterial()) { + spdlog::warn("Material instance '{}' has no material assigned, skipping", material_instance->GetName()); + return; + } if (m_MaterialInstanceInfos.contains(material_instance.get())) return; MaterialInstanceInfo info{ @@ -224,12 +244,24 @@ void ForwardRenderer::RecordMaterialInstance(rg::RenderPassBuilder& builder, con .samplers = {m_Sampler}, }; - for (const auto& texture : material_instance->GetAssociatedTextures()) { - texture->InitGPUData(m_GfxDevice); - builder.Read( - info.textures.emplace_back(m_RenderGraph.Import(texture->GetGPUData(), texture->GetUniqueName())), - {}, - gfx::PipelineStage::PixelShader); + auto associated = material_instance->GetAssociatedTextures(); + if (associated.empty() && material_instance->GetMaterial()) { + spdlog::info(" '{}' (mat='{}') has 0 associated textures", material_instance->GetName(), material_instance->GetMaterial()->GetName()); + } + for (std::size_t ti = 0; ti < associated.size(); ti++) { + const auto& texture = associated[ti]; + if (texture && !texture->Empty()) { + texture->InitGPUData(m_GfxDevice); + builder.Read( + info.textures.emplace_back(m_RenderGraph.Import(texture->GetGPUData(), texture->GetUniqueName())), + {}, + gfx::PipelineStage::PixelShader); + } else { + if (m_InstanceInfos.size() <= 1) { + spdlog::info(" '{}' texture[{}] = {}", material_instance->GetName(), ti, texture ? "empty" : "null"); + } + info.textures.emplace_back(rg::TextureHandle{}); + } } m_MaterialInstanceInfos.emplace(material_instance.get(), std::move(info)); diff --git a/xmake.lua b/xmake.lua index f21c278d..54690959 100644 --- a/xmake.lua +++ b/xmake.lua @@ -32,7 +32,7 @@ option_end() if has_config("profile") then add_defines("TRACY_ENABLE") - add_requireconfs("tracy", {configs = {on_demand = true}}) + -- add_requireconfs("tracy", {configs = {on_demand = true}}) if is_plat("windows") then add_defines("TRACY_IMPORTS") end @@ -61,7 +61,6 @@ add_requires( add_requires("magic_enum", {configs = {modules = true}}) add_requires("tracy v0.12.1") add_requires("assimp", {configs = {cxflags = "/EHsc"}}) -add_requires("usd", {configs = {toolchains = "msvc"}}) add_requires("spdlog", {configs = {fmt_external = true}}) add_requires("imgui v1.92.1-docking", {configs = {freetype = true, wchar32 = true}}) add_requires("d3d12-memory-allocator", "directx12-agility-sdk", {optional = true})