diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index 66efb6a00f8..388f0b829e1 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -1474,6 +1474,8 @@ cocos_source_files( cocos/renderer/pipeline/custom/details/Utility.h cocos/renderer/pipeline/profile/GPUTimeQuery.cpp cocos/renderer/pipeline/profile/GPUTimeQuery.h + cocos/renderer/pipeline/profile/GPUStatisticsQuery.cpp + cocos/renderer/pipeline/profile/GPUStatisticsQuery.h cocos/renderer/pipeline/profile/PipelineProfiler.cpp cocos/renderer/pipeline/profile/PipelineProfiler.h ) @@ -3354,7 +3356,7 @@ set(COCOS_SOURCE_LIST_EXCLUDE_GENRATED ${COCOS_SOURCE_LIST}) set(COCOS_GENERATED_LIST) foreach(src IN LISTS COCOS_SOURCE_LIST_EXCLUDE_GENRATED) get_source_file_property(IS_GENERATED ${src} GENERATED) - if(IS_GENERATED) + if(IS_GENERATED) list(REMOVE_ITEM COCOS_SOURCE_LIST_EXCLUDE_GENRATED ${src}) list(APPEND COCOS_GENERATED_LIST ${src}) endif() diff --git a/native/cocos/renderer/gfx-vulkan/VKGPUContext.cpp b/native/cocos/renderer/gfx-vulkan/VKGPUContext.cpp index 3a3a114aeae..66a610c696d 100644 --- a/native/cocos/renderer/gfx-vulkan/VKGPUContext.cpp +++ b/native/cocos/renderer/gfx-vulkan/VKGPUContext.cpp @@ -33,7 +33,7 @@ namespace { constexpr uint32_t FORCE_MINOR_VERSION = 0; // 0 for default version, otherwise minorVersion = (FORCE_MINOR_VERSION - 1) -#define FORCE_ENABLE_VALIDATION 0 +#define FORCE_ENABLE_VALIDATION 1 #define FORCE_DISABLE_VALIDATION 1 using ccstd::vector; diff --git a/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp b/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp index 470d9726c11..bd54256da9a 100644 --- a/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp @@ -52,6 +52,8 @@ #include "details/GslUtils.h" #include "details/Range.h" +#define CC_PIPELINE_PROFILER 1 + namespace cc { namespace render { @@ -209,17 +211,6 @@ PersistentRenderPassAndFramebuffer& fetchOrCreateFramebuffer( return iter->second; } -std::string getPassName(const RenderGraph& g, RenderGraph::vertex_descriptor v) noexcept { - std::stringstream ss; - const auto &name = get(RenderGraph::NameTag{}, g, v); - if (name.empty()) { - ss << "GraphPass_" << v; - return ss.str(); - } - - return name.c_str(); -} - struct RenderGraphFilter { bool operator()(RenderGraph::vertex_descriptor u) const { return validPasses->operator[](u); @@ -1140,8 +1131,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { #if CC_DEBUG ctx.cmdBuff->beginMarker(makeMarkerInfo(get(RenderGraph::NameTag{}, ctx.g, vertID).c_str(), RASTER_COLOR)); #endif - ctx.context.pipelineProfiler.writeGpuTimeStamp(ctx.cmdBuff, vertID); - +#if CC_PIPELINE_PROFILER + ctx.context.pipelineProfiler.beginScope(ctx.cmdBuff, vertID); +#endif const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID); if (!renderData.custom.empty()) { const auto& passes = ctx.ppl->custom.renderPasses; @@ -1182,8 +1174,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { #if CC_DEBUG ctx.cmdBuff->insertMarker(makeMarkerInfo(get(RenderGraph::NameTag{}, ctx.g, vertID).c_str(), RASTER_COLOR)); #endif - ctx.context.pipelineProfiler.writeGpuTimeStamp(ctx.cmdBuff, vertID); - +#if CC_PIPELINE_PROFILER + ctx.context.pipelineProfiler.beginScope(ctx.cmdBuff, vertID); +#endif const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID); if (!renderData.custom.empty()) { const auto& subpasses = ctx.ppl->custom.renderSubpasses; @@ -1207,8 +1200,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { #if CC_DEBUG ctx.cmdBuff->insertMarker(makeMarkerInfo(get(RenderGraph::NameTag{}, ctx.g, vertID).c_str(), RASTER_COLOR)); #endif - ctx.context.pipelineProfiler.writeGpuTimeStamp(ctx.cmdBuff, vertID); - +#if CC_PIPELINE_PROFILER + ctx.context.pipelineProfiler.beginScope(ctx.cmdBuff, vertID); +#endif const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID); if (!renderData.custom.empty()) { const auto& subpasses = ctx.ppl->custom.computeSubpasses; @@ -1227,8 +1221,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { #if CC_DEBUG ctx.cmdBuff->beginMarker(makeMarkerInfo(get(RenderGraph::NameTag{}, ctx.g, vertID).c_str(), COMPUTE_COLOR)); #endif - ctx.context.pipelineProfiler.writeGpuTimeStamp(ctx.cmdBuff, vertID); - +#if CC_PIPELINE_PROFILER + ctx.context.pipelineProfiler.beginScope(ctx.cmdBuff, vertID); +#endif const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID); if (!renderData.custom.empty()) { const auto& passes = ctx.ppl->custom.computePasses; @@ -1380,8 +1375,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { #if CC_DEBUG ctx.cmdBuff->beginMarker(makeMarkerInfo(get(RenderGraph::NameTag{}, ctx.g, vertID).c_str(), RENDER_QUEUE_COLOR)); #endif - ctx.context.pipelineProfiler.writeGpuTimeStamp(ctx.cmdBuff, vertID); - +#if CC_PIPELINE_PROFILER + ctx.context.pipelineProfiler.beginScope(ctx.cmdBuff, vertID); +#endif const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID); if (!renderData.custom.empty()) { const auto& queues = ctx.ppl->custom.renderQueues; @@ -1421,6 +1417,12 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { } } void begin(const Blit& blit, RenderGraph::vertex_descriptor vertID) const { +#if CC_DEBUG + ctx.cmdBuff->beginMarker(makeMarkerInfo(get(RenderGraph::NameTag{}, ctx.g, vertID).c_str(), RENDER_QUEUE_COLOR)); +#endif +#if CC_PIPELINE_PROFILER + ctx.context.pipelineProfiler.beginScope(ctx.cmdBuff, vertID); +#endif const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID); if (!renderData.custom.empty()) { const auto& commands = ctx.ppl->custom.renderCommands; @@ -1503,8 +1505,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { ctx.cmdBuff->endRenderPass(); ctx.currentPass = nullptr; ctx.currentPassLayoutID = LayoutGraphData::null_vertex(); - - ctx.context.pipelineProfiler.writeGpuTimeStamp(ctx.cmdBuff, vertID); +#if CC_PIPELINE_PROFILER + ctx.context.pipelineProfiler.endScope(ctx.cmdBuff, vertID); +#endif #if CC_DEBUG ctx.cmdBuff->endMarker(); #endif @@ -1524,7 +1527,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { std::ignore = vertID; ctx.subpassIndex = 0; // noop - ctx.context.pipelineProfiler.writeGpuTimeStamp(ctx.cmdBuff, vertID); +#if CC_PIPELINE_PROFILER + ctx.context.pipelineProfiler.endScope(ctx.cmdBuff, vertID); +#endif } void end(const ComputeSubpass& subpass, RenderGraph::vertex_descriptor vertID) const { // NOLINT(readability-convert-member-functions-to-static) const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID); @@ -1540,7 +1545,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { std::ignore = subpass; std::ignore = vertID; // noop - ctx.context.pipelineProfiler.writeGpuTimeStamp(ctx.cmdBuff, vertID); +#if CC_PIPELINE_PROFILER + ctx.context.pipelineProfiler.endScope(ctx.cmdBuff, vertID); +#endif } void end(const ComputePass& pass, RenderGraph::vertex_descriptor vertID) const { const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID); @@ -1552,7 +1559,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { return; } } - ctx.context.pipelineProfiler.writeGpuTimeStamp(ctx.cmdBuff, vertID); +#if CC_PIPELINE_PROFILER + ctx.context.pipelineProfiler.endScope(ctx.cmdBuff, vertID); +#endif #if CC_DEBUG ctx.cmdBuff->endMarker(); #endif @@ -1580,7 +1589,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { return; } } - ctx.context.pipelineProfiler.writeGpuTimeStamp(ctx.cmdBuff, vertID); +#if CC_PIPELINE_PROFILER + ctx.context.pipelineProfiler.endScope(ctx.cmdBuff, vertID); +#endif #if CC_DEBUG ctx.cmdBuff->endMarker(); #endif @@ -1598,6 +1609,12 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { return; } } +#if CC_PIPELINE_PROFILER + ctx.context.pipelineProfiler.endScope(ctx.cmdBuff, vertID); +#endif +#if CC_DEBUG + ctx.cmdBuff->endMarker(); +#endif std::ignore = pass; } void end(const Dispatch& pass, RenderGraph::vertex_descriptor vertID) const { diff --git a/native/cocos/renderer/pipeline/profile/GPUStatisticsQuery.cpp b/native/cocos/renderer/pipeline/profile/GPUStatisticsQuery.cpp new file mode 100644 index 00000000000..dffbf3ffed4 --- /dev/null +++ b/native/cocos/renderer/pipeline/profile/GPUStatisticsQuery.cpp @@ -0,0 +1,144 @@ +/**************************************************************************** + Copyright (c) 2023-2023 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +****************************************************************************/ + +#include "GPUStatisticsQuery.h" +#include "gfx-base/GFXDevice.h" +#include "base/TypeDef.h" + +namespace cc::render { + +static constexpr uint32_t MAX_FRAME_INFLIGHT = 2; // from device agent + +namespace { + +uint32_t getStatsCount(const gfx::PipelineStatisticFlags &flags) { + uint32_t res = 0; + if (hasFlag(flags, gfx::PipelineStatisticFlagBit::IA_VERTICES)) ++res; + if (hasFlag(flags, gfx::PipelineStatisticFlagBit::IA_PRIMITIVES)) ++res; + if (hasFlag(flags, gfx::PipelineStatisticFlagBit::VS_INVOCATIONS)) ++res; + if (hasFlag(flags, gfx::PipelineStatisticFlagBit::CLIP_INVOCATIONS)) ++res; + if (hasFlag(flags, gfx::PipelineStatisticFlagBit::CLIP_PRIMITIVES)) ++res; + if (hasFlag(flags, gfx::PipelineStatisticFlagBit::FS_INVOCATIONS)) ++res; + if (hasFlag(flags, gfx::PipelineStatisticFlagBit::CS_INVOCATIONS)) ++res; + return res; +} + +} // namespace + +const uint64_t* GPUStatisticsQuery::getReadBuffer() const { + const auto* ptr = reinterpret_cast(_results.data()); + const uint64_t readIndex = (_frameIndex + MAX_FRAME_INFLIGHT - 1) % MAX_FRAME_INFLIGHT; + return reinterpret_cast(ptr + readIndex * _dataSize); +} + +GPUPipelineStats GPUStatisticsQuery::resolveData(uint32_t id) const { + GPUPipelineStats stats = {}; + + const uint64_t *ptr = getReadBuffer() + id * _dataCount; + if (hasFlag(flags, gfx::PipelineStatisticFlagBit::IA_VERTICES)) { + stats.iaVertices = *ptr; + ptr++; + } + if (hasFlag(flags, gfx::PipelineStatisticFlagBit::IA_PRIMITIVES)) { + stats.iaPrimitives = *ptr; + ptr++; + } + if (hasFlag(flags, gfx::PipelineStatisticFlagBit::VS_INVOCATIONS)) { + stats.vsInvocations = *ptr; + ptr++; + } + if (hasFlag(flags, gfx::PipelineStatisticFlagBit::CLIP_INVOCATIONS)) { + stats.clipInvocations = *ptr; + ptr++; + } + if (hasFlag(flags, gfx::PipelineStatisticFlagBit::CLIP_PRIMITIVES)) { + stats.clipPrimitives = *ptr; + ptr++; + } + if (hasFlag(flags, gfx::PipelineStatisticFlagBit::FS_INVOCATIONS)) { + stats.fsInvocations = *ptr; + ptr++; + } + if (hasFlag(flags, gfx::PipelineStatisticFlagBit::CS_INVOCATIONS)) { + stats.csInvocations = *ptr; + } + return stats; +} + +void GPUStatisticsQuery::resize(uint32_t size) { + if (size <= _capacity) { + return; + } + + _capacity = size; + + auto *device = gfx::Device::getInstance(); + + device->getSupportedPipelineStatisticFlags(gfx::PipelineStatisticFlagBit::ALL, flags); + _dataCount = getStatsCount(flags); + _dataSize = _capacity * _dataCount * sizeof(uint64_t); + + gfx::QueryPoolInfo poolInfo = {}; + poolInfo.type = gfx::QueryType::PIPELINE_STATISTICS; + poolInfo.maxQueryObjects = _capacity; + poolInfo.pipelineStatisticFlags = flags; + + _queryPool = device->createQueryPool(poolInfo); + + gfx::BufferInfo bufferInfo = {}; + bufferInfo.usage = gfx::BufferUsageBit::TRANSFER_DST; + bufferInfo.memUsage = gfx::MemoryUsageBit::HOST | gfx::MemoryUsageBit::DEVICE; + bufferInfo.stride = _dataCount * sizeof(uint64_t); + bufferInfo.size = _dataSize; + + _results.resize(bufferInfo.size * MAX_FRAME_INFLIGHT); + _readBackBuffer = device->createBuffer(bufferInfo); +} + +void GPUStatisticsQuery::reset(gfx::CommandBuffer *cmdBuffer) { + cmdBuffer->resetQueryPool(_queryPool, 0, _capacity); + _count = 0; + idMap.clear(); +} + +void GPUStatisticsQuery::begin(gfx::CommandBuffer *cmdBuffer, uint32_t key) { + idMap[key] = _count; + cmdBuffer->beginQuery(_queryPool, _count); + ++_count; +} + +void GPUStatisticsQuery::end(gfx::CommandBuffer *cmdBuffer, uint32_t key) { + cmdBuffer->endQuery(_queryPool, idMap[key]); +} + +void GPUStatisticsQuery::copyResult(gfx::CommandBuffer *cmdBuffer) { + cmdBuffer->copyQueryResult(_queryPool, _readBackBuffer, 0, _dataCount * sizeof(uint64_t), 0, _count); + _frameIndex = (_frameIndex + 1) % MAX_FRAME_INFLIGHT; + + auto* ptr = reinterpret_cast(_results.data()); + uint8_t* writeBuffer = ptr + static_cast(_frameIndex) * _dataSize; + _readBackBuffer->readBack(writeBuffer, 0, _dataSize); +} + +} // namespace cc::render diff --git a/native/cocos/renderer/pipeline/profile/GPUStatisticsQuery.h b/native/cocos/renderer/pipeline/profile/GPUStatisticsQuery.h new file mode 100644 index 00000000000..f3f756c2acb --- /dev/null +++ b/native/cocos/renderer/pipeline/profile/GPUStatisticsQuery.h @@ -0,0 +1,84 @@ +/**************************************************************************** + Copyright (c) 2023-2023 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +****************************************************************************/ + +#pragma once + +#include +#include "base/Ptr.h" +#include "base/std/variant.h" +#include "base/std/container/vector.h" +#include "gfx-base/GFXQueryPool.h" +#include "gfx-base/GFXCommandBuffer.h" + +namespace cc::render { + +struct GPUPipelineStats { + uint64_t iaVertices = 0; + uint64_t iaPrimitives = 0; + uint64_t vsInvocations = 0; + uint64_t clipInvocations = 0; + uint64_t clipPrimitives = 0; + uint64_t fsInvocations = 0; + uint64_t csInvocations = 0; +}; + +class GPUStatisticsQuery { +public: + GPUStatisticsQuery() = default; + ~GPUStatisticsQuery() = default; + + void resize(uint32_t size); + + void reset(gfx::CommandBuffer *cmdBuffer); + void begin(gfx::CommandBuffer *cmdBuffer, uint32_t key); + void end(gfx::CommandBuffer *cmdBuffer,uint32_t key); + + void copyResult(gfx::CommandBuffer *cmdBuffer); + + template + void foreachData(Func &&func) const { + for (auto &[key, id] : idMap) { + func(key, resolveData(id)); + } + } + +private: + const uint64_t* getReadBuffer() const; + GPUPipelineStats resolveData(uint32_t id) const; + + uint32_t _capacity = 0; + uint32_t _count = 0; + uint32_t _dataCount = 0; + uint32_t _dataSize = 0; + uint32_t _frameIndex = 0; + + gfx::PipelineStatisticFlags flags = gfx::PipelineStatisticFlagBit::ALL; + + ccstd::unordered_map idMap; + ccstd::vector _results; + IntrusivePtr _queryPool; + IntrusivePtr _readBackBuffer; +}; + +} // namespace cc::render diff --git a/native/cocos/renderer/pipeline/profile/GPUTimeQuery.cpp b/native/cocos/renderer/pipeline/profile/GPUTimeQuery.cpp index 00777e5854a..f293fec04d8 100644 --- a/native/cocos/renderer/pipeline/profile/GPUTimeQuery.cpp +++ b/native/cocos/renderer/pipeline/profile/GPUTimeQuery.cpp @@ -59,7 +59,7 @@ void GPUTimeQuery::resize(uint32_t size) { } void GPUTimeQuery::reset(gfx::CommandBuffer *cmdBuffer) { - cmdBuffer->resetQueryPool(_queryPool, 0, _count); + cmdBuffer->resetQueryPool(_queryPool, 0, _capacity); _count = 0; } diff --git a/native/cocos/renderer/pipeline/profile/PipelineProfiler.cpp b/native/cocos/renderer/pipeline/profile/PipelineProfiler.cpp index 68a8cc6abc5..1615c8a6087 100644 --- a/native/cocos/renderer/pipeline/profile/PipelineProfiler.cpp +++ b/native/cocos/renderer/pipeline/profile/PipelineProfiler.cpp @@ -63,14 +63,34 @@ void PipelineProfiler::beginFrame(uint32_t passCount, gfx::CommandBuffer *cmdBuf _timeQuery.resize(passCount * 2); _timeQuery.reset(cmdBuffer); _passTimes.clear(); + _passStats.clear(); + + _statsQuery.resize(passCount); + _statsQuery.reset(cmdBuffer); } void PipelineProfiler::endFrame(gfx::CommandBuffer *cmdBuffer) { _timeQuery.copyResult(cmdBuffer); + _statsQuery.copyResult(cmdBuffer); + + currentPassId = CC_INVALID_INDEX; +} + +void PipelineProfiler::beginScope(gfx::CommandBuffer *cmdBuffer, uint32_t passID) { + if (currentPassId == CC_INVALID_INDEX) { + _statsQuery.begin(cmdBuffer, passID); + currentPassId = passID; + } + _timeQuery.writeTimestampWithKey(cmdBuffer, passID); } -void PipelineProfiler::writeGpuTimeStamp(gfx::CommandBuffer *cmdBuffer, uint32_t passID) { +void PipelineProfiler::endScope(gfx::CommandBuffer *cmdBuffer, uint32_t passID) { _timeQuery.writeTimestampWithKey(cmdBuffer, passID); + + if (currentPassId == passID) { + _statsQuery.end(cmdBuffer, passID); + currentPassId = CC_INVALID_INDEX; + } } void PipelineProfiler::render(gfx::RenderPass *renderPass, uint32_t subPassId, gfx::CommandBuffer *cmdBuff) { @@ -92,21 +112,38 @@ void PipelineProfiler::resolveData(NativePipeline &pipeline) { stack.pop_back(); } }); + + _statsQuery.foreachData([&](const auto &key, const GPUPipelineStats &v) { + _passStats[key] = v; + }); + #if CC_USE_DEBUG_RENDERER const uint32_t lineHeight = _textRenderer->getLineHeight(); - const DebugTextInfo coreInfo = {{1.0F, 0.0F, 0.0F, 1.0F}, true, false, true, 1U, {0.0F, 0.0F, 0.0F, 1.0F}, 1.5F}; - const DebugTextInfo passInfo = {{1.0F, 1.0F, 1.0F, 1.0F}, true, false, true, 1U, {0.0F, 0.0F, 0.0F, 1.0F}, 1.0F}; + const DebugTextInfo coreInfo = {{1.0F, 0.0F, 0.0F, 1.0F}, true, false, false, 1U, {0.0F, 0.0F, 0.0F, 1.0F}, 1.0F}; + const DebugTextInfo passInfo = {{1.0F, 1.0F, 1.0F, 1.0F}, true, false, false, 1U, {0.0F, 0.0F, 0.0F, 1.0F}, 1.0F}; float baseOffset = 5; uint32_t baseLine = 1; - _textRenderer->addText(StringUtil::format("CoreStats"), {5, static_cast(lineHeight * baseLine++)}, coreInfo); size_t columnOffset = 0; for (auto &[passID, time] : _passTimes) { auto name = get(RenderGraph::NameTag{}, pipeline.renderGraph, passID); columnOffset = std::max(columnOffset, name.length()); } - + float stampOffset = baseOffset + static_cast(columnOffset * 10); + float statsOffset = baseOffset + stampOffset + 100.f; + float statsWidth = 60; + + float yOffset = static_cast(lineHeight * baseLine++); + _textRenderer->addText(StringUtil::format("%s", "Name"), {baseOffset, yOffset}, coreInfo); + _textRenderer->addText(StringUtil::format("%s", "Time(ns)"), {stampOffset, yOffset}, coreInfo); + _textRenderer->addText(StringUtil::format("%s", "IAV"), {statsOffset + statsWidth * 0, yOffset}, coreInfo); + _textRenderer->addText(StringUtil::format("%s", "IAP"), {statsOffset + statsWidth * 1, yOffset}, coreInfo); + _textRenderer->addText(StringUtil::format("%s", "VSI"), {statsOffset + statsWidth * 2, yOffset}, coreInfo); + _textRenderer->addText(StringUtil::format("%s", "CI"), {statsOffset + statsWidth * 3, yOffset}, coreInfo); + _textRenderer->addText(StringUtil::format("%s", "CP"), {statsOffset + statsWidth * 4, yOffset}, coreInfo); + _textRenderer->addText(StringUtil::format("%s", "FSI"), {statsOffset + statsWidth * 5, yOffset}, coreInfo); + _textRenderer->addText(StringUtil::format("%s", "CSI"), {statsOffset + statsWidth * 6, yOffset}, coreInfo); for (auto &[passID, time] : _passTimes) { auto name = get(RenderGraph::NameTag{}, pipeline.renderGraph, passID); @@ -114,14 +151,31 @@ void PipelineProfiler::resolveData(NativePipeline &pipeline) { float levelOffset = 0; visitObject( passID, pipeline.renderGraph, - [&](const RenderQueue &) { + [&](const RenderQueue &queue) { + std::ignore = queue; levelOffset = 8; }, - [&](const auto&) { + [&](const auto&v) { + std::ignore = v; }); _textRenderer->addText(StringUtil::format("%s", name.c_str()), {baseOffset + levelOffset, static_cast(lineHeight * baseLine)}, passInfo); - _textRenderer->addText(StringUtil::format("[%llu]", time), {baseOffset + static_cast(columnOffset * lineHeight), static_cast(lineHeight * baseLine++)}, passInfo); + + yOffset = static_cast(lineHeight * baseLine++); + + _textRenderer->addText(StringUtil::format("[%llu]", time), {stampOffset, yOffset}, passInfo); + + auto iter = _passStats.find(passID); + if (iter != _passStats.end()) { + const auto &stats = iter->second; + _textRenderer->addText(StringUtil::format("%llu", stats.iaVertices), {statsOffset + statsWidth * 0, yOffset}, passInfo); + _textRenderer->addText(StringUtil::format("%llu", stats.iaPrimitives), {statsOffset + statsWidth * 1, yOffset}, passInfo); + _textRenderer->addText(StringUtil::format("%llu", stats.vsInvocations), {statsOffset + statsWidth * 2, yOffset}, passInfo); + _textRenderer->addText(StringUtil::format("%llu", stats.clipInvocations), {statsOffset + statsWidth * 3, yOffset}, passInfo); + _textRenderer->addText(StringUtil::format("%llu", stats.clipPrimitives), {statsOffset + statsWidth * 4, yOffset}, passInfo); + _textRenderer->addText(StringUtil::format("%llu", stats.fsInvocations), {statsOffset + statsWidth * 5, yOffset}, passInfo); + _textRenderer->addText(StringUtil::format("%llu", stats.csInvocations), {statsOffset + statsWidth * 6, yOffset}, passInfo); + } } #endif } diff --git a/native/cocos/renderer/pipeline/profile/PipelineProfiler.h b/native/cocos/renderer/pipeline/profile/PipelineProfiler.h index d338b60ee8e..f9db6fc4ca3 100644 --- a/native/cocos/renderer/pipeline/profile/PipelineProfiler.h +++ b/native/cocos/renderer/pipeline/profile/PipelineProfiler.h @@ -26,6 +26,7 @@ #include "base/std/container/map.h" #include "GPUTimeQuery.h" +#include "GPUStatisticsQuery.h" #include "profiler/DebugRenderer.h" #include "core/assets/Material.h" #include "scene/Pass.h" @@ -42,12 +43,18 @@ class PipelineProfiler { void endFrame(gfx::CommandBuffer *cmdBuffer); void resolveData(NativePipeline &pipeline); - void writeGpuTimeStamp(gfx::CommandBuffer *cmdBuffer, uint32_t passID); + void beginScope(gfx::CommandBuffer *cmdBuffer, uint32_t passID); + void endScope(gfx::CommandBuffer *cmdBuffer, uint32_t passID); + void render(gfx::RenderPass *renderPass, uint32_t subpass, gfx::CommandBuffer *cmdBuff); private: GPUTimeQuery _timeQuery; + GPUStatisticsQuery _statsQuery; + + uint32_t currentPassId = CC_INVALID_INDEX; ccstd::map _passTimes; + ccstd::map _passStats; #if CC_USE_DEBUG_RENDERER std::unique_ptr _textRenderer; IntrusivePtr _material;