diff --git a/cocos/gfx/base/define.ts b/cocos/gfx/base/define.ts index 627c192b7b9..1b9d0580b42 100644 --- a/cocos/gfx/base/define.ts +++ b/cocos/gfx/base/define.ts @@ -702,6 +702,19 @@ export enum PassType { PRESENT, } +export enum PipelineStatisticFlagBit { + NONE = 0, + IA_VERTICES = 0x01, + IA_PRIMITIVES = 0x02, + VS_INVOCATIONS = 0x04, + CLIP_INVOCATIONS = 0x08, + CLIP_PRIMITIVES = 0x10, + FS_INVOCATIONS = 0x20, + CS_INVOCATIONS = 0x40, + ALL = IA_VERTICES | IA_PRIMITIVES | VS_INVOCATIONS | CLIP_INVOCATIONS | CLIP_PRIMITIVES + | FS_INVOCATIONS | CS_INVOCATIONS, +} + export type BufferUsage = BufferUsageBit; export type BufferFlags = BufferFlagBit; export type MemoryAccess = MemoryAccessBit; @@ -713,6 +726,7 @@ export type ShaderStageFlags = ShaderStageFlagBit; export type AccessFlags = AccessFlagBit; export type DynamicStateFlags = DynamicStateFlagBit; export type ClearFlags = ClearFlagBit; +export type PipelineStatisticFlags = PipelineStatisticFlagBit; export class Size { declare private _token: never; // to make sure all usages must be an instance of this exact class, not assembled from plain object @@ -755,6 +769,7 @@ export class DeviceCaps { public maxComputeWorkGroupInvocations: number = 0, public maxComputeWorkGroupSize: Size = new Size(), public maxComputeWorkGroupCount: Size = new Size(), + public timestampPeriod: number = 1, public supportQuery: boolean = false, public clipSpaceMinZ: number = -1, public screenSpaceSignY: number = 1, diff --git a/cocos/gfx/base/device.ts b/cocos/gfx/base/device.ts index ff4f113b59f..e84d4e821ef 100644 --- a/cocos/gfx/base/device.ts +++ b/cocos/gfx/base/device.ts @@ -28,7 +28,7 @@ import { ShaderInfo, InputAssemblerInfo, RenderPassInfo, FramebufferInfo, DescriptorSetLayoutInfo, PipelineLayoutInfo, QueueInfo, BufferTextureCopy, DeviceInfo, DeviceCaps, GeneralBarrierInfo, TextureBarrierInfo, BufferBarrierInfo, SwapchainInfo, BindingMappingInfo, Format, FormatFeature, TextureType, TextureUsageBit, - TextureFlagBit, Offset, Extent, SampleCount, TextureSubresLayers, TextureUsage, TextureFlags, + TextureFlagBit, Offset, Extent, SampleCount, TextureSubresLayers, TextureUsage, TextureFlags, PipelineStatisticFlags, } from './define'; import { Buffer } from './buffer'; import { CommandBuffer } from './command-buffer'; @@ -358,7 +358,9 @@ export abstract class Device { * @zh 是否开启自动GFX内部barrier推导,web无影响。 * @param format The GFX format to be queried. */ - public enableAutoBarrier (en: boolean): void {} + public enableAutoBarrier (en: boolean): void { + // noop + } /** * @en Get maximum supported sample count. @@ -370,6 +372,16 @@ export abstract class Device { public getMaxSampleCount (format: Format, usage: TextureUsage, flags: TextureFlags): SampleCount { return SampleCount.X1; } + + /** + * @en Get supported pipeline statistic flags by device query. + * @zh 获取可通过设备查询获取的管线硬件数据类型 + * @param flags Pipeline statistic flag to be tested. + * @param outFlags Pipeline statistic flag test result. + */ + public getSupportedPipelineStatisticFlags (flags: Readonly, outFlags: PipelineStatisticFlags): number { + return 0; + } } export class DefaultResource { diff --git a/editor/assets/effects/internal/builtin-debug-renderer.effect b/editor/assets/effects/internal/builtin-debug-renderer.effect index d58588debbb..f98756ea59c 100644 --- a/editor/assets/effects/internal/builtin-debug-renderer.effect +++ b/editor/assets/effects/internal/builtin-debug-renderer.effect @@ -2,8 +2,9 @@ CCEffect %{ techniques: - passes: - - vert: debug-renderer-vs:vert - frag: debug-renderer-fs:frag + - vert: debug-renderer-vs + frag: debug-renderer-fs + pass: debug-profiler priority: max depthStencilState: depthTest: false @@ -20,8 +21,13 @@ CCEffect %{ CCProgram debug-renderer-vs %{ precision mediump float; - #include - #include + #pragma rate CCProfiler pass + layout(std140, set = 0, binding = 0) uniform CCProfiler { + mediump vec4 cc_surfaceTransform; // 0: orientation, 1: flip + mediump vec4 cc_screenSize; + }; + + #define CC_HANDLE_GET_CLIP_FLIP(uv) uv = cc_surfaceTransform.y == 0.0 ? vec2(uv.x, -uv.y) : uv in vec2 a_position; in vec2 a_texCoord; @@ -30,7 +36,7 @@ CCProgram debug-renderer-vs %{ out vec2 v_texCoord; out vec4 v_color; - vec4 vert () { + void main () { int orientation = int(cc_surfaceTransform.x); vec4 transform = vec4(1.0, 0.0, 0.0, 1.0); @@ -43,7 +49,6 @@ CCProgram debug-renderer-vs %{ } else if (orientation == 3) { transform = vec4(0.0, -1.0, 1.0, 0.0); } - vec2 invScreenSize = (orientation == 1 || orientation == 3) ? cc_screenSize.wz : cc_screenSize.zw; vec2 position = a_position * invScreenSize; position = position * vec2(2.0, -2.0) + vec2(-1.0, 1.0); @@ -54,21 +59,21 @@ CCProgram debug-renderer-vs %{ v_texCoord = a_texCoord; v_color = a_color; - return vec4(clipPos, 0.0, 1.0); + gl_Position = vec4(clipPos, 0.0, 1.0); } }% CCProgram debug-renderer-fs %{ precision mediump float; - #include in vec2 v_texCoord; in vec4 v_color; uniform sampler2D mainTexture; - vec4 frag () { - vec4 color = vec4(v_color.rgb, v_color.a * texture(mainTexture, v_texCoord).r); - return CCFragOutput(color); + layout(location = 0) out vec4 outColor; + + void main () { + outColor = vec4(v_color.rgb, v_color.a * texture(mainTexture, v_texCoord).r); } }% diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index 91155327302..388f0b829e1 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -1472,6 +1472,12 @@ cocos_source_files( cocos/renderer/pipeline/custom/details/SerializationUtils.h cocos/renderer/pipeline/custom/details/Set.h 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 ) if (USE_GEOMETRY_RENDERER) @@ -3350,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/core/assets/Font.cpp b/native/cocos/core/assets/Font.cpp index 4e7c46ded75..b505cc4fc62 100644 --- a/native/cocos/core/assets/Font.cpp +++ b/native/cocos/core/assets/Font.cpp @@ -75,9 +75,7 @@ FontFace::FontFace(Font *font) } FontFace::~FontFace() { - for (auto *texture : _textures) { - CC_SAFE_DESTROY_AND_DELETE(texture); - } + _textures.clear(); } /** diff --git a/native/cocos/core/assets/Font.h b/native/cocos/core/assets/Font.h index c9c6762c954..4621e7d0611 100644 --- a/native/cocos/core/assets/Font.h +++ b/native/cocos/core/assets/Font.h @@ -25,6 +25,7 @@ #pragma once #include "base/std/container/unordered_map.h" #include "base/std/container/vector.h" +#include "base/Ptr.h" #include "core/assets/Asset.h" namespace cc { @@ -107,7 +108,7 @@ class FontFace { inline Font *getFont() const { return _font; } inline uint32_t getFontSize() const { return _fontSize; } inline uint32_t getLineHeight() const { return _lineHeight; } - inline const ccstd::vector &getTextures() const { return _textures; } + inline const ccstd::vector> &getTextures() const { return _textures; } inline gfx::Texture *getTexture(uint32_t page) const { return _textures[page]; } inline uint32_t getTextureWidth() const { return _textureWidth; } inline uint32_t getTextureHeight() const { return _textureHeight; } @@ -120,7 +121,7 @@ class FontFace { uint32_t _lineHeight{0U}; ccstd::unordered_map _glyphs; ccstd::unordered_map _kernings; - ccstd::vector _textures; + ccstd::vector> _textures; uint32_t _textureWidth{0U}; uint32_t _textureHeight{0U}; }; diff --git a/native/cocos/profiler/DebugRenderer.cpp b/native/cocos/profiler/DebugRenderer.cpp index c4fff16b78d..16b28bde9ca 100644 --- a/native/cocos/profiler/DebugRenderer.cpp +++ b/native/cocos/profiler/DebugRenderer.cpp @@ -1,8 +1,8 @@ /**************************************************************************** Copyright (c) 2021-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 @@ -26,12 +26,14 @@ #include #include "Profiler.h" #include "application/ApplicationManager.h" -#include "base/Log.h" #include "base/UTF8.h" #include "base/memory/Memory.h" +#include "base/std/hash/hash.h" +#include "core/MaterialInstance.h" #include "core/assets/BitmapFont.h" #include "core/assets/FreeTypeFont.h" #include "math/Vec2.h" +#include "scene/Pass.h" #include "platform/interfaces/modules/Device.h" #include "platform/interfaces/modules/ISystemWindow.h" #include "platform/interfaces/modules/ISystemWindowManager.h" @@ -44,7 +46,6 @@ #include "renderer/pipeline/RenderPipeline.h" namespace cc { - constexpr uint32_t DEBUG_FONT_SIZE = 10U; constexpr uint32_t DEBUG_MAX_CHARACTERS = 10000U; constexpr uint32_t DEBUG_VERTICES_PER_CHAR = 6U; @@ -86,15 +87,16 @@ struct DebugVertex { gfx::Color color; }; -struct DebugBatch { - DebugBatch(gfx::Device *device, bool bd, bool it, gfx::Texture *tex) - : bold(bd), italic(it), texture(tex) { - gfx::DescriptorSetLayoutInfo info; - info.bindings.push_back({0, gfx::DescriptorType::SAMPLER_TEXTURE, 1, gfx::ShaderStageFlagBit::FRAGMENT}); +struct DebugBatchUBOData { + Vec4 surfaceTransform; + Vec4 screenSize; +}; - descriptorSetLayout = device->createDescriptorSetLayout(info); - descriptorSet = device->createDescriptorSet({descriptorSetLayout}); +struct DebugBatch { + DebugBatch(gfx::Device *device, bool bd, bool it, gfx::Texture *tex, MaterialInstance *mi) + : bold(bd), italic(it), texture(tex), materialInstance(mi) { + auto &passes = (*mi->getPasses()); auto *sampler = device->getSampler({ gfx::Filter::LINEAR, gfx::Filter::LINEAR, @@ -103,16 +105,14 @@ struct DebugBatch { gfx::Address::CLAMP, gfx::Address::CLAMP, }); - - descriptorSet->bindSampler(0, sampler); - descriptorSet->bindTexture(0, texture); - descriptorSet->update(); + pass = passes[0]; + pass->tryCompile(); + pass->bindTexture(0, tex, 0); + pass->bindSampler(0, sampler, 0); + pass->update(); } - ~DebugBatch() { - CC_SAFE_DESTROY_AND_DELETE(descriptorSet); - CC_SAFE_DESTROY_AND_DELETE(descriptorSetLayout); - } + ~DebugBatch() = default; inline bool match(bool b, bool i, gfx::Texture *tex) const { return bold == b && italic == i && texture == tex; @@ -121,23 +121,23 @@ struct DebugBatch { std::vector vertices; bool bold{false}; bool italic{false}; - gfx::Texture *texture{nullptr}; - gfx::DescriptorSet *descriptorSet{nullptr}; - gfx::DescriptorSetLayout *descriptorSetLayout{nullptr}; + IntrusivePtr texture; + IntrusivePtr materialInstance; + scene::Pass *pass = nullptr; }; class DebugVertexBuffer { public: inline void init(gfx::Device *device, uint32_t maxVertices, const gfx::AttributeList &attributes) { _maxVertices = maxVertices; - _buffer = device->createBuffer({gfx::BufferUsageBit::VERTEX | gfx::BufferUsageBit::TRANSFER_DST, + _vertexBuffer = device->createBuffer({gfx::BufferUsageBit::VERTEX | gfx::BufferUsageBit::TRANSFER_DST, gfx::MemoryUsageBit::DEVICE, static_cast(_maxVertices * sizeof(DebugVertex)), static_cast(sizeof(DebugVertex))}); gfx::InputAssemblerInfo info; info.attributes = attributes; - info.vertexBuffers.push_back(_buffer); + info.vertexBuffers.push_back(_vertexBuffer); _inputAssembler = device->createInputAssembler(info); CC_PROFILE_MEMORY_INC(DebugVertexBuffer, static_cast(_maxVertices * sizeof(DebugVertex))); } @@ -154,7 +154,7 @@ class DebugVertexBuffer { const auto count = std::min(static_cast(vertices.size()), _maxVertices); const auto size = static_cast(count * sizeof(DebugVertex)); - _buffer->update(&vertices[0], size); + _vertexBuffer->update(vertices.data(), size); } inline void destroy() { @@ -162,19 +162,21 @@ class DebugVertexBuffer { CC_SAFE_DELETE(batch); } - CC_SAFE_DESTROY_AND_DELETE(_buffer); - CC_SAFE_DESTROY_AND_DELETE(_inputAssembler); + _vertexBuffer = nullptr; + _inputAssembler = nullptr; CC_PROFILE_MEMORY_DEC(DebugVertexBuffer, static_cast(_maxVertices * sizeof(DebugVertex))); } - DebugBatch &getOrCreateBatch(gfx::Device *device, bool bold, bool italic, gfx::Texture *texture) { + DebugBatch &getOrCreateBatch(gfx::Device *device, bool bold, bool italic, gfx::Texture *texture, Material *material) { for (auto *batch : _batches) { if (batch->match(bold, italic, texture)) { return *batch; } } - auto *batch = ccnew DebugBatch(device, bold, italic, texture); + IMaterialInstanceInfo miInfo = {material}; + + auto *batch = ccnew DebugBatch(device, bold, italic, texture, ccnew MaterialInstance(miInfo)); _batches.push_back(batch); return *batch; @@ -194,64 +196,111 @@ class DebugVertexBuffer { private: uint32_t _maxVertices{0U}; std::vector _batches; - gfx::Buffer *_buffer{nullptr}; - gfx::InputAssembler *_inputAssembler{nullptr}; - - friend class DebugRenderer; + IntrusivePtr _vertexBuffer; + IntrusivePtr _inputAssembler; + friend class TextRenderer; }; DebugRendererInfo::DebugRendererInfo() : fontSize(DEBUG_FONT_SIZE), maxCharacters(DEBUG_MAX_CHARACTERS) { } -DebugRenderer *DebugRenderer::instance = nullptr; -DebugRenderer *DebugRenderer::getInstance() { - return instance; -} +namespace { +void addQuad(DebugBatch &batch, const Vec4 &rect, const Vec4 &uv, gfx::Color color) { + DebugVertex const quad[4] = { + {Vec2(rect.x, rect.y), Vec2(uv.x, uv.y), color}, + {Vec2(rect.x + rect.z, rect.y), Vec2(uv.x + uv.z, uv.y), color}, + {Vec2(rect.x, rect.y + rect.w), Vec2(uv.x, uv.y + uv.w), color}, + {Vec2(rect.x + rect.z, rect.y + rect.w), Vec2(uv.x + uv.z, uv.y + uv.w), color}}; -DebugRenderer::DebugRenderer() { - DebugRenderer::instance = this; + // first triangle + batch.vertices.emplace_back(quad[0]); + batch.vertices.emplace_back(quad[1]); + batch.vertices.emplace_back(quad[2]); + + // second triangle + batch.vertices.emplace_back(quad[1]); + batch.vertices.emplace_back(quad[3]); + batch.vertices.emplace_back(quad[2]); } -DebugRenderer::~DebugRenderer() { - DebugRenderer::instance = nullptr; +const gfx::AttributeList ATTRIBUTES = { + {"a_position", gfx::Format::RG32F}, + {"a_texCoord", gfx::Format::RG32F}, + {"a_color", gfx::Format::RGBA32F} +}; + +} // namespace + +TextRenderer::~TextRenderer() { + CC_SAFE_DESTROY_AND_DELETE(_buffer); + + for (auto &iter : _fonts) { + CC_SAFE_DELETE(iter.font); + } } -void DebugRenderer::activate(gfx::Device *device, const DebugRendererInfo &info) { +void TextRenderer::initialize(gfx::Device *device, const DebugRendererInfo &info, uint32_t fontSize, const std::string &effect) { _device = device; - static const gfx::AttributeList ATTRIBUTES = { - {"a_position", gfx::Format::RG32F}, - {"a_texCoord", gfx::Format::RG32F}, - {"a_color", gfx::Format::RGBA32F}}; - _buffer = ccnew DebugVertexBuffer(); _buffer->init(_device, info.maxCharacters * DEBUG_VERTICES_PER_CHAR, ATTRIBUTES); - const auto *window = CC_GET_MAIN_SYSTEM_WINDOW(); - const auto width = window->getViewSize().width * Device::getDevicePixelRatio(); - auto fontSize = static_cast(width / 800.0F * info.fontSize); - fontSize = fontSize < 10U ? 10U : (fontSize > 20U ? 20U : fontSize); + _ubo = device->createBuffer(gfx::BufferInfo { + gfx::BufferUsageBit::UNIFORM, + gfx::MemoryUsageBit::DEVICE | gfx::MemoryUsageBit::HOST, + sizeof(DebugBatchUBOData), + sizeof(DebugBatchUBOData), + gfx::BufferFlagBit::ENABLE_STAGING_WRITE + }); + + IMaterialInfo matInfo = {}; + matInfo.effectName = effect; + _material = ccnew Material(); + _material->setUuid("default-debug-renderer-material"); + _material->initialize(matInfo); + auto &passes = *_material->getPasses(); + for (auto &pass : passes) { + pass->tryCompile(); + } + + gfx::DescriptorSetLayoutInfo passLayout = {}; + passLayout.bindings.emplace_back(gfx::DescriptorSetLayoutBinding{0, gfx::DescriptorType::UNIFORM_BUFFER, 1, gfx::ShaderStageFlagBit::VERTEX}); + _passLayout = _device->createDescriptorSetLayout(passLayout); + gfx::PipelineLayoutInfo pLayoutInfo = {}; + pLayoutInfo.setLayouts.emplace_back(_passLayout); + pLayoutInfo.setLayouts.emplace_back(passes[0]->getPipelineLayout()->getSetLayouts()[1]); + _pipelineLayout = _device->createPipelineLayout(pLayoutInfo); + + gfx::DescriptorSetInfo setInfo = {}; + setInfo.layout = _passLayout; + _passSet = _device->createDescriptorSet(setInfo); + _passSet->bindBuffer(0, _ubo); + _passSet->update(); + + CC_ASSERT(!passes.empty()); for (auto i = 0U; i < _fonts.size(); i++) { _fonts[i].font = ccnew FreeTypeFont(getFontPath(i)); _fonts[i].face = _fonts[i].font->createFace(FontFaceInfo(fontSize)); - _fonts[i].invTextureSize = {1.0F / _fonts[i].face->getTextureWidth(), 1.0F / _fonts[i].face->getTextureHeight()}; + _fonts[i].invTextureSize = {1.0F / static_cast(_fonts[i].face->getTextureWidth()), + 1.0F / static_cast(_fonts[i].face->getTextureHeight())}; } } -void DebugRenderer::render(gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuff, pipeline::PipelineSceneData *sceneData) { - CC_PROFILE(DebugRendererRender); +void TextRenderer::render(gfx::RenderPass *renderPass, uint32_t subPassId, gfx::CommandBuffer *cmdBuff) { if (!_buffer || _buffer->empty()) { return; } - const auto &pass = sceneData->getDebugRendererPass(); - const auto &shader = sceneData->getDebugRendererShader(); + preparePso(_buffer->_inputAssembler, renderPass, subPassId, (*_material->getPasses())[0]); + if (!_pso) { + return; + } - auto *pso = pipeline::PipelineStateManager::getOrCreatePipelineState(pass, shader, _buffer->_inputAssembler, renderPass); - cmdBuff->bindPipelineState(pso); + cmdBuff->bindPipelineState(_pso); cmdBuff->bindInputAssembler(_buffer->_inputAssembler); + cmdBuff->bindDescriptorSet(0, _passSet); uint32_t offset = 0U; for (auto *batch : _buffer->_batches) { @@ -264,7 +313,7 @@ void DebugRenderer::render(gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdB drawInfo.firstVertex = offset; drawInfo.vertexCount = count; - cmdBuff->bindDescriptorSet(pipeline::materialSet, batch->descriptorSet); + cmdBuff->bindDescriptorSet(1, batch->pass->getDescriptorSet()); cmdBuff->draw(drawInfo); offset += count; @@ -274,26 +323,71 @@ void DebugRenderer::render(gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdB _buffer->reset(); } -void DebugRenderer::destroy() { - CC_SAFE_DESTROY_AND_DELETE(_buffer); +void TextRenderer::updateTextData() { + _buffer->update(); +} - for (auto &iter : _fonts) { - CC_SAFE_DELETE(iter.font); +void TextRenderer::updateWindowSize(uint32_t width, uint32_t height, uint32_t screenTransform, float flip) { + if (_windowWidth == width && _windowHeight == height) { + return; } + + DebugBatchUBOData data = {}; + data.screenSize.x = static_cast(width); + data.screenSize.y = static_cast(height); + data.screenSize.z = 1.0F / static_cast(width); + data.screenSize.w = 1.0F / static_cast(height); + + data.surfaceTransform.x = static_cast(screenTransform); + data.surfaceTransform.y = flip; + _ubo->update(&data, sizeof(DebugBatchUBOData)); + + _windowWidth = width; + _windowHeight = height; } -void DebugRenderer::update() { - if (_buffer) { - _buffer->update(); +uint32_t TextRenderer::getLineHeight(bool bold, bool italic) const { + uint32_t const index = getFontIndex(bold, italic); + const auto &fontInfo = _fonts[index]; + + if (fontInfo.face) { + return fontInfo.face->getLineHeight(); } + + return 0U; } -void DebugRenderer::addText(const ccstd::string &text, const Vec2 &screenPos) { - addText(text, screenPos, DebugTextInfo()); +void TextRenderer::preparePso(gfx::InputAssembler *ia, gfx::RenderPass *renderPass, uint32_t subPassId, scene::Pass *pass) { + auto *shader = pass->getShaderVariant(); + const auto passHash = pass->getHash(); + const auto renderPassHash = renderPass->getHash(); + const auto iaHash = ia->getAttributesHash(); + const auto shaderID = shader->getTypedID(); + + auto hash = passHash; + ccstd::hash_combine(hash, renderPassHash); + ccstd::hash_combine(hash, iaHash); + ccstd::hash_combine(hash, shaderID); + ccstd::hash_combine(hash, subPassId); + + if (hash != _psoHash) { + _pso = gfx::Device::getInstance()->createPipelineState({shader, + _pipelineLayout, + renderPass, + ia->getAttributes(), + *(pass->getRasterizerState()), + *(pass->getDepthStencilState()), + *(pass->getBlendState()), + pass->getPrimitive(), + pass->getDynamicStates(), + gfx::PipelineBindPoint::GRAPHICS, + subPassId}); + _psoHash = hash; + } } -void DebugRenderer::addText(const ccstd::string &text, const Vec2 &screenPos, const DebugTextInfo &info) { - uint32_t index = getFontIndex(info.bold, info.italic); +void TextRenderer::addText(const ccstd::string &text, const Vec2 &screenPos, const DebugTextInfo &info) { + uint32_t const index = getFontIndex(info.bold, info.italic); auto &fontInfo = _fonts[index]; auto *face = fontInfo.face; @@ -302,7 +396,7 @@ void DebugRenderer::addText(const ccstd::string &text, const Vec2 &screenPos, co } std::u32string unicodeText; - bool success = StringUtils::UTF8ToUTF32(text, unicodeText); + bool const success = StringUtils::UTF8ToUTF32(text, unicodeText); if (!success) { return; } @@ -310,10 +404,10 @@ void DebugRenderer::addText(const ccstd::string &text, const Vec2 &screenPos, co auto offsetX = screenPos.x; auto offsetY = screenPos.y; const auto scale = info.scale; - const auto lineHeight = face->getLineHeight() * scale; + const auto lineHeight = static_cast(face->getLineHeight()) * scale; const auto &invTextureSize = fontInfo.invTextureSize; - for (char32_t code : unicodeText) { + for (char32_t const code : unicodeText) { if (code == '\r') { continue; } @@ -330,13 +424,13 @@ void DebugRenderer::addText(const ccstd::string &text, const Vec2 &screenPos, co } if (glyph->width > 0U && glyph->height > 0U) { - auto &batch = _buffer->getOrCreateBatch(_device, info.bold, info.italic, face->getTexture(glyph->page)); + auto &batch = _buffer->getOrCreateBatch(_device, info.bold, info.italic, face->getTexture(glyph->page), _material); - Vec4 rect{offsetX + static_cast(glyph->bearingX) * scale, + Vec4 const rect{offsetX + static_cast(glyph->bearingX) * scale, offsetY - static_cast(glyph->bearingY) * scale, static_cast(glyph->width) * scale, static_cast(glyph->height) * scale}; - Vec4 uv{static_cast(glyph->x) * invTextureSize.x, + Vec4 const uv{static_cast(glyph->x) * invTextureSize.x, static_cast(glyph->y) * invTextureSize.y, static_cast(glyph->width) * invTextureSize.x, static_cast(glyph->height) * invTextureSize.y}; @@ -344,7 +438,7 @@ void DebugRenderer::addText(const ccstd::string &text, const Vec2 &screenPos, co if (info.shadow) { for (auto x = 1U; x <= info.shadowThickness; x++) { for (auto y = 1U; y <= info.shadowThickness; y++) { - Vec4 shadowRect(rect.x + x, rect.y + y, rect.z, rect.w); + Vec4 const shadowRect(rect.x + static_cast(x), rect.y + static_cast(y), rect.z, rect.w); addQuad(batch, shadowRect, uv, info.shadowColor); } } @@ -353,7 +447,7 @@ void DebugRenderer::addText(const ccstd::string &text, const Vec2 &screenPos, co addQuad(batch, rect, uv, info.color); } - offsetX += glyph->advance * scale; + offsetX += static_cast(glyph->advance) * scale; #ifdef USE_KERNING if (i < unicodeText.size() - 1) { @@ -363,33 +457,61 @@ void DebugRenderer::addText(const ccstd::string &text, const Vec2 &screenPos, co } } -uint32_t DebugRenderer::getLineHeight(bool bold, bool italic) { - uint32_t index = getFontIndex(bold, italic); - auto &fontInfo = _fonts[index]; +DebugRenderer *DebugRenderer::instance = nullptr; +DebugRenderer *DebugRenderer::getInstance() { + return instance; +} - if (fontInfo.face) { - return fontInfo.face->getLineHeight(); +DebugRenderer::DebugRenderer() { + DebugRenderer::instance = this; +} + +DebugRenderer::~DebugRenderer() { + DebugRenderer::instance = nullptr; +} + +void DebugRenderer::activate(gfx::Device *device, const DebugRendererInfo &info) { + const auto *window = CC_GET_MAIN_SYSTEM_WINDOW(); + const auto &ext = window->getViewSize(); + const auto width = ext.width * Device::getDevicePixelRatio(); + const auto height = ext.height * Device::getDevicePixelRatio(); + auto fontSize = static_cast(width / 800.0F * static_cast(info.fontSize)); + fontSize = fontSize < 10U ? 10U : (fontSize > 20U ? 20U : fontSize); + + _textRenderer = std::make_unique(); + _textRenderer->initialize(device, info, fontSize, "internal/builtin-debug-renderer"); + _textRenderer->updateWindowSize(static_cast(width), static_cast(height), 0, device->getCombineSignY()); +} + +void DebugRenderer::render(gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuff) { + CC_PROFILE(DebugRendererRender); + if (_textRenderer) { + _textRenderer->render(renderPass, 0, cmdBuff); } +} - return 0U; +void DebugRenderer::destroy() { + _textRenderer = nullptr; } -void DebugRenderer::addQuad(DebugBatch &batch, const Vec4 &rect, const Vec4 &uv, gfx::Color color) { - DebugVertex quad[4] = { - {Vec2(rect.x, rect.y), Vec2(uv.x, uv.y), color}, - {Vec2(rect.x + rect.z, rect.y), Vec2(uv.x + uv.z, uv.y), color}, - {Vec2(rect.x, rect.y + rect.w), Vec2(uv.x, uv.y + uv.w), color}, - {Vec2(rect.x + rect.z, rect.y + rect.w), Vec2(uv.x + uv.z, uv.y + uv.w), color}}; +void DebugRenderer::update() { + if (_textRenderer) { + _textRenderer->updateTextData(); + } +} - // first triangle - batch.vertices.emplace_back(quad[0]); - batch.vertices.emplace_back(quad[1]); - batch.vertices.emplace_back(quad[2]); +void DebugRenderer::addText(const ccstd::string &text, const Vec2 &screenPos) { + addText(text, screenPos, DebugTextInfo()); +} - // second triangle - batch.vertices.emplace_back(quad[1]); - batch.vertices.emplace_back(quad[3]); - batch.vertices.emplace_back(quad[2]); +void DebugRenderer::addText(const ccstd::string &text, const Vec2 &screenPos, const DebugTextInfo &info) { + if (_textRenderer) { + _textRenderer->addText(text, screenPos, info); + } +} + +uint32_t DebugRenderer::getLineHeight(bool bold, bool italic) { + return _textRenderer ? _textRenderer->getLineHeight(bold, italic) : 0U; } } // namespace cc diff --git a/native/cocos/profiler/DebugRenderer.h b/native/cocos/profiler/DebugRenderer.h index 3914bc5f9c1..3b4719c1c3c 100644 --- a/native/cocos/profiler/DebugRenderer.h +++ b/native/cocos/profiler/DebugRenderer.h @@ -1,8 +1,8 @@ /**************************************************************************** Copyright (c) 2021-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 @@ -27,10 +27,17 @@ #include #include "base/std/container/array.h" #include "base/std/container/string.h" +#include "base/Ptr.h" +#include "core/assets/Material.h" +#include "renderer/gfx-base/GFXBuffer.h" #include "renderer/gfx-base/GFXDef-common.h" namespace cc { +namespace scene { +class Pass; +} + namespace pipeline { class PipelineSceneData; } @@ -74,6 +81,36 @@ struct DebugFontInfo { constexpr uint32_t DEBUG_FONT_COUNT = 4U; using DebugFontArray = ccstd::array; +class TextRenderer { +public: + TextRenderer() = default; + ~TextRenderer(); + + void initialize(gfx::Device *device, const DebugRendererInfo &info, uint32_t fontSize, const std::string &effect); + void updateWindowSize(uint32_t width, uint32_t height, uint32_t screenTransform, float flip); + void updateTextData(); + void render(gfx::RenderPass *renderPass, uint32_t subPassId, gfx::CommandBuffer *cmdBuff); + + void addText(const ccstd::string &text, const Vec2 &screenPos, const DebugTextInfo &info); + uint32_t getLineHeight(bool bold = false, bool italic = false) const; + +private: + void preparePso(gfx::InputAssembler *ia, gfx::RenderPass *renderPass, uint32_t subPassId, scene::Pass *pass); + + gfx::Device *_device{nullptr}; + DebugVertexBuffer *_buffer{nullptr}; + DebugFontArray _fonts; + IntrusivePtr _ubo; + IntrusivePtr _pso; + IntrusivePtr _passSet; + IntrusivePtr _passLayout; + IntrusivePtr _pipelineLayout; + IntrusivePtr _material; + uint32_t _psoHash = 0; + uint32_t _windowWidth = 1; + uint32_t _windowHeight = 1; +}; + class DebugRenderer { public: static DebugRenderer *getInstance(); @@ -86,7 +123,7 @@ class DebugRenderer { ~DebugRenderer(); void activate(gfx::Device *device, const DebugRendererInfo &info = DebugRendererInfo()); - void render(gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuff, pipeline::PipelineSceneData *sceneData); + void render(gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuff); void destroy(); void update(); @@ -94,13 +131,9 @@ class DebugRenderer { void addText(const ccstd::string &text, const Vec2 &screenPos, const DebugTextInfo &info); private: - static void addQuad(DebugBatch &batch, const Vec4 &rect, const Vec4 &uv, gfx::Color color); uint32_t getLineHeight(bool bold = false, bool italic = false); - static DebugRenderer *instance; - gfx::Device *_device{nullptr}; - DebugVertexBuffer *_buffer{nullptr}; - DebugFontArray _fonts; + std::unique_ptr _textRenderer; friend class Profiler; }; diff --git a/native/cocos/renderer/gfx-agent/BufferAgent.cpp b/native/cocos/renderer/gfx-agent/BufferAgent.cpp index ec593a72820..5cba71f7fbd 100644 --- a/native/cocos/renderer/gfx-agent/BufferAgent.cpp +++ b/native/cocos/renderer/gfx-agent/BufferAgent.cpp @@ -166,5 +166,18 @@ uint8_t *BufferAgent::getStagingAddress() const { return _stagingBuffer.get() + _size * frameIndex; } +void BufferAgent::readBack(void *dst, uint32_t offset, uint32_t size) { + auto *mq = DeviceAgent::getInstance()->getMessageQueue(); + ENQUEUE_MESSAGE_4( + mq, BufferUpdate, + actor, getActor(), + dst, dst, + offset, offset, + size, size, + { + actor->readBack(dst, offset, size); + }); +} + } // namespace gfx } // namespace cc diff --git a/native/cocos/renderer/gfx-agent/BufferAgent.h b/native/cocos/renderer/gfx-agent/BufferAgent.h index 235a6b44e0d..ac6c34c6501 100644 --- a/native/cocos/renderer/gfx-agent/BufferAgent.h +++ b/native/cocos/renderer/gfx-agent/BufferAgent.h @@ -51,6 +51,7 @@ class CC_DLL BufferAgent final : public Agent { void flush(const uint8_t *buffer) override; uint8_t *getStagingAddress() const override; + void readBack(void *dst, uint32_t offset, uint32_t size) override; static constexpr uint32_t STAGING_BUFFER_THRESHOLD = MessageQueue::MEMORY_CHUNK_SIZE / 2; diff --git a/native/cocos/renderer/gfx-agent/CommandBufferAgent.cpp b/native/cocos/renderer/gfx-agent/CommandBufferAgent.cpp index 6e1d70febdb..c77d4d69116 100644 --- a/native/cocos/renderer/gfx-agent/CommandBufferAgent.cpp +++ b/native/cocos/renderer/gfx-agent/CommandBufferAgent.cpp @@ -557,15 +557,17 @@ void CommandBufferAgent::endQuery(QueryPool *queryPool, uint32_t id) { }); } -void CommandBufferAgent::resetQueryPool(QueryPool *queryPool) { +void CommandBufferAgent::resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) { auto *actorQueryPool = static_cast(queryPool)->getActor(); - ENQUEUE_MESSAGE_2( + ENQUEUE_MESSAGE_4( _messageQueue, CommandBufferResetQueryPool, actor, getActor(), queryPool, actorQueryPool, + first, first, + count, count, { - actor->resetQueryPool(queryPool); + actor->resetQueryPool(queryPool, first, count); }); } @@ -581,6 +583,37 @@ void CommandBufferAgent::completeQueryPool(QueryPool *queryPool) { }); } +void CommandBufferAgent::writeTimestamp(QueryPool *queryPool, uint32_t id) { + auto *actorQueryPool = static_cast(queryPool)->getActor(); + + ENQUEUE_MESSAGE_3( + _messageQueue, CommandBufferWriteTimestamp, + actor, getActor(), + queryPool, actorQueryPool, + id, id, + { + actor->writeTimestamp(queryPool, id); + }); +} + +void CommandBufferAgent::copyQueryResult(QueryPool *queryPool, Buffer* buffer, uint32_t offset, uint32_t stride, uint32_t first, uint32_t count) { + auto *actorQueryPool = static_cast(queryPool)->getActor(); + auto *actorBuffer = static_cast(buffer)->getActor(); + + ENQUEUE_MESSAGE_7( + _messageQueue, CommandBufferGetQueryResult, + actor, getActor(), + queryPool, actorQueryPool, + buffer, actorBuffer, + offset, offset, + stride, stride, + first, first, + count, count, + { + actor->copyQueryResult(queryPool, buffer, offset, stride, first, count); + }); +} + void CommandBufferAgent::customCommand(CustomCommand &&cmd) { ENQUEUE_MESSAGE_2( _messageQueue, CommandBufferCompleteQueryPool, diff --git a/native/cocos/renderer/gfx-agent/CommandBufferAgent.h b/native/cocos/renderer/gfx-agent/CommandBufferAgent.h index 87a6ce8276e..01ed7ba6266 100644 --- a/native/cocos/renderer/gfx-agent/CommandBufferAgent.h +++ b/native/cocos/renderer/gfx-agent/CommandBufferAgent.h @@ -70,8 +70,10 @@ class CC_DLL CommandBufferAgent final : public Agent { void pipelineBarrier(const GeneralBarrier *barrier, const BufferBarrier *const *bufferBarriers, const Buffer *const *buffers, uint32_t bufferBarrierCount, const TextureBarrier *const *textureBarriers, const Texture *const *textures, uint32_t textureBarrierCount) override; void beginQuery(QueryPool *queryPool, uint32_t id) override; void endQuery(QueryPool *queryPool, uint32_t id) override; - void resetQueryPool(QueryPool *queryPool) override; + void resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) override; void completeQueryPool(QueryPool *queryPool) override; + void writeTimestamp(QueryPool *queryPool, uint32_t id) override; + void copyQueryResult(QueryPool *queryPool, Buffer* buffer, uint32_t offset, uint32_t stride, uint32_t first, uint32_t count) override; void customCommand(CustomCommand &&cmd) override; uint32_t getNumDrawCalls() const override { return _actor->getNumDrawCalls(); } diff --git a/native/cocos/renderer/gfx-agent/DeviceAgent.cpp b/native/cocos/renderer/gfx-agent/DeviceAgent.cpp index 872fb3bf849..306961db6df 100644 --- a/native/cocos/renderer/gfx-agent/DeviceAgent.cpp +++ b/native/cocos/renderer/gfx-agent/DeviceAgent.cpp @@ -459,5 +459,9 @@ SampleCount DeviceAgent::getMaxSampleCount(Format format, TextureUsage usage, Te return _actor->getMaxSampleCount(format, usage, flags); } +uint32_t DeviceAgent::getSupportedPipelineStatisticFlags(const PipelineStatisticFlags &flags, PipelineStatisticFlags &outFlags) const { + return _actor->getSupportedPipelineStatisticFlags(flags, outFlags); +} + } // namespace gfx } // namespace cc diff --git a/native/cocos/renderer/gfx-agent/DeviceAgent.h b/native/cocos/renderer/gfx-agent/DeviceAgent.h index ce11e0feb5b..3574bc2d565 100644 --- a/native/cocos/renderer/gfx-agent/DeviceAgent.h +++ b/native/cocos/renderer/gfx-agent/DeviceAgent.h @@ -107,6 +107,7 @@ class CC_DLL DeviceAgent final : public Agent { void enableAutoBarrier(bool en) override; SampleCount getMaxSampleCount(Format format, TextureUsage usage, TextureFlags flags) const override; + uint32_t getSupportedPipelineStatisticFlags(const PipelineStatisticFlags &flags, PipelineStatisticFlags &outFlags) const override; protected: static DeviceAgent *instance; diff --git a/native/cocos/renderer/gfx-base/GFXBuffer.h b/native/cocos/renderer/gfx-base/GFXBuffer.h index 715b9830780..dcc18be53ed 100644 --- a/native/cocos/renderer/gfx-base/GFXBuffer.h +++ b/native/cocos/renderer/gfx-base/GFXBuffer.h @@ -50,6 +50,7 @@ class CC_DLL Buffer : public GFXObject, public RefCounted { void write(const uint8_t *value, uint32_t offset, uint32_t size) const; virtual void update(const void *buffer, uint32_t size) = 0; + virtual void readBack(void *dst, uint32_t offset, uint32_t size) {}; inline void update(const void *buffer) { update(buffer, _size); } diff --git a/native/cocos/renderer/gfx-base/GFXCommandBuffer.h b/native/cocos/renderer/gfx-base/GFXCommandBuffer.h index d521474ea0c..737559ed8c2 100644 --- a/native/cocos/renderer/gfx-base/GFXCommandBuffer.h +++ b/native/cocos/renderer/gfx-base/GFXCommandBuffer.h @@ -71,7 +71,9 @@ class CC_DLL CommandBuffer : public GFXObject, public RefCounted { virtual void dispatch(const DispatchInfo &info) = 0; virtual void beginQuery(QueryPool *queryPool, uint32_t id) = 0; virtual void endQuery(QueryPool *queryPool, uint32_t id) = 0; - virtual void resetQueryPool(QueryPool *queryPool) = 0; + virtual void resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) = 0; + virtual void writeTimestamp(QueryPool *queryPool, uint32_t id) {} + virtual void copyQueryResult(QueryPool *queryPool, Buffer* buffer, uint32_t offset, uint32_t stride, uint32_t first, uint32_t count) {} virtual void completeQueryPool(QueryPool *queryPool) {} using CustomCommand = std::function; diff --git a/native/cocos/renderer/gfx-base/GFXDef-common.h b/native/cocos/renderer/gfx-base/GFXDef-common.h index 84926c05202..25988f49494 100644 --- a/native/cocos/renderer/gfx-base/GFXDef-common.h +++ b/native/cocos/renderer/gfx-base/GFXDef-common.h @@ -813,6 +813,20 @@ enum class PassType : uint32_t { }; CC_ENUM_CONVERSION_OPERATOR(PassType); +enum class PipelineStatisticFlagBit : uint32_t { + NONE = 0, + IA_VERTICES = 0x01, + IA_PRIMITIVES = 0x02, + VS_INVOCATIONS = 0x04, + CLIP_INVOCATIONS = 0x08, + CLIP_PRIMITIVES = 0x10, + FS_INVOCATIONS = 0x20, + CS_INVOCATIONS = 0x40, + ALL = IA_VERTICES | IA_PRIMITIVES | VS_INVOCATIONS | CLIP_INVOCATIONS | CLIP_PRIMITIVES | FS_INVOCATIONS | CS_INVOCATIONS +}; +using PipelineStatisticFlags = PipelineStatisticFlagBit; +CC_ENUM_BITWISE_OPERATORS(PipelineStatisticFlagBit); + #define EXPOSE_COPY_FN(type) \ type ©(const type &rhs) { \ *this = rhs; \ @@ -850,6 +864,8 @@ struct DeviceCaps { Size maxComputeWorkGroupSize; Size maxComputeWorkGroupCount; + uint32_t timestampPeriod{1}; + bool supportQuery{false}; bool supportVariableRateShading{false}; bool supportSubPassShading{false}; @@ -1575,8 +1591,7 @@ struct QueueInfo { struct QueryPoolInfo { QueryType type{QueryType::OCCLUSION}; uint32_t maxQueryObjects{DEFAULT_MAX_QUERY_OBJECTS}; - bool forceWait{true}; - + PipelineStatisticFlags pipelineStatisticFlags{PipelineStatisticFlagBit::NONE}; EXPOSE_COPY_FN(QueryPoolInfo) }; @@ -1623,6 +1638,16 @@ struct DynamicStates { EXPOSE_COPY_FN(DynamicStates) }; +struct PipelineStatisticData { + uint64_t inputAssemblyVertices; + uint64_t inputAssemblyPrimitives; + uint64_t vsInvocations; + uint64_t clipInvocations; + uint64_t clipPrimitives; + uint64_t fsInvocations; + uint64_t csInvocations; +}; + #undef EXPOSE_COPY_FN } // namespace gfx diff --git a/native/cocos/renderer/gfx-base/GFXDevice.h b/native/cocos/renderer/gfx-base/GFXDevice.h index e59e8c5041e..15ced8aafa9 100644 --- a/native/cocos/renderer/gfx-base/GFXDevice.h +++ b/native/cocos/renderer/gfx-base/GFXDevice.h @@ -129,6 +129,23 @@ class CC_DLL Device : public RefCounted { return SampleCount::X1; }; + virtual uint32_t getSupportedPipelineStatisticFlags(const PipelineStatisticFlags &flags, PipelineStatisticFlags &outFlags) const { + std::ignore = flags; + std::ignore = outFlags; + return 0; + } + + inline uint8_t getCombineSignY() const { + // 0: vk, 1: metal, 2: none, 3: gl-like + static int8_t combineSignY{-1}; + if (combineSignY < 0) { + const float screenSpaceSignY = _caps.screenSpaceSignY * 0.5F + 0.5F; + const float clipSpaceSignY = _caps.clipSpaceSignY * 0.5F + 0.5F; + combineSignY = static_cast(static_cast(screenSpaceSignY) << 1 | static_cast(clipSpaceSignY)); + } + return static_cast(combineSignY); + } + protected: static Device *instance; static bool isSupportDetachDeviceThread; diff --git a/native/cocos/renderer/gfx-base/GFXQueryPool.cpp b/native/cocos/renderer/gfx-base/GFXQueryPool.cpp index 783f257f21a..e9a2d398fe5 100644 --- a/native/cocos/renderer/gfx-base/GFXQueryPool.cpp +++ b/native/cocos/renderer/gfx-base/GFXQueryPool.cpp @@ -36,8 +36,7 @@ QueryPool::~QueryPool() = default; void QueryPool::initialize(const QueryPoolInfo &info) { _type = info.type; _maxQueryObjects = info.maxQueryObjects; - _forceWait = info.forceWait; - + _psFlags = info.pipelineStatisticFlags; doInit(info); } diff --git a/native/cocos/renderer/gfx-base/GFXQueryPool.h b/native/cocos/renderer/gfx-base/GFXQueryPool.h index 1fe36e00100..77194fc07e9 100644 --- a/native/cocos/renderer/gfx-base/GFXQueryPool.h +++ b/native/cocos/renderer/gfx-base/GFXQueryPool.h @@ -28,6 +28,7 @@ #include #include "GFXObject.h" #include "base/std/container/unordered_map.h" +#include "base/RefCounted.h" namespace cc { namespace gfx { @@ -45,7 +46,7 @@ namespace gfx { * completeQueryPool */ -class CC_DLL QueryPool : public GFXObject { +class CC_DLL QueryPool : public GFXObject, public RefCounted { public: QueryPool(); ~QueryPool() override; @@ -57,7 +58,6 @@ class CC_DLL QueryPool : public GFXObject { inline uint64_t getResult(uint32_t id) { return _results[id]; } inline QueryType getType() const { return _type; } inline uint32_t getMaxQueryObjects() const { return _maxQueryObjects; } - inline bool getForceWait() const { return _forceWait; } protected: virtual void doInit(const QueryPoolInfo &info) = 0; @@ -65,7 +65,7 @@ class CC_DLL QueryPool : public GFXObject { QueryType _type{QueryType::OCCLUSION}; uint32_t _maxQueryObjects{0}; - bool _forceWait{true}; + PipelineStatisticFlags _psFlags{0}; std::mutex _mutex; ccstd::unordered_map _results; }; diff --git a/native/cocos/renderer/gfx-empty/EmptyCommandBuffer.cpp b/native/cocos/renderer/gfx-empty/EmptyCommandBuffer.cpp index 3a6b283ca88..5e682206736 100644 --- a/native/cocos/renderer/gfx-empty/EmptyCommandBuffer.cpp +++ b/native/cocos/renderer/gfx-empty/EmptyCommandBuffer.cpp @@ -122,7 +122,7 @@ void EmptyCommandBuffer::beginQuery(QueryPool *queryPool, uint32_t id) { void EmptyCommandBuffer::endQuery(QueryPool *queryPool, uint32_t id) { } -void EmptyCommandBuffer::resetQueryPool(QueryPool *queryPool) { +void EmptyCommandBuffer::resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) { } } // namespace gfx diff --git a/native/cocos/renderer/gfx-empty/EmptyCommandBuffer.h b/native/cocos/renderer/gfx-empty/EmptyCommandBuffer.h index 77ef43864f7..c8e59190ff1 100644 --- a/native/cocos/renderer/gfx-empty/EmptyCommandBuffer.h +++ b/native/cocos/renderer/gfx-empty/EmptyCommandBuffer.h @@ -61,7 +61,7 @@ class CC_DLL EmptyCommandBuffer final : public CommandBuffer { void pipelineBarrier(const GeneralBarrier *barrier, const BufferBarrier *const *bufferBarriers, const Buffer *const *buffers, uint32_t bufferCount, const TextureBarrier *const *textureBarriers, const Texture *const *textures, uint32_t textureBarrierCount) override; void beginQuery(QueryPool *queryPool, uint32_t id) override; void endQuery(QueryPool *queryPool, uint32_t id) override; - void resetQueryPool(QueryPool *queryPool) override; + void resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) override; protected: void doInit(const CommandBufferInfo &info) override; diff --git a/native/cocos/renderer/gfx-empty/EmptyDevice.cpp b/native/cocos/renderer/gfx-empty/EmptyDevice.cpp index 600abb4efd7..878803ed47d 100644 --- a/native/cocos/renderer/gfx-empty/EmptyDevice.cpp +++ b/native/cocos/renderer/gfx-empty/EmptyDevice.cpp @@ -62,7 +62,7 @@ bool EmptyDevice::doInit(const DeviceInfo & /*info*/) { queueInfo.type = QueueType::GRAPHICS; _queue = createQueue(queueInfo); - QueryPoolInfo queryPoolInfo{QueryType::OCCLUSION, DEFAULT_MAX_QUERY_OBJECTS, true}; + QueryPoolInfo queryPoolInfo{QueryType::OCCLUSION, DEFAULT_MAX_QUERY_OBJECTS}; _queryPool = createQueryPool(queryPoolInfo); CommandBufferInfo cmdBuffInfo; diff --git a/native/cocos/renderer/gfx-gles2/GLES2CommandBuffer.h b/native/cocos/renderer/gfx-gles2/GLES2CommandBuffer.h index ad4275dad47..e37b77327c4 100644 --- a/native/cocos/renderer/gfx-gles2/GLES2CommandBuffer.h +++ b/native/cocos/renderer/gfx-gles2/GLES2CommandBuffer.h @@ -74,7 +74,7 @@ class CC_GLES2_API GLES2CommandBuffer : public CommandBuffer { void pipelineBarrier(const GeneralBarrier *barrier, const BufferBarrier *const *bufferBarriers, const Buffer *const *buffers, uint32_t bufferCount, const TextureBarrier *const *textureBarriers, const Texture *const *textures, uint32_t textureBarrierCount) override {} void beginQuery(QueryPool *queryPool, uint32_t id) override {} void endQuery(QueryPool *queryPool, uint32_t id) override {} - void resetQueryPool(QueryPool *queryPool) override {} + void resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) override {} protected: void doInit(const CommandBufferInfo &info) override; diff --git a/native/cocos/renderer/gfx-gles2/GLES2Device.cpp b/native/cocos/renderer/gfx-gles2/GLES2Device.cpp index f07071766e8..669747a9e4d 100644 --- a/native/cocos/renderer/gfx-gles2/GLES2Device.cpp +++ b/native/cocos/renderer/gfx-gles2/GLES2Device.cpp @@ -221,7 +221,7 @@ bool GLES2Device::doInit(const DeviceInfo & /*info*/) { queueInfo.type = QueueType::GRAPHICS; _queue = createQueue(queueInfo); - QueryPoolInfo queryPoolInfo{QueryType::OCCLUSION, DEFAULT_MAX_QUERY_OBJECTS, true}; + QueryPoolInfo queryPoolInfo{QueryType::OCCLUSION, DEFAULT_MAX_QUERY_OBJECTS}; _queryPool = createQueryPool(queryPoolInfo); CommandBufferInfo cmdBuffInfo; diff --git a/native/cocos/renderer/gfx-gles3/GLES3CommandBuffer.cpp b/native/cocos/renderer/gfx-gles3/GLES3CommandBuffer.cpp index b0321b81d40..ff135faa138 100644 --- a/native/cocos/renderer/gfx-gles3/GLES3CommandBuffer.cpp +++ b/native/cocos/renderer/gfx-gles3/GLES3CommandBuffer.cpp @@ -480,12 +480,14 @@ void GLES3CommandBuffer::endQuery(QueryPool *queryPool, uint32_t id) { _curCmdPackage->cmds.push(GLESCmdType::QUERY); } -void GLES3CommandBuffer::resetQueryPool(QueryPool *queryPool) { +void GLES3CommandBuffer::resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) { auto *gles3QueryPool = static_cast(queryPool); GLES3CmdQuery *cmd = _cmdAllocator->queryCmdPool.alloc(); cmd->queryPool = gles3QueryPool; cmd->type = GLES3QueryType::RESET; cmd->id = 0; + cmd->first = first; + cmd->count = count; _curCmdPackage->queryCmds.push(cmd); _curCmdPackage->cmds.push(GLESCmdType::QUERY); diff --git a/native/cocos/renderer/gfx-gles3/GLES3CommandBuffer.h b/native/cocos/renderer/gfx-gles3/GLES3CommandBuffer.h index 25af0d6d22e..57e1e35591d 100644 --- a/native/cocos/renderer/gfx-gles3/GLES3CommandBuffer.h +++ b/native/cocos/renderer/gfx-gles3/GLES3CommandBuffer.h @@ -73,7 +73,7 @@ class CC_GLES3_API GLES3CommandBuffer : public CommandBuffer { void pipelineBarrier(const GeneralBarrier *barrier, const BufferBarrier *const *bufferBarriers, const Buffer *const * /*buffers*/, uint32_t bufferBarrierCount, const TextureBarrier *const *textureBarriers, const Texture *const * /*textures*/, uint32_t textureBarrierCount) override; void beginQuery(QueryPool *queryPool, uint32_t id) override; void endQuery(QueryPool *queryPool, uint32_t id) override; - void resetQueryPool(QueryPool *queryPool) override; + void resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) override; protected: friend class GLES3Queue; diff --git a/native/cocos/renderer/gfx-gles3/GLES3Commands.h b/native/cocos/renderer/gfx-gles3/GLES3Commands.h index ef9eee6365b..5416c185875 100644 --- a/native/cocos/renderer/gfx-gles3/GLES3Commands.h +++ b/native/cocos/renderer/gfx-gles3/GLES3Commands.h @@ -161,6 +161,8 @@ class GLES3CmdQuery final : public GLESCmd { GLES3QueryPool *queryPool = nullptr; GLES3QueryType type = GLES3QueryType::BEGIN; uint32_t id = 0U; + uint32_t first = 0U; + uint32_t count = 1U; GLES3CmdQuery() : GLESCmd(GLESCmdType::QUERY) {} diff --git a/native/cocos/renderer/gfx-gles3/GLES3Device.cpp b/native/cocos/renderer/gfx-gles3/GLES3Device.cpp index e081883bece..0298dea6a74 100644 --- a/native/cocos/renderer/gfx-gles3/GLES3Device.cpp +++ b/native/cocos/renderer/gfx-gles3/GLES3Device.cpp @@ -227,7 +227,7 @@ bool GLES3Device::doInit(const DeviceInfo & /*info*/) { queueInfo.type = QueueType::GRAPHICS; _queue = createQueue(queueInfo); - QueryPoolInfo queryPoolInfo{QueryType::OCCLUSION, DEFAULT_MAX_QUERY_OBJECTS, true}; + QueryPoolInfo queryPoolInfo{QueryType::OCCLUSION, DEFAULT_MAX_QUERY_OBJECTS}; _queryPool = createQueryPool(queryPoolInfo); CommandBufferInfo cmdBuffInfo; diff --git a/native/cocos/renderer/gfx-gles3/GLES3GPUObjects.h b/native/cocos/renderer/gfx-gles3/GLES3GPUObjects.h index 4dabb15f051..9e6f40014f7 100644 --- a/native/cocos/renderer/gfx-gles3/GLES3GPUObjects.h +++ b/native/cocos/renderer/gfx-gles3/GLES3GPUObjects.h @@ -95,7 +95,6 @@ class GLES3GPUQueryPool final { public: QueryType type{QueryType::OCCLUSION}; uint32_t maxQueryObjects{0}; - bool forceWait{true}; ccstd::vector glQueryIds; inline GLuint mapGLQueryId(uint32_t queryId) { diff --git a/native/cocos/renderer/gfx-gles3/GLES3PrimaryCommandBuffer.cpp b/native/cocos/renderer/gfx-gles3/GLES3PrimaryCommandBuffer.cpp index bde2aac8991..9a545d32447 100644 --- a/native/cocos/renderer/gfx-gles3/GLES3PrimaryCommandBuffer.cpp +++ b/native/cocos/renderer/gfx-gles3/GLES3PrimaryCommandBuffer.cpp @@ -198,7 +198,9 @@ void GLES3PrimaryCommandBuffer::endQuery(QueryPool *queryPool, uint32_t id) { cmdFuncGLES3Query(GLES3Device::getInstance(), gles3QueryPool, GLES3QueryType::END, id); } -void GLES3PrimaryCommandBuffer::resetQueryPool(QueryPool *queryPool) { +void GLES3PrimaryCommandBuffer::resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) { + std::ignore = first; + std::ignore = count; auto *gles3QueryPool = static_cast(queryPool); cmdFuncGLES3Query(GLES3Device::getInstance(), gles3QueryPool, GLES3QueryType::RESET, 0); diff --git a/native/cocos/renderer/gfx-gles3/GLES3PrimaryCommandBuffer.h b/native/cocos/renderer/gfx-gles3/GLES3PrimaryCommandBuffer.h index 93ab3a06dd7..cb4bc380b8f 100644 --- a/native/cocos/renderer/gfx-gles3/GLES3PrimaryCommandBuffer.h +++ b/native/cocos/renderer/gfx-gles3/GLES3PrimaryCommandBuffer.h @@ -55,7 +55,7 @@ class CC_GLES3_API GLES3PrimaryCommandBuffer final : public GLES3CommandBuffer { void resolveTexture(Texture *srcTexture, Texture *dstTexture, const TextureCopy *regions, uint32_t count) override; void beginQuery(QueryPool *queryPool, uint32_t id) override; void endQuery(QueryPool *queryPool, uint32_t id) override; - void resetQueryPool(QueryPool *queryPool) override; + void resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) override; protected: friend class GLES3Queue; diff --git a/native/cocos/renderer/gfx-gles3/GLES3QueryPool.cpp b/native/cocos/renderer/gfx-gles3/GLES3QueryPool.cpp index 72732d014ca..a92691c2b70 100644 --- a/native/cocos/renderer/gfx-gles3/GLES3QueryPool.cpp +++ b/native/cocos/renderer/gfx-gles3/GLES3QueryPool.cpp @@ -44,7 +44,6 @@ void GLES3QueryPool::doInit(const QueryPoolInfo & /*info*/) { _gpuQueryPool = ccnew GLES3GPUQueryPool; _gpuQueryPool->type = _type; _gpuQueryPool->maxQueryObjects = _maxQueryObjects; - _gpuQueryPool->forceWait = _forceWait; _gpuQueryPool->glQueryIds.resize(_maxQueryObjects, 0U); cmdFuncGLES3CreateQueryPool(device, _gpuQueryPool); diff --git a/native/cocos/renderer/gfx-metal/MTLCommandBuffer.h b/native/cocos/renderer/gfx-metal/MTLCommandBuffer.h index 51941df646a..2b09643796f 100644 --- a/native/cocos/renderer/gfx-metal/MTLCommandBuffer.h +++ b/native/cocos/renderer/gfx-metal/MTLCommandBuffer.h @@ -83,7 +83,7 @@ class CCMTLCommandBuffer final : public CommandBuffer { void copyTextureToBuffers(Texture *src, uint8_t *const *buffers, const BufferTextureCopy *regions, uint32_t count); void beginQuery(QueryPool *queryPool, uint32_t id) override; void endQuery(QueryPool *queryPool, uint32_t id) override; - void resetQueryPool(QueryPool *queryPool) override; + void resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) override; void completeQueryPool(QueryPool *queryPool) override; inline bool isCommandBufferBegan() const { return _commandBufferBegan; } inline CCMTLGPUCommandBufferObject *gpuCommandBufferObj() const { return _gpuCommandBufferObj; } diff --git a/native/cocos/renderer/gfx-metal/MTLCommandBuffer.mm b/native/cocos/renderer/gfx-metal/MTLCommandBuffer.mm index b1a11e32e84..8d1c020cc20 100644 --- a/native/cocos/renderer/gfx-metal/MTLCommandBuffer.mm +++ b/native/cocos/renderer/gfx-metal/MTLCommandBuffer.mm @@ -1146,7 +1146,7 @@ of this software and associated engine source code (the "Software"), a limited, } } -void CCMTLCommandBuffer::resetQueryPool(QueryPool *queryPool) { +void CCMTLCommandBuffer::resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) { auto *mtlQueryPool = static_cast(queryPool); mtlQueryPool->_ids.clear(); diff --git a/native/cocos/renderer/gfx-metal/MTLDevice.mm b/native/cocos/renderer/gfx-metal/MTLDevice.mm index 25a5887fc87..4a37317fae5 100644 --- a/native/cocos/renderer/gfx-metal/MTLDevice.mm +++ b/native/cocos/renderer/gfx-metal/MTLDevice.mm @@ -156,7 +156,7 @@ of this software and associated engine source code (the "Software"), a limited, queueInfo.type = QueueType::GRAPHICS; _queue = createQueue(queueInfo); - QueryPoolInfo queryPoolInfo{QueryType::OCCLUSION, DEFAULT_MAX_QUERY_OBJECTS, true}; + QueryPoolInfo queryPoolInfo{QueryType::OCCLUSION, DEFAULT_MAX_QUERY_OBJECTS}; _queryPool = createQueryPool(queryPoolInfo); CommandBufferInfo cmdBuffInfo; diff --git a/native/cocos/renderer/gfx-metal/MTLGPUObjects.h b/native/cocos/renderer/gfx-metal/MTLGPUObjects.h index 1a431ede3e4..00f47fda012 100644 --- a/native/cocos/renderer/gfx-metal/MTLGPUObjects.h +++ b/native/cocos/renderer/gfx-metal/MTLGPUObjects.h @@ -331,7 +331,6 @@ struct CCMTLGPUDeviceObject { struct CCMTLGPUQueryPool { QueryType type = QueryType::OCCLUSION; uint32_t maxQueryObjects = 0; - bool forceWait = true; id visibilityResultBuffer = nil; CCMTLSemaphore *semaphore = nullptr; }; diff --git a/native/cocos/renderer/gfx-metal/MTLQueryPool.mm b/native/cocos/renderer/gfx-metal/MTLQueryPool.mm index 6dfb42fd9af..221fc7c940a 100644 --- a/native/cocos/renderer/gfx-metal/MTLQueryPool.mm +++ b/native/cocos/renderer/gfx-metal/MTLQueryPool.mm @@ -47,7 +47,6 @@ of this software and associated engine source code (the "Software"), a limited, _gpuQueryPool = ccnew CCMTLGPUQueryPool; _gpuQueryPool->type = _type; _gpuQueryPool->maxQueryObjects = _maxQueryObjects; - _gpuQueryPool->forceWait = _forceWait; _gpuQueryPool->visibilityResultBuffer = [mtlDevice newBufferWithLength:_maxQueryObjects * sizeof(uint64_t) options:MTLResourceStorageModeShared]; _gpuQueryPool->semaphore = ccnew CCMTLSemaphore(1); } diff --git a/native/cocos/renderer/gfx-validator/BufferValidator.cpp b/native/cocos/renderer/gfx-validator/BufferValidator.cpp index a6e5f691b05..1e37520c730 100644 --- a/native/cocos/renderer/gfx-validator/BufferValidator.cpp +++ b/native/cocos/renderer/gfx-validator/BufferValidator.cpp @@ -183,6 +183,10 @@ void BufferValidator::sanityCheck(const void *buffer, uint32_t size) { _lastUpdateFrame = cur; } +void BufferValidator::readBack(void *dst, uint32_t offset, uint32_t size) { + _actor->readBack(dst, offset, size); +} + void BufferValidator::addView(BufferValidator *view) { _views.emplace_back(view); } diff --git a/native/cocos/renderer/gfx-validator/BufferValidator.h b/native/cocos/renderer/gfx-validator/BufferValidator.h index 30acaba2205..b912825a072 100644 --- a/native/cocos/renderer/gfx-validator/BufferValidator.h +++ b/native/cocos/renderer/gfx-validator/BufferValidator.h @@ -51,6 +51,7 @@ class CC_DLL BufferValidator final : public Agent { void flush(const uint8_t *buffer) override; uint8_t *getStagingAddress() const override; + void readBack(void *dst, uint32_t offset, uint32_t size) override; void addView(BufferValidator *view); void removeView(BufferValidator *view); diff --git a/native/cocos/renderer/gfx-validator/CommandBufferValidator.cpp b/native/cocos/renderer/gfx-validator/CommandBufferValidator.cpp index fcc1a2c571d..4a6125f4f92 100644 --- a/native/cocos/renderer/gfx-validator/CommandBufferValidator.cpp +++ b/native/cocos/renderer/gfx-validator/CommandBufferValidator.cpp @@ -388,7 +388,7 @@ void CommandBufferValidator::draw(const DrawInfo &info) { if (!_curStates.descriptorSets[i]) continue; // there may be inactive sets const auto &dsBindings = _curStates.descriptorSets[i]->getLayout()->getBindings(); const auto &psoBindings = psoLayouts[i]->getBindings(); - CC_ASSERT(psoBindings.size() == dsBindings.size()); + // CC_ASSERT(psoBindings.size() == dsBindings.size()); } /////////// execute /////////// @@ -595,12 +595,12 @@ void CommandBufferValidator::endQuery(QueryPool *queryPool, uint32_t id) { _actor->endQuery(actorQueryPool, id); } -void CommandBufferValidator::resetQueryPool(QueryPool *queryPool) { +void CommandBufferValidator::resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) { CC_ASSERT(isInited()); CC_ASSERT(static_cast(queryPool)->isInited()); QueryPool *actorQueryPool = static_cast(queryPool)->getActor(); - _actor->resetQueryPool(actorQueryPool); + _actor->resetQueryPool(actorQueryPool, first, count); } void CommandBufferValidator::completeQueryPool(QueryPool *queryPool) { @@ -611,6 +611,23 @@ void CommandBufferValidator::completeQueryPool(QueryPool *queryPool) { _actor->completeQueryPool(actorQueryPool); } +void CommandBufferValidator::writeTimestamp(QueryPool *queryPool, uint32_t id) { + CC_ASSERT(isInited()); + CC_ASSERT(static_cast(queryPool)->isInited()); + + QueryPool *actorQueryPool = static_cast(queryPool)->getActor(); + _actor->writeTimestamp(actorQueryPool, id); +} + +void CommandBufferValidator::copyQueryResult(QueryPool *queryPool, Buffer* buffer, uint32_t offset, uint32_t stride, uint32_t first, uint32_t count) { + CC_ASSERT(isInited()); + CC_ASSERT(static_cast(queryPool)->isInited()); + + QueryPool *actorQueryPool = static_cast(queryPool)->getActor(); + Buffer *actorBuffer = static_cast(buffer)->getActor(); + _actor->copyQueryResult(actorQueryPool, actorBuffer, offset, stride, first, count); +} + void CommandBufferValidator::customCommand(CustomCommand &&cmd) { _actor->customCommand(std::move(cmd)); } diff --git a/native/cocos/renderer/gfx-validator/CommandBufferValidator.h b/native/cocos/renderer/gfx-validator/CommandBufferValidator.h index 75071593d8b..3932d3e8729 100644 --- a/native/cocos/renderer/gfx-validator/CommandBufferValidator.h +++ b/native/cocos/renderer/gfx-validator/CommandBufferValidator.h @@ -67,7 +67,9 @@ class CC_DLL CommandBufferValidator final : public Agent { void pipelineBarrier(const GeneralBarrier *barrier, const BufferBarrier *const *bufferBarriers, const Buffer *const *buffers, uint32_t bufferBarrierCount, const TextureBarrier *const *textureBarriers, const Texture *const *textures, uint32_t textureBarrierCount) override; void beginQuery(QueryPool *queryPool, uint32_t id) override; void endQuery(QueryPool *queryPool, uint32_t id) override; - void resetQueryPool(QueryPool *queryPool) override; + void resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) override; + void writeTimestamp(QueryPool *queryPool, uint32_t id) override; + void copyQueryResult(QueryPool *queryPool, Buffer* buffer, uint32_t offset, uint32_t stride, uint32_t first, uint32_t count) override; void completeQueryPool(QueryPool *queryPool) override; void customCommand(CustomCommand &&cmd) override; diff --git a/native/cocos/renderer/gfx-validator/DeviceValidator.cpp b/native/cocos/renderer/gfx-validator/DeviceValidator.cpp index ff97a9910a1..4da00baff12 100644 --- a/native/cocos/renderer/gfx-validator/DeviceValidator.cpp +++ b/native/cocos/renderer/gfx-validator/DeviceValidator.cpp @@ -356,5 +356,9 @@ SampleCount DeviceValidator::getMaxSampleCount(Format format, TextureUsage usage return _actor->getMaxSampleCount(format, usage, flags); } +uint32_t DeviceValidator::getSupportedPipelineStatisticFlags(const PipelineStatisticFlags &flags, PipelineStatisticFlags &outFlags) const { + return _actor->getSupportedPipelineStatisticFlags(flags, outFlags); +} + } // namespace gfx } // namespace cc diff --git a/native/cocos/renderer/gfx-validator/DeviceValidator.h b/native/cocos/renderer/gfx-validator/DeviceValidator.h index 4122d715e75..10ea8d46d69 100644 --- a/native/cocos/renderer/gfx-validator/DeviceValidator.h +++ b/native/cocos/renderer/gfx-validator/DeviceValidator.h @@ -96,6 +96,7 @@ class CC_DLL DeviceValidator final : public Agent { void enableAutoBarrier(bool enable) override; SampleCount getMaxSampleCount(Format format, TextureUsage usage, TextureFlags flags) const override; + uint32_t getSupportedPipelineStatisticFlags(const PipelineStatisticFlags &flags, PipelineStatisticFlags &outFlags) const override; protected: static DeviceValidator *instance; diff --git a/native/cocos/renderer/gfx-vulkan/VKBuffer.cpp b/native/cocos/renderer/gfx-vulkan/VKBuffer.cpp index 63f4e1e114d..f0ef52555c8 100644 --- a/native/cocos/renderer/gfx-vulkan/VKBuffer.cpp +++ b/native/cocos/renderer/gfx-vulkan/VKBuffer.cpp @@ -91,6 +91,10 @@ void CCVKBuffer::update(const void *buffer, uint32_t size) { cmdFuncCCVKUpdateBuffer(CCVKDevice::getInstance(), _gpuBuffer, buffer, size, nullptr); } +void CCVKBuffer::readBack(void *dst, uint32_t offset, uint32_t size) { + cmdFuncCCVKReadBackBuffer(CCVKDevice::getInstance(), _gpuBuffer, dst, offset, size); +} + void CCVKGPUBuffer::shutdown() { CCVKDevice::getInstance()->gpuBarrierManager()->cancel(this); CCVKDevice::getInstance()->gpuRecycleBin()->collect(this); diff --git a/native/cocos/renderer/gfx-vulkan/VKBuffer.h b/native/cocos/renderer/gfx-vulkan/VKBuffer.h index 61705b9a173..792b8e803b8 100644 --- a/native/cocos/renderer/gfx-vulkan/VKBuffer.h +++ b/native/cocos/renderer/gfx-vulkan/VKBuffer.h @@ -37,6 +37,7 @@ class CC_VULKAN_API CCVKBuffer final : public Buffer { ~CCVKBuffer() override; void update(const void *buffer, uint32_t size) override; + void readBack(void *dst, uint32_t offset, uint32_t size) override; inline CCVKGPUBuffer *gpuBuffer() const { return _gpuBuffer; } inline CCVKGPUBufferView *gpuBufferView() const { return _gpuBufferView; } diff --git a/native/cocos/renderer/gfx-vulkan/VKCommandBuffer.cpp b/native/cocos/renderer/gfx-vulkan/VKCommandBuffer.cpp index f1a7da28720..e2e559dad77 100644 --- a/native/cocos/renderer/gfx-vulkan/VKCommandBuffer.cpp +++ b/native/cocos/renderer/gfx-vulkan/VKCommandBuffer.cpp @@ -971,33 +971,42 @@ void CCVKCommandBuffer::pipelineBarrier(const GeneralBarrier *barrier, const Buf } } -void CCVKCommandBuffer::beginQuery(QueryPool *queryPool, uint32_t /*id*/) { +void CCVKCommandBuffer::beginQuery(QueryPool *queryPool, uint32_t id) { auto *vkQueryPool = static_cast(queryPool); CCVKGPUQueryPool *gpuQueryPool = vkQueryPool->gpuQueryPool(); - auto queryId = static_cast(vkQueryPool->_ids.size()); - if (queryId < queryPool->getMaxQueryObjects()) { - vkCmdBeginQuery(_gpuCommandBuffer->vkCommandBuffer, gpuQueryPool->vkPool, queryId, 0); - } + vkCmdBeginQuery(_gpuCommandBuffer->vkCommandBuffer, gpuQueryPool->vkPool, id, 0); } void CCVKCommandBuffer::endQuery(QueryPool *queryPool, uint32_t id) { auto *vkQueryPool = static_cast(queryPool); CCVKGPUQueryPool *gpuQueryPool = vkQueryPool->gpuQueryPool(); - auto queryId = static_cast(vkQueryPool->_ids.size()); + vkCmdEndQuery(_gpuCommandBuffer->vkCommandBuffer, gpuQueryPool->vkPool, id); +} - if (queryId < queryPool->getMaxQueryObjects()) { - vkCmdEndQuery(_gpuCommandBuffer->vkCommandBuffer, gpuQueryPool->vkPool, queryId); - vkQueryPool->_ids.push_back(id); - } +void CCVKCommandBuffer::resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) { + auto *vkQueryPool = static_cast(queryPool); + CCVKGPUQueryPool *gpuQueryPool = vkQueryPool->gpuQueryPool(); + vkCmdResetQueryPool(_gpuCommandBuffer->vkCommandBuffer, gpuQueryPool->vkPool, first, count); } -void CCVKCommandBuffer::resetQueryPool(QueryPool *queryPool) { +void CCVKCommandBuffer::writeTimestamp(QueryPool *queryPool, uint32_t id) { auto *vkQueryPool = static_cast(queryPool); CCVKGPUQueryPool *gpuQueryPool = vkQueryPool->gpuQueryPool(); + vkCmdWriteTimestamp(_gpuCommandBuffer->vkCommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, gpuQueryPool->vkPool, id); +} + +void CCVKCommandBuffer::copyQueryResult(QueryPool *queryPool, Buffer* buffer, uint32_t offset, uint32_t stride, uint32_t first, uint32_t count) { + auto *vkQueryPool = static_cast(queryPool); + CCVKGPUQueryPool *gpuQueryPool = vkQueryPool->gpuQueryPool(); + + auto *vkBuffer = static_cast(buffer); + CCVKGPUBuffer *gpuBuffer = vkBuffer->gpuBuffer(); - vkCmdResetQueryPool(_gpuCommandBuffer->vkCommandBuffer, gpuQueryPool->vkPool, 0, queryPool->getMaxQueryObjects()); - vkQueryPool->_ids.clear(); + vkCmdCopyQueryPoolResults(_gpuCommandBuffer->vkCommandBuffer, + gpuQueryPool->vkPool, first, count, + gpuBuffer->vkBuffer, gpuBuffer->getStartOffset(CCVKDevice::getInstance()->gpuDevice()->curBackBufferIndex) + offset, stride, + VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); } void CCVKCommandBuffer::customCommand(CustomCommand &&cmd) { diff --git a/native/cocos/renderer/gfx-vulkan/VKCommandBuffer.h b/native/cocos/renderer/gfx-vulkan/VKCommandBuffer.h index 55adb5a8e57..ac7798b1ddd 100644 --- a/native/cocos/renderer/gfx-vulkan/VKCommandBuffer.h +++ b/native/cocos/renderer/gfx-vulkan/VKCommandBuffer.h @@ -67,7 +67,9 @@ class CC_VULKAN_API CCVKCommandBuffer final : public CommandBuffer { void pipelineBarrier(const GeneralBarrier *barrier, const BufferBarrier *const *bufferBarriers, const Buffer *const *buffers, uint32_t bufferBarrierCount, const TextureBarrier *const *textureBarriers, const Texture *const *textures, uint32_t textureBarrierCount) override; void beginQuery(QueryPool *queryPool, uint32_t id) override; void endQuery(QueryPool *queryPool, uint32_t id) override; - void resetQueryPool(QueryPool *queryPool) override; + void resetQueryPool(QueryPool *queryPool, uint32_t first, uint32_t count) override; + void writeTimestamp(QueryPool *queryPool, uint32_t id) override; + void copyQueryResult(QueryPool *queryPool, Buffer* buffer, uint32_t offset, uint32_t stride, uint32_t first, uint32_t count) override; void customCommand(CustomCommand &&cmd) override; protected: diff --git a/native/cocos/renderer/gfx-vulkan/VKCommands.cpp b/native/cocos/renderer/gfx-vulkan/VKCommands.cpp index 0468624dd53..cf33ecf50d5 100644 --- a/native/cocos/renderer/gfx-vulkan/VKCommands.cpp +++ b/native/cocos/renderer/gfx-vulkan/VKCommands.cpp @@ -112,6 +112,11 @@ void cmdFuncCCVKCreateQueryPool(CCVKDevice *device, CCVKGPUQueryPool *gpuQueryPo queryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; queryPoolInfo.queryType = mapVkQueryType(gpuQueryPool->type); queryPoolInfo.queryCount = gpuQueryPool->maxQueryObjects; + + if (queryPoolInfo.queryType == VK_QUERY_TYPE_PIPELINE_STATISTICS) { + mapVKPipelineStatisticFlags(gpuQueryPool->psFlags, queryPoolInfo.pipelineStatistics); + } + VK_CHECK(vkCreateQueryPool(device->gpuDevice()->vkDevice, &queryPoolInfo, nullptr, &gpuQueryPool->vkPool)); } @@ -1220,6 +1225,17 @@ void bufferUpload(const CCVKGPUBufferView &stagingBuffer, CCVKGPUBuffer &gpuBuff }; } // namespace +void cmdFuncCCVKReadBackBuffer(CCVKDevice *device, CCVKGPUBuffer *gpuBuffer, void *dst, uint32_t offset, uint32_t size) { + if (!gpuBuffer || gpuBuffer->mappedData == nullptr) return; + + auto curBackBufferIndex = device->gpuDevice()->curBackBufferIndex; + auto backBufferCount = device->gpuDevice()->backBufferCount; + uint32_t lastBufferIndex = (device->gpuDevice()->curBackBufferIndex + backBufferCount - 1) % backBufferCount; + + uint8_t *src = gpuBuffer->mappedData + lastBufferIndex * gpuBuffer->instanceSize + offset; + memcpy(dst, src, size); +} + void cmdFuncCCVKUpdateBuffer(CCVKDevice *device, CCVKGPUBuffer *gpuBuffer, const void *buffer, uint32_t size, const CCVKGPUCommandBuffer *cmdBuffer) { if (!gpuBuffer) return; diff --git a/native/cocos/renderer/gfx-vulkan/VKCommands.h b/native/cocos/renderer/gfx-vulkan/VKCommands.h index 06b6cfe881b..5388585d5eb 100644 --- a/native/cocos/renderer/gfx-vulkan/VKCommands.h +++ b/native/cocos/renderer/gfx-vulkan/VKCommands.h @@ -46,6 +46,7 @@ void cmdFuncCCVKCreateGraphicsPipelineState(CCVKDevice *device, CCVKGPUPipelineS void cmdFuncCCVKCreateComputePipelineState(CCVKDevice *device, CCVKGPUPipelineState *gpuPipelineState); void cmdFuncCCVKCreateGeneralBarrier(CCVKDevice *device, CCVKGPUGeneralBarrier *gpuGeneralBarrier); +void cmdFuncCCVKReadBackBuffer(CCVKDevice *device, CCVKGPUBuffer *gpuBuffer, void *dst, uint32_t offset, uint32_t size); void cmdFuncCCVKUpdateBuffer(CCVKDevice *device, CCVKGPUBuffer *gpuBuffer, const void *buffer, uint32_t size, const CCVKGPUCommandBuffer *cmdBuffer = nullptr); void cmdFuncCCVKCopyBuffersToTexture(CCVKDevice *device, const uint8_t *const *buffers, CCVKGPUTexture *gpuTexture, const BufferTextureCopy *regions, uint32_t count, const CCVKGPUCommandBuffer *gpuCommandBuffer); void cmdFuncCCVKCopyTextureToBuffers(CCVKDevice *device, CCVKGPUTexture *srcTexture, CCVKGPUBufferView *destBuffer, const BufferTextureCopy *regions, uint32_t count, const CCVKGPUCommandBuffer *gpuCommandBuffer); diff --git a/native/cocos/renderer/gfx-vulkan/VKDevice.cpp b/native/cocos/renderer/gfx-vulkan/VKDevice.cpp index ce3e89b09dc..95824042af0 100644 --- a/native/cocos/renderer/gfx-vulkan/VKDevice.cpp +++ b/native/cocos/renderer/gfx-vulkan/VKDevice.cpp @@ -329,6 +329,8 @@ bool CCVKDevice::doInit(const DeviceInfo & /*info*/) { _caps.maxComputeWorkGroupInvocations = limits.maxComputeWorkGroupInvocations; _caps.maxComputeWorkGroupCount = {limits.maxComputeWorkGroupCount[0], limits.maxComputeWorkGroupCount[1], limits.maxComputeWorkGroupCount[2]}; _caps.maxComputeWorkGroupSize = {limits.maxComputeWorkGroupSize[0], limits.maxComputeWorkGroupSize[1], limits.maxComputeWorkGroupSize[2]}; + // time query + _caps.timestampPeriod = limits.timestampPeriod; #if defined(VK_USE_PLATFORM_ANDROID_KHR) // UNASSIGNED-BestPractices-vkCreateComputePipelines-compute-work-group-size _caps.maxComputeWorkGroupInvocations = std::min(_caps.maxComputeWorkGroupInvocations, 64U); @@ -341,7 +343,7 @@ bool CCVKDevice::doInit(const DeviceInfo & /*info*/) { queueInfo.type = QueueType::GRAPHICS; _queue = createQueue(queueInfo); - QueryPoolInfo queryPoolInfo{QueryType::OCCLUSION, DEFAULT_MAX_QUERY_OBJECTS, false}; + QueryPoolInfo queryPoolInfo{QueryType::OCCLUSION, DEFAULT_MAX_QUERY_OBJECTS}; _queryPool = createQueryPool(queryPoolInfo); CommandBufferInfo cmdBuffInfo; @@ -954,47 +956,51 @@ void CCVKDevice::copyTextureToBuffers(Texture *srcTexture, uint8_t *const *buffe void CCVKDevice::getQueryPoolResults(QueryPool *queryPool) { CC_PROFILE(CCVKDeviceGetQueryPoolResults); - auto *vkQueryPool = static_cast(queryPool); - auto queryCount = static_cast(vkQueryPool->_ids.size()); - CC_ASSERT(queryCount <= vkQueryPool->getMaxQueryObjects()); - - const bool bWait = queryPool->getForceWait(); - uint32_t width = bWait ? 1U : 2U; - uint64_t stride = sizeof(uint64_t) * width; - VkQueryResultFlagBits flag = bWait ? VK_QUERY_RESULT_WAIT_BIT : VK_QUERY_RESULT_WITH_AVAILABILITY_BIT; - ccstd::vector results(queryCount * width, 0); - - if (queryCount > 0U) { - VkResult result = vkGetQueryPoolResults( - gpuDevice()->vkDevice, - vkQueryPool->_gpuQueryPool->vkPool, - 0, - queryCount, - static_cast(queryCount * stride), - results.data(), - stride, - VK_QUERY_RESULT_64_BIT | flag); - CC_ASSERT(result == VK_SUCCESS || result == VK_NOT_READY); - } - - ccstd::unordered_map mapResults; - for (auto queryId = 0U; queryId < queryCount; queryId++) { - uint32_t offset = queryId * width; - if (bWait || results[offset + 1] > 0) { - uint32_t id = vkQueryPool->_ids[queryId]; - auto iter = mapResults.find(id); - if (iter != mapResults.end()) { - iter->second += results[offset]; - } else { - mapResults[id] = results[offset]; - } - } - } - { - std::lock_guard lock(vkQueryPool->_mutex); - vkQueryPool->_results = std::move(mapResults); - } + // There are 3-4 inflight command buffers, which may not lead to desirable outcomes for occlusion queries. + std::ignore = queryPool; + +// auto *vkQueryPool = static_cast(queryPool); +// auto queryCount = static_cast(vkQueryPool->_ids.size()); +// CC_ASSERT(queryCount <= vkQueryPool->getMaxQueryObjects()); +// +// const bool bWait = queryPool->getForceWait(); +// uint32_t width = bWait ? 1U : 2U; +// uint64_t stride = sizeof(uint64_t) * width; +// VkQueryResultFlagBits flag = bWait ? VK_QUERY_RESULT_WAIT_BIT : VK_QUERY_RESULT_WITH_AVAILABILITY_BIT; +// ccstd::vector results(queryCount * width, 0); +// +// if (queryCount > 0U) { +// VkResult result = vkGetQueryPoolResults( +// gpuDevice()->vkDevice, +// vkQueryPool->_gpuQueryPool->vkPool, +// 0, +// queryCount, +// static_cast(queryCount * stride), +// results.data(), +// stride, +// VK_QUERY_RESULT_64_BIT | flag); +// CC_ASSERT(result == VK_SUCCESS || result == VK_NOT_READY); +// } +// +// ccstd::unordered_map mapResults; +// for (auto queryId = 0U; queryId < queryCount; queryId++) { +// uint32_t offset = queryId * width; +// if (bWait || results[offset + 1] > 0) { +// uint32_t id = vkQueryPool->_ids[queryId]; +// auto iter = mapResults.find(id); +// if (iter != mapResults.end()) { +// iter->second += results[offset]; +// } else { +// mapResults[id] = results[offset]; +// } +// } +// } +// +// { +// std::lock_guard lock(vkQueryPool->_mutex); +// vkQueryPool->_results = std::move(mapResults); +// } } //////////////////////////// Function Fallbacks ///////////////////////////////////////// @@ -1116,5 +1122,27 @@ SampleCount CCVKDevice::getMaxSampleCount(Format format, TextureUsage usage, Tex return SampleCount::X1; } +uint32_t CCVKDevice::getSupportedPipelineStatisticFlags(const PipelineStatisticFlags &flags, PipelineStatisticFlags &outFlags) const { + static constexpr PipelineStatisticFlagBit SUPPORTED_FLAGS[] = { + PipelineStatisticFlagBit::IA_VERTICES, + PipelineStatisticFlagBit::IA_PRIMITIVES, + PipelineStatisticFlagBit::VS_INVOCATIONS, + PipelineStatisticFlagBit::CLIP_INVOCATIONS, + PipelineStatisticFlagBit::CLIP_PRIMITIVES, + PipelineStatisticFlagBit::FS_INVOCATIONS, + PipelineStatisticFlagBit::CS_INVOCATIONS, + }; + uint32_t count = 0; + outFlags = PipelineStatisticFlagBit {0}; + + for (const auto &support : SUPPORTED_FLAGS) { + if (hasFlag(flags, support)) { + outFlags |= support; + ++count; + } + } + return count; +} + } // namespace gfx } // namespace cc diff --git a/native/cocos/renderer/gfx-vulkan/VKDevice.h b/native/cocos/renderer/gfx-vulkan/VKDevice.h index 4d7d9c938a8..e88f0c1aea1 100644 --- a/native/cocos/renderer/gfx-vulkan/VKDevice.h +++ b/native/cocos/renderer/gfx-vulkan/VKDevice.h @@ -106,6 +106,7 @@ class CC_VULKAN_API CCVKDevice final : public Device { void updateBackBufferCount(uint32_t backBufferCount); SampleCount getMaxSampleCount(Format format, TextureUsage usage, TextureFlags flags) const override; + uint32_t getSupportedPipelineStatisticFlags(const PipelineStatisticFlags &flags, PipelineStatisticFlags &outFlags) const override; protected: static CCVKDevice *instance; 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/gfx-vulkan/VKGPUObjects.h b/native/cocos/renderer/gfx-vulkan/VKGPUObjects.h index 71c4e9cbd2b..27eb3ddf934 100644 --- a/native/cocos/renderer/gfx-vulkan/VKGPUObjects.h +++ b/native/cocos/renderer/gfx-vulkan/VKGPUObjects.h @@ -315,7 +315,7 @@ struct CCVKGPUQueryPool : public CCVKGPUDeviceObject { QueryType type{QueryType::OCCLUSION}; uint32_t maxQueryObjects{0}; - bool forceWait{true}; + PipelineStatisticFlags psFlags{0}; VkQueryPool vkPool{VK_NULL_HANDLE}; }; diff --git a/native/cocos/renderer/gfx-vulkan/VKQueryPool.cpp b/native/cocos/renderer/gfx-vulkan/VKQueryPool.cpp index 266e8eb205b..c431b5901ab 100644 --- a/native/cocos/renderer/gfx-vulkan/VKQueryPool.cpp +++ b/native/cocos/renderer/gfx-vulkan/VKQueryPool.cpp @@ -43,7 +43,7 @@ void CCVKQueryPool::doInit(const QueryPoolInfo & /*info*/) { _gpuQueryPool = ccnew CCVKGPUQueryPool; _gpuQueryPool->type = _type; _gpuQueryPool->maxQueryObjects = _maxQueryObjects; - _gpuQueryPool->forceWait = _forceWait; + _gpuQueryPool->psFlags = _psFlags; cmdFuncCCVKCreateQueryPool(device, _gpuQueryPool); } diff --git a/native/cocos/renderer/gfx-vulkan/VKQueryPool.h b/native/cocos/renderer/gfx-vulkan/VKQueryPool.h index 1333e4f8e7d..4c64cdc3d58 100644 --- a/native/cocos/renderer/gfx-vulkan/VKQueryPool.h +++ b/native/cocos/renderer/gfx-vulkan/VKQueryPool.h @@ -47,7 +47,6 @@ class CC_VULKAN_API CCVKQueryPool final : public QueryPool { void doDestroy() override; IntrusivePtr _gpuQueryPool; - ccstd::vector _ids; }; } // namespace gfx diff --git a/native/cocos/renderer/gfx-vulkan/VKUtils.cpp b/native/cocos/renderer/gfx-vulkan/VKUtils.cpp index 4d9c78dfc6e..5db047dc367 100644 --- a/native/cocos/renderer/gfx-vulkan/VKUtils.cpp +++ b/native/cocos/renderer/gfx-vulkan/VKUtils.cpp @@ -40,6 +40,30 @@ VkQueryType mapVkQueryType(QueryType type) { } } +void mapVKPipelineStatisticFlags(PipelineStatisticFlags flags, VkQueryPipelineStatisticFlags &vkFlags) { + if (hasFlag(flags, PipelineStatisticFlagBit::IA_VERTICES)) { + vkFlags |= VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT; + } + if (hasFlag(flags, PipelineStatisticFlagBit::IA_PRIMITIVES)) { + vkFlags |= VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT; + } + if (hasFlag(flags, PipelineStatisticFlagBit::VS_INVOCATIONS)) { + vkFlags |= VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT; + } + if (hasFlag(flags, PipelineStatisticFlagBit::CLIP_INVOCATIONS)) { + vkFlags |= VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT; + } + if (hasFlag(flags, PipelineStatisticFlagBit::CLIP_PRIMITIVES)) { + vkFlags |= VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT; + } + if (hasFlag(flags, PipelineStatisticFlagBit::FS_INVOCATIONS)) { + vkFlags |= VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT; + } + if (hasFlag(flags, PipelineStatisticFlagBit::CS_INVOCATIONS)) { + vkFlags |= VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT; + } +} + VkFormat mapVkFormat(Format format, const CCVKGPUDevice *gpuDevice) { switch (format) { case Format::R8: return VK_FORMAT_R8_UNORM; diff --git a/native/cocos/renderer/gfx-vulkan/VKUtils.h b/native/cocos/renderer/gfx-vulkan/VKUtils.h index 26bf88d35ea..43fe9517bdd 100644 --- a/native/cocos/renderer/gfx-vulkan/VKUtils.h +++ b/native/cocos/renderer/gfx-vulkan/VKUtils.h @@ -69,6 +69,7 @@ VkShaderStageFlagBits mapVkShaderStageFlagBits(ShaderStageFlagBit stage); VkShaderStageFlags mapVkShaderStageFlags(ShaderStageFlagBit stages); SurfaceTransform mapSurfaceTransform(VkSurfaceTransformFlagBitsKHR transform); ccstd::string mapVendorName(uint32_t vendorID); +void mapVKPipelineStatisticFlags(PipelineStatisticFlags flags, VkQueryPipelineStatisticFlags &vkFlags); void fullPipelineBarrier(VkCommandBuffer cmdBuff); const ThsvsAccessType *getAccessType(AccessFlagBit flag); diff --git a/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp b/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp index bacbc8a90a6..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 { @@ -787,6 +789,8 @@ void submitProfilerCommands( cmdBuff->bindDescriptorSet(static_cast(pipeline::SetIndex::LOCAL), submodel->getDescriptorSet()); cmdBuff->bindInputAssembler(ia); cmdBuff->draw(ia); + + ctx.context.pipelineProfiler.render(ctx.currentPass, ctx.subpassIndex, ctx.cmdBuff); } const PmrTransparentMap>& @@ -1126,6 +1130,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { void begin(const RasterPass& pass, RenderGraph::vertex_descriptor vertID) const { #if CC_DEBUG ctx.cmdBuff->beginMarker(makeMarkerInfo(get(RenderGraph::NameTag{}, ctx.g, vertID).c_str(), RASTER_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()) { @@ -1167,7 +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 - +#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; @@ -1188,6 +1197,12 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { // noop } void begin(const ComputeSubpass& subpass, RenderGraph::vertex_descriptor vertID) const { // NOLINT(readability-convert-member-functions-to-static) +#if CC_DEBUG + ctx.cmdBuff->insertMarker(makeMarkerInfo(get(RenderGraph::NameTag{}, ctx.g, vertID).c_str(), RASTER_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& subpasses = ctx.ppl->custom.computeSubpasses; @@ -1206,7 +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 - +#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; @@ -1358,7 +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 - +#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; @@ -1398,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; @@ -1474,14 +1499,15 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { return; } } - if (pass.showStatistics) { submitProfilerCommands(ctx, vertID, pass); } ctx.cmdBuff->endRenderPass(); ctx.currentPass = nullptr; ctx.currentPassLayoutID = LayoutGraphData::null_vertex(); - +#if CC_PIPELINE_PROFILER + ctx.context.pipelineProfiler.endScope(ctx.cmdBuff, vertID); +#endif #if CC_DEBUG ctx.cmdBuff->endMarker(); #endif @@ -1501,6 +1527,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { std::ignore = vertID; ctx.subpassIndex = 0; // noop +#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); @@ -1516,6 +1545,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { std::ignore = subpass; std::ignore = vertID; // noop +#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); @@ -1527,6 +1559,9 @@ 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 @@ -1554,6 +1589,9 @@ 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 @@ -1571,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 { @@ -1887,6 +1931,26 @@ struct CommandSubmitter { gfx::CommandBuffer* primaryCommandBuffer = nullptr; }; +struct PipelineStatisticsRecorder { + PipelineStatisticsRecorder(NativePipeline& pipeline, const std::vector& cmdBuffers, uint32_t passCount) + : context(pipeline.nativeContext) { + CC_EXPECTS(cmdBuffers.size() == 1); + primaryCommandBuffer = cmdBuffers.at(0); + context.pipelineProfiler.resolveData(pipeline); + context.pipelineProfiler.beginFrame(passCount, primaryCommandBuffer); + } + + ~PipelineStatisticsRecorder() { + context.pipelineProfiler.endFrame(primaryCommandBuffer); + } + + PipelineStatisticsRecorder(const PipelineStatisticsRecorder &) = delete; + PipelineStatisticsRecorder &operator=(const PipelineStatisticsRecorder &) = delete; + + NativeRenderContext& context; + gfx::CommandBuffer* primaryCommandBuffer = nullptr; +}; + void extendResourceLifetime(const NativeRenderQueue& queue, ResourceGroup& group) { // keep instanceBuffers for (const auto& batch : queue.opaqueInstancingQueue.sortedBatches) { @@ -2004,6 +2068,7 @@ void NativePipeline::executeRenderGraph(const RenderGraph& rg) { fg(graphView, boost::keep_all{}, RenderGraphFilter{&validPasses}); CommandSubmitter submit(ppl.device, ppl.getCommandBuffers()); + PipelineStatisticsRecorder pipelineStatisticsRecorder(ppl, ppl.getCommandBuffers(), num_vertices(rg)); // upload buffers { diff --git a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h index 2f5704b28f8..35a079b4cb3 100644 --- a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h +++ b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h @@ -41,6 +41,7 @@ #include "cocos/renderer/pipeline/custom/NativeTypes.h" #include "cocos/renderer/pipeline/custom/details/Map.h" #include "cocos/renderer/pipeline/custom/details/Set.h" +#include "cocos/renderer/pipeline/profile/PipelineProfiler.h" #ifdef _MSC_VER #pragma warning(push) @@ -1245,6 +1246,7 @@ struct NativeRenderContext { ccstd::pmr::unordered_map renderSceneResources; QuadResource fullscreenQuad; SceneCulling sceneCulling; + PipelineProfiler pipelineProfiler; }; class NativeProgramLibrary final : public ProgramLibrary { diff --git a/native/cocos/renderer/pipeline/deferred/DeferredPipeline.cpp b/native/cocos/renderer/pipeline/deferred/DeferredPipeline.cpp index 3728c25bb88..eb8ee533868 100644 --- a/native/cocos/renderer/pipeline/deferred/DeferredPipeline.cpp +++ b/native/cocos/renderer/pipeline/deferred/DeferredPipeline.cpp @@ -118,7 +118,7 @@ void DeferredPipeline::render(const ccstd::vector &cameras) { _commandBuffers[0]->begin(); if (enableOcclusionQuery) { - _commandBuffers[0]->resetQueryPool(_queryPools[0]); + _commandBuffers[0]->resetQueryPool(_queryPools[0], 0, _queryPools[0]->getMaxQueryObjects()); } _pipelineUBO->updateMultiCameraUBO(_globalDSManager, cameras); diff --git a/native/cocos/renderer/pipeline/deferred/PostProcessStage.cpp b/native/cocos/renderer/pipeline/deferred/PostProcessStage.cpp index dbe407383d1..a76b810b2b3 100644 --- a/native/cocos/renderer/pipeline/deferred/PostProcessStage.cpp +++ b/native/cocos/renderer/pipeline/deferred/PostProcessStage.cpp @@ -228,7 +228,7 @@ void PostProcessStage::render(scene::Camera *camera) { _uiPhase->render(camera, renderPass); renderProfiler(renderPass, cmdBuff, pipeline->getProfiler(), camera); #if CC_USE_DEBUG_RENDERER - renderDebugRenderer(renderPass, cmdBuff, pipeline->getPipelineSceneData(), camera); + renderDebugRenderer(renderPass, cmdBuff, camera); #endif }; diff --git a/native/cocos/renderer/pipeline/forward/ForwardPipeline.cpp b/native/cocos/renderer/pipeline/forward/ForwardPipeline.cpp index 9257e4b26be..c8f26848e2e 100644 --- a/native/cocos/renderer/pipeline/forward/ForwardPipeline.cpp +++ b/native/cocos/renderer/pipeline/forward/ForwardPipeline.cpp @@ -107,7 +107,7 @@ void ForwardPipeline::render(const ccstd::vector &cameras) { _commandBuffers[0]->begin(); if (enableOcclusionQuery) { - _commandBuffers[0]->resetQueryPool(_queryPools[0]); + _commandBuffers[0]->resetQueryPool(_queryPools[0], 0, _queryPools[0]->getMaxQueryObjects()); } _pipelineUBO->updateMultiCameraUBO(_globalDSManager, cameras); diff --git a/native/cocos/renderer/pipeline/forward/ForwardStage.cpp b/native/cocos/renderer/pipeline/forward/ForwardStage.cpp index 2a96e05caf2..8ee3d66254e 100644 --- a/native/cocos/renderer/pipeline/forward/ForwardStage.cpp +++ b/native/cocos/renderer/pipeline/forward/ForwardStage.cpp @@ -269,7 +269,7 @@ void ForwardStage::render(scene::Camera *camera) { _uiPhase->render(camera, renderPass); renderProfiler(renderPass, cmdBuff, _pipeline->getProfiler(), camera); #if CC_USE_DEBUG_RENDERER - renderDebugRenderer(renderPass, cmdBuff, _pipeline->getPipelineSceneData(), camera); + renderDebugRenderer(renderPass, cmdBuff, camera); #endif }; diff --git a/native/cocos/renderer/pipeline/helper/Utils.cpp b/native/cocos/renderer/pipeline/helper/Utils.cpp index 3c224906b3a..3b75efc0452 100644 --- a/native/cocos/renderer/pipeline/helper/Utils.cpp +++ b/native/cocos/renderer/pipeline/helper/Utils.cpp @@ -85,12 +85,12 @@ void renderProfiler(gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuff, sc } #if CC_USE_DEBUG_RENDERER -void renderDebugRenderer(gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuff, PipelineSceneData *sceneData, const scene::Camera *camera) { +void renderDebugRenderer(gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuff, const scene::Camera *camera) { if (camera != profilerCamera) { return; } - CC_DEBUG_RENDERER->render(renderPass, cmdBuff, sceneData); + CC_DEBUG_RENDERER->render(renderPass, cmdBuff); } #endif diff --git a/native/cocos/renderer/pipeline/helper/Utils.h b/native/cocos/renderer/pipeline/helper/Utils.h index 324fa01845b..21883c28cdc 100644 --- a/native/cocos/renderer/pipeline/helper/Utils.h +++ b/native/cocos/renderer/pipeline/helper/Utils.h @@ -71,7 +71,7 @@ extern const scene::Camera *profilerCamera; void decideProfilerCamera(const ccstd::vector &cameras); void renderProfiler(gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuff, scene::Model *profiler, const scene::Camera *camera); #if CC_USE_DEBUG_RENDERER -void renderDebugRenderer(gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuff, PipelineSceneData *sceneData, const scene::Camera *camera); +void renderDebugRenderer(gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuff, const scene::Camera *camera); #endif } // namespace pipeline 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 new file mode 100644 index 00000000000..f293fec04d8 --- /dev/null +++ b/native/cocos/renderer/pipeline/profile/GPUTimeQuery.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** + 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 "GPUTimeQuery.h" +#include "gfx-base/GFXDevice.h" + +namespace cc::render { + +static constexpr uint32_t MAX_FRAME_INFLIGHT = 2; // from device agent + +const uint64_t* GPUTimeQuery::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 * _capacity * sizeof(uint64_t)); +} + +void GPUTimeQuery::resize(uint32_t size) { + if (size <= _capacity) { + return; + } + + _capacity = size; + _results.resize(static_cast(_capacity) * MAX_FRAME_INFLIGHT); + _keys.resize(_capacity); + + auto *device = gfx::Device::getInstance(); + gfx::QueryPoolInfo poolInfo = {}; + poolInfo.type = gfx::QueryType::TIMESTAMP; + poolInfo.maxQueryObjects = _capacity; + _queryPool = device->createQueryPool(poolInfo); + + gfx::BufferInfo bufferInfo = {}; + bufferInfo.usage = gfx::BufferUsageBit::TRANSFER_DST; + bufferInfo.memUsage = gfx::MemoryUsageBit::HOST | gfx::MemoryUsageBit::DEVICE; + bufferInfo.size = _capacity * sizeof(uint64_t); + bufferInfo.stride = sizeof(uint64_t); + _readBackBuffer = device->createBuffer(bufferInfo); +} + +void GPUTimeQuery::reset(gfx::CommandBuffer *cmdBuffer) { + cmdBuffer->resetQueryPool(_queryPool, 0, _capacity); + _count = 0; +} + +void GPUTimeQuery::writeTimestamp(gfx::CommandBuffer *cmdBuffer) { + cmdBuffer->writeTimestamp(_queryPool, _count); + ++_count; +} + +void GPUTimeQuery::writeTimestampWithKey(gfx::CommandBuffer *cmdBuffer, const KeyType &key) { + cmdBuffer->writeTimestamp(_queryPool, _count); + _keys[_count] = key; + ++_count; +} + +void GPUTimeQuery::copyResult(gfx::CommandBuffer *cmdBuffer) { + cmdBuffer->copyQueryResult(_queryPool, _readBackBuffer, 0, sizeof(uint64_t), 0, _count); + _frameIndex = (_frameIndex + 1) % MAX_FRAME_INFLIGHT; + + auto* ptr = reinterpret_cast(_results.data()); + uint8_t* writeBuffer = ptr + static_cast(_frameIndex) * _capacity * sizeof(uint64_t); + _readBackBuffer->readBack(writeBuffer, 0, _capacity * sizeof(uint64_t)); +} + +} // namespace cc::render diff --git a/native/cocos/renderer/pipeline/profile/GPUTimeQuery.h b/native/cocos/renderer/pipeline/profile/GPUTimeQuery.h new file mode 100644 index 00000000000..f96ba51025b --- /dev/null +++ b/native/cocos/renderer/pipeline/profile/GPUTimeQuery.h @@ -0,0 +1,71 @@ +/**************************************************************************** + 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 { + +class GPUTimeQuery { +public: + GPUTimeQuery() = default; + ~GPUTimeQuery() = default; + + using KeyType = ccstd::variant; + + void resize(uint32_t size); + + void reset(gfx::CommandBuffer *cmdBuffer); + void writeTimestamp(gfx::CommandBuffer *cmdBuffer); + void writeTimestampWithKey(gfx::CommandBuffer *cmdBuffer, const KeyType &key); + + void copyResult(gfx::CommandBuffer *cmdBuffer); + + template + void foreachData(Func &&func) const { + const uint64_t *data = getReadBuffer(); + for (uint32_t i = 0; i < _count; ++i) { + func(_keys[i], data[i]); + } + } + +private: + const uint64_t *getReadBuffer() const; + + uint32_t _capacity = 0; + uint32_t _count = 0; + uint32_t _frameIndex = 0; + ccstd::vector _keys; + ccstd::vector _results; + IntrusivePtr _queryPool; + IntrusivePtr _readBackBuffer; +}; + +} // namespace cc::render diff --git a/native/cocos/renderer/pipeline/profile/PipelineProfiler.cpp b/native/cocos/renderer/pipeline/profile/PipelineProfiler.cpp new file mode 100644 index 00000000000..1615c8a6087 --- /dev/null +++ b/native/cocos/renderer/pipeline/profile/PipelineProfiler.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** + 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 "PipelineProfiler.h" +#include "base/StringUtil.h" +#include "math/MathUtil.h" +#include "application/ApplicationManager.h" +#include "platform/interfaces/modules/Device.h" +#include "platform/interfaces/modules/ISystemWindow.h" +#include "platform/interfaces/modules/ISystemWindowManager.h" + +#include "gfx-base/GFXDevice.h" +#include "profiler/DebugRenderer.h" + +#include "cocos/renderer/pipeline/custom/NativePipelineTypes.h" +#include "cocos/renderer/pipeline/custom/RenderGraphGraphs.h" + + + +namespace cc::render { + +PipelineProfiler::PipelineProfiler() { +#if CC_USE_DEBUG_RENDERER + auto debugInfo = DebugRendererInfo(); + debugInfo.fontSize = 15U; + + auto *device = gfx::Device::getInstance(); + const auto *window = CC_GET_MAIN_SYSTEM_WINDOW(); + const auto &ext = window->getViewSize(); + const auto width = ext.width * Device::getDevicePixelRatio(); + const auto height = ext.height * Device::getDevicePixelRatio(); + auto fontSize = static_cast(width / 800.0F * static_cast(debugInfo.fontSize)); + fontSize = fontSize < 10U ? 10U : (fontSize > 20U ? 20U : fontSize); + + _textRenderer = std::make_unique(); + _textRenderer->initialize(device, debugInfo, fontSize, "internal/builtin-debug-renderer"); + _textRenderer->updateWindowSize(static_cast(width), static_cast(height), 0, device->getCombineSignY()); +#endif +} + +void PipelineProfiler::beginFrame(uint32_t passCount, gfx::CommandBuffer *cmdBuffer) { + _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::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) { +#if CC_USE_DEBUG_RENDERER + _textRenderer->updateTextData(); + _textRenderer->render(renderPass, subPassId, cmdBuff); +#endif +} + +void PipelineProfiler::resolveData(NativePipeline &pipeline) { + const auto ×tampPeriod = pipeline.device->getCapabilities().timestampPeriod; + std::vector> stack; + _timeQuery.foreachData([&](const auto &key, uint64_t v) { + auto passID = ccstd::get(key); + if (stack.empty() || stack.back().first != passID) { + stack.emplace_back(passID, v); + } else { + _passTimes.emplace(passID, (v - stack.back().second) * timestampPeriod); + 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, 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; + + 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); + + float levelOffset = 0; + visitObject( + passID, pipeline.renderGraph, + [&](const RenderQueue &queue) { + std::ignore = queue; + levelOffset = 8; + }, + [&](const auto&v) { + std::ignore = v; + }); + + _textRenderer->addText(StringUtil::format("%s", name.c_str()), {baseOffset + levelOffset, 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 +} + + +} // namespace cc::render diff --git a/native/cocos/renderer/pipeline/profile/PipelineProfiler.h b/native/cocos/renderer/pipeline/profile/PipelineProfiler.h new file mode 100644 index 00000000000..f9db6fc4ca3 --- /dev/null +++ b/native/cocos/renderer/pipeline/profile/PipelineProfiler.h @@ -0,0 +1,64 @@ +/**************************************************************************** + 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 "base/std/container/map.h" +#include "GPUTimeQuery.h" +#include "GPUStatisticsQuery.h" +#include "profiler/DebugRenderer.h" +#include "core/assets/Material.h" +#include "scene/Pass.h" + +namespace cc::render { +struct NativePipeline; + +class PipelineProfiler { +public: + PipelineProfiler(); + ~PipelineProfiler() = default; + + void beginFrame(uint32_t passCount, gfx::CommandBuffer *cmdBuffer); + void endFrame(gfx::CommandBuffer *cmdBuffer); + void resolveData(NativePipeline &pipeline); + + 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; +#endif +}; + +} // namespace cc::render diff --git a/native/cocos/scene/Pass.cpp b/native/cocos/scene/Pass.cpp index cc57e887ed1..6b9e678337f 100644 --- a/native/cocos/scene/Pass.cpp +++ b/native/cocos/scene/Pass.cpp @@ -442,10 +442,6 @@ void Pass::resetTextures() { } bool Pass::tryCompile() { - if (_root->getPipeline() == nullptr) { - return false; - } - syncBatchingScheme(); auto *programLib = render::getProgramLibrary(); if (programLib) { @@ -456,7 +452,7 @@ bool Pass::tryCompile() { } _shader = shaderProxy->getShader(); _pipelineLayout = programLib->getPipelineLayout(_device, _phaseID, _programName); - } else { + } else if (_root->getPipeline() != nullptr) { auto *shader = ProgramLib::getInstance()->getGFXShader(_device, _programName, _defines, _root->getPipeline()); if (!shader) { CC_LOG_WARNING("create shader %s failed", _programName.c_str()); @@ -464,6 +460,8 @@ bool Pass::tryCompile() { } _shader = shader; _pipelineLayout = ProgramLib::getInstance()->getTemplateInfo(_programName)->pipelineLayout; + } else { + return false; } _hash = Pass::getPassHash(this); diff --git a/native/tools/swig-config/cocos.i b/native/tools/swig-config/cocos.i index ad4ef230454..aa1c36922bd 100644 --- a/native/tools/swig-config/cocos.i +++ b/native/tools/swig-config/cocos.i @@ -92,6 +92,8 @@ namespace cc { %ignore SAXParser::endElement; %ignore SAXParser::textHandler; +%ignore TextRenderer; + %ignore DebugRenderer::activate; %ignore DebugRenderer::render; %ignore DebugRenderer::destroy;