diff --git a/cocos/3d/framework/mesh-renderer.ts b/cocos/3d/framework/mesh-renderer.ts index 142fd459264..ff28d56222d 100644 --- a/cocos/3d/framework/mesh-renderer.ts +++ b/cocos/3d/framework/mesh-renderer.ts @@ -312,6 +312,9 @@ export class MeshRenderer extends ModelRenderer { @serializable protected _mesh: Mesh | null = null; + @serializable + protected _gpuDrivenEnabled: boolean = true; + @serializable protected _shadowCastingMode = ModelShadowCastingMode.OFF; @@ -456,7 +459,7 @@ export class MeshRenderer extends ModelRenderer { * 注意,设置时,所有形变目标的权重都将归零。 */ @type(Mesh) - @displayOrder(1) + @displayOrder(0) get mesh (): Mesh | null { return this._mesh; } @@ -479,6 +482,21 @@ export class MeshRenderer extends ModelRenderer { this._updateReceiveDirLight(); } + /** + * @en Whether to enable GPU Driven. + * @zh 是否开启 GPU Driven 。 + */ + @editable + @displayOrder(1) + get gpuDrivenEnabled (): boolean { + return this._gpuDrivenEnabled; + } + + set gpuDrivenEnabled (val) { + this._gpuDrivenEnabled = val; + this._updateGPUDrivenEnabled(); + } + /** * @en Gets the model in [[RenderScene]]. * @zh 获取渲染场景 [[RenderScene]] 中对应的模型。 @@ -567,6 +585,7 @@ export class MeshRenderer extends ModelRenderer { this._updateUseReflectionProbe(); this._updateReceiveDirLight(); this._updateStandardSkin(); + this._updateGPUDrivenEnabled(); } // Redo, Undo, Prefab restore, etc. @@ -584,6 +603,7 @@ export class MeshRenderer extends ModelRenderer { this._updateUseReflectionProbe(); this._updateReceiveDirLight(); this._updateStandardSkin(); + this._updateGPUDrivenEnabled(); } public onEnable (): void { @@ -610,6 +630,7 @@ export class MeshRenderer extends ModelRenderer { this._onUpdateReflectionProbeDataMap(); this._onUpdateLocalReflectionProbeData(); this._updateStandardSkin(); + this._updateGPUDrivenEnabled(); this._attachToScene(); } @@ -916,6 +937,7 @@ export class MeshRenderer extends ModelRenderer { this._updateReceiveDirLight(); this._onUpdateReflectionProbeDataMap(); this._onUpdateLocalReflectionProbeData(); + this._updateGPUDrivenEnabled(); } } @@ -974,9 +996,7 @@ export class MeshRenderer extends ModelRenderer { this._detachFromScene(); } - if (this.supportGPUScene()) { - if (this.mesh) renderScene.addGPUMesh(this.mesh); - + if (this.supportGPUDriven() && this.mesh!.isInGPUScene()) { renderScene.addGPUModel(this._model); } else { renderScene.addModel(this._model); @@ -988,7 +1008,7 @@ export class MeshRenderer extends ModelRenderer { */ public _detachFromScene (): void { if (this._model && this._model.scene) { - if (this.supportGPUScene()) { + if (this.supportGPUDriven() && this.mesh!.isInGPUScene()) { this._model.scene.removeGPUModel(this._model); } else { this._model.scene.removeModel(this._model); @@ -999,12 +1019,16 @@ export class MeshRenderer extends ModelRenderer { /** * @engineInternal */ - public supportGPUScene (): boolean { + public supportGPUDriven (): boolean { const sceneData = cclegacy.director.root.pipeline.pipelineSceneData; if (!sceneData || !sceneData.isGPUDrivenEnabled()) { return false; } + if (!this._gpuDrivenEnabled) { + return false; + } + if (!this._mesh || !this.node) { return false; } @@ -1168,6 +1192,11 @@ export class MeshRenderer extends ModelRenderer { } } + protected _updateGPUDrivenEnabled (): void { + if (!this._model) { return; } + this._model.gpuDrivenEnabled = this._gpuDrivenEnabled; + } + protected onMobilityChanged (): void { this._updateUseLightProbe(); this._updateReceiveDirLight(); diff --git a/cocos/asset/assets/simple-texture.ts b/cocos/asset/assets/simple-texture.ts index a93516c20c7..a0a49cd2725 100644 --- a/cocos/asset/assets/simple-texture.ts +++ b/cocos/asset/assets/simple-texture.ts @@ -322,7 +322,9 @@ export class SimpleTexture extends TextureBase { if (this._width === 0 || this._height === 0) { return; } let flags = TextureFlagBit.NONE; if (this._mipFilter !== Filter.NONE && canGenerateMipmap(device, this._width, this._height)) { - this._mipmapLevel = getMipLevel(this._width, this._height); + if (!this.isCompressed) { + this._mipmapLevel = getMipLevel(this._width, this._height); + } if (!this.isUsingOfflineMipmaps() && !this.isCompressed) { flags = TextureFlagBit.GEN_MIPMAP; } diff --git a/cocos/game/director.ts b/cocos/game/director.ts index 5f43630cfd0..4d741a6a267 100644 --- a/cocos/game/director.ts +++ b/cocos/game/director.ts @@ -535,8 +535,8 @@ export class Director extends EventTarget { for (let i = 0; i < renderers.length; i++) { const renderer = renderers[i]; const mesh = renderer.mesh; - if (renderer.supportGPUScene()) { - meshes.push(mesh!); + if (mesh && mesh.supportGPUScene()) { + meshes.push(mesh); } } diff --git a/cocos/render-scene/core/render-scene.ts b/cocos/render-scene/core/render-scene.ts index 60208d8aa35..d8c5d957d1e 100644 --- a/cocos/render-scene/core/render-scene.ts +++ b/cocos/render-scene/core/render-scene.ts @@ -203,13 +203,14 @@ export class RenderScene { /** * @engineInternal */ - public activate () { + public activate (): void { + // Do nothing } /** * @engineInternal */ - public buildGPUScene (meshes: Mesh[]) { + public buildGPUScene (meshes: Mesh[]): void { // Only support in native. } @@ -557,13 +558,6 @@ export class RenderScene { this._models.length = 0; } - /** - * Add a mesh to GPUScene. - * Only support in native. - * @internal - */ - public addGPUMesh (m: Mesh): void {} - /** * @en Add a GPU Driven model, all models attached to the render scene will be submitted for rendering. * @zh 增加一个 GPU Driven 模型,渲染场景上挂载的所有模型都会被提交渲染。 diff --git a/cocos/render-scene/scene/model.ts b/cocos/render-scene/scene/model.ts index 4bd1ce433b8..23859d2889f 100644 --- a/cocos/render-scene/scene/model.ts +++ b/cocos/render-scene/scene/model.ts @@ -384,6 +384,18 @@ export class Model { this._reflectionProbeBlendWeight = val; } + /** + * @en Whether to enable GPU Driven. + * @zh 是否开启 GPU Driven 。 + */ + get gpuDrivenEnabled (): boolean { + return this._gpuDrivenEnabled; + } + + set gpuDrivenEnabled (val) { + this._gpuDrivenEnabled = val; + } + /** * @en The type of the model * @zh 模型类型 @@ -577,6 +589,12 @@ export class Model { */ protected _reflectionProbeType = ReflectionProbeType.NONE; + /** + * @en Whether to enable GPU Driven. + * @zh 是否开启 GPU Driven 。 + */ + protected _gpuDrivenEnabled = true; + /** * @internal * @en native object diff --git a/editor/assets/effects/pipeline/gpu-driven/gpu-culling.effect b/editor/assets/effects/pipeline/gpu-driven/gpu-culling.effect index 629bb61c2c2..51cc75956a1 100644 --- a/editor/assets/effects/pipeline/gpu-driven/gpu-culling.effect +++ b/editor/assets/effects/pipeline/gpu-driven/gpu-culling.effect @@ -95,7 +95,21 @@ CCProgram culling-main %{ #if CC_USE_OCCLUSION_CULLING // 2D Polyhedral Bounds of a Clipped, Perspective-Projected 3D Sphere. Michael Mara, Morgan McGuire. 2013 // Consider: minClipZ & projectionSignY & orientation in projection, so we can not use p00 & p11 directly. - bool projectSpherePerspective(vec3 c, float r, float znear, mat4 proj, uint orientation, out vec4 aabb, out float nearest) { + /** + if (orientation == 0) { + aabb = vec4(minx * proj[0][0], miny * proj[1][1], maxx * proj[0][0], maxy * proj[1][1]); + } + else if (orientation == 1) { + aabb = vec4(maxy * proj[1][0], minx * proj[0][1], miny * proj[1][0], maxx * proj[0][1]); + } + else if (orientation == 2) { + aabb = vec4(maxx * proj[0][0], maxy * proj[1][1], minx * proj[0][0], miny * proj[1][1]); + } + else { + aabb = vec4(miny * proj[1][0], maxx * proj[0][1], maxy * proj[1][0], minx * proj[0][1]); + } + */ + bool projectSpherePerspective(vec3 c, float r, float znear, mat4 proj, out vec4 aabb, out float nearest) { if (-c.z < r + znear) return false; vec3 cr = c * r; @@ -110,18 +124,7 @@ CCProgram culling-main %{ float maxy = (vy * c.y - cr.z) / -(vy * c.z + cr.y); // project to ndc space - if (orientation == 0) { - aabb = vec4(minx * proj[0][0], miny * proj[1][1], maxx * proj[0][0], maxy * proj[1][1]); - } - else if (orientation == 1) { - aabb = vec4(maxy * proj[1][0], minx * proj[0][1], miny * proj[1][0], maxx * proj[0][1]); - } - else if (orientation == 2) { - aabb = vec4(maxx * proj[0][0], maxy * proj[1][1], minx * proj[0][0], miny * proj[1][1]); - } - else { - aabb = vec4(miny * proj[1][0], maxx * proj[0][1], maxy * proj[1][0], minx * proj[0][1]); - } + aabb = vec4(minx * proj[0][0], miny * proj[1][1], maxx * proj[0][0], maxy * proj[1][1]); // ndc space -> uv space aabb = aabb * vec4(0.5f) + vec4(0.5f); @@ -131,7 +134,7 @@ CCProgram culling-main %{ return true; } - bool projectSphereOrtho(vec3 c, float r, float znear, mat4 proj, uint orientation, out vec4 aabb, out float nearest) { + bool projectSphereOrtho(vec3 c, float r, float znear, mat4 proj, out vec4 aabb, out float nearest) { if (-c.z < r + znear) return false; float minx = c.x - r; @@ -141,18 +144,7 @@ CCProgram culling-main %{ float maxy = c.y + r; // project to ndc space - if (orientation == 0) { - aabb = vec4(minx * proj[0][0], miny * proj[1][1], maxx * proj[0][0], maxy * proj[1][1]); - } - else if (orientation == 1) { - aabb = vec4(maxy * proj[1][0], minx * proj[0][1], miny * proj[1][0], maxx * proj[0][1]); - } - else if (orientation == 2) { - aabb = vec4(maxx * proj[0][0], maxy * proj[1][1], minx * proj[0][0], miny * proj[1][1]); - } - else { - aabb = vec4(miny * proj[1][0], maxx * proj[0][1], maxy * proj[1][0], minx * proj[0][1]); - } + aabb = vec4(minx * proj[0][0], miny * proj[1][1], maxx * proj[0][0], maxy * proj[1][1]); aabb = aabb + vec4(proj[3][0], proj[3][1], proj[3][0], proj[3][1]); // ndc space -> uv space @@ -171,19 +163,27 @@ CCProgram culling-main %{ float radius = sphere.w; if (cc_isPerspective != 0) { - if (!projectSpherePerspective(center, radius, cc_znear, cc_proj, cc_orientation, aabb, nearest)) { + if (!projectSpherePerspective(center, radius, cc_znear, cc_proj, aabb, nearest)) { return false; } } else { - if (!projectSphereOrtho(center, radius, cc_znear, cc_proj, cc_orientation, aabb, nearest)) { + if (!projectSphereOrtho(center, radius, cc_znear, cc_proj, aabb, nearest)) { return false; } } float width = abs(aabb.z - aabb.x) * cc_depthWidth; float height = abs(aabb.w - aabb.y) * cc_depthHeight; - float level = max(0.0, floor(log2(max(width, height)))); - float depth = textureLod(CCDepthMap, (aabb.xy + aabb.zw) * vec2(0.5), level).x; + float level = max(0.0, ceil(log2(max(width, height)))); + #if CC_USE_SAMPLER_FILTER_MIN_MAX + float depth = textureLod(CCDepthMap, (aabb.xy + aabb.zw) * vec2(0.5), level).x; + #else + float a = textureLod(CCDepthMap, aabb.xy, level).x; + float b = textureLod(CCDepthMap, aabb.xw, level).x; + float c = textureLod(CCDepthMap, aabb.zy, level).x; + float d = textureLod(CCDepthMap, aabb.zw, level).x; + float depth = max(max(a, b), max(c, d)); + #endif return nearest > depth; } #endif diff --git a/editor/i18n/en/modules/rendering.js b/editor/i18n/en/modules/rendering.js index 3806a1f47a6..674e43e5c8b 100644 --- a/editor/i18n/en/modules/rendering.js +++ b/editor/i18n/en/modules/rendering.js @@ -57,6 +57,10 @@ module.exports = { displayName: 'Materials', tooltip: 'Material array. Each item in turn specifies material of sub mesh.', }, + 'gpuDrivenEnabled': { + displayName: 'Enable GPU Driven', + tooltip: 'Whether to enable GPU Driven.', + }, 'shadowCastingModeForInspector': { displayName: 'Cast Shadows', tooltip: 'Whether if this mesh casts shadows.', diff --git a/editor/i18n/zh/modules/rendering.js b/editor/i18n/zh/modules/rendering.js index c202e2eda20..dae14f2f74b 100644 --- a/editor/i18n/zh/modules/rendering.js +++ b/editor/i18n/zh/modules/rendering.js @@ -57,6 +57,10 @@ module.exports = { displayName: '材质', tooltip: '材质资源数组。每一项依次指定了子网格的材质。', }, + 'gpuDrivenEnabled': { + displayName: '启用 GPU Driven', + tooltip: '此物体是否启用 GPU Driven。', + }, 'shadowCastingModeForInspector': { displayName: '投射阴影', tooltip: '此网格是否投射阴影。', diff --git a/native/cocos/3d/assets/Mesh.cpp b/native/cocos/3d/assets/Mesh.cpp index 00bc466d063..f01d66e9234 100644 --- a/native/cocos/3d/assets/Mesh.cpp +++ b/native/cocos/3d/assets/Mesh.cpp @@ -449,7 +449,7 @@ void Mesh::initialize() { } _initialized = true; - _supportGPUScene = (!_struct.supportGPUScene.has_value() || _struct.supportGPUScene.value()) && isGPUMeshFormat(); + _supportGPUScene = (_struct.supportGPUScene.has_value() ? _struct.supportGPUScene.value() : true) && isGPUMeshFormat(); if (_struct.compressed) { // decompress @@ -463,7 +463,30 @@ void Mesh::initialize() { MeshUtils::dequantizeMesh(_struct, _data); } - if (_struct.dynamic.has_value()) { + const auto *pipeline = Root::getInstance()->getPipeline(); + const auto *sceneData = pipeline ? pipeline->getPipelineSceneData() : nullptr; + const auto gpuDrivenEnabled = sceneData && sceneData->isGPUDrivenEnabled(); + if (gpuDrivenEnabled && _supportGPUScene) { + for (auto i = 0U; i < _struct.primitives.size(); i++) { + const auto &primitive = _struct.primitives[i]; + gfx::BufferList subVBs = {}; + gfx::AttributeList attributes; + + for (const auto idx : primitive.vertexBundelIndices) { + const auto &vertexBundle = _struct.vertexBundles[idx]; + for (const auto &attr : vertexBundle.attributes) { + attributes.emplace_back(attr); + } + } + + // Update buffers later in GPUMeshPool + auto *subMesh = ccnew RenderingSubMesh(subVBs, attributes, primitive.primitiveMode, nullptr); + subMesh->setMesh(this); + subMesh->setSubMeshIdx(static_cast(i)); + + _renderingSubMeshes.emplace_back(subMesh); + } + } else if (_struct.dynamic.has_value()) { auto *device = gfx::Device::getInstance(); gfx::BufferList vertexBuffers; @@ -610,7 +633,7 @@ void Mesh::initialize() { _isMeshDataUploaded = true; #if !CC_EDITOR - if (!_allowDataAccess && !supportGPUScene()) { + if (!_allowDataAccess) { releaseData(); } #endif diff --git a/native/cocos/core/assets/RenderingSubMesh.cpp b/native/cocos/core/assets/RenderingSubMesh.cpp index 97ed95e5c00..bb6ba40598a 100644 --- a/native/cocos/core/assets/RenderingSubMesh.cpp +++ b/native/cocos/core/assets/RenderingSubMesh.cpp @@ -381,4 +381,22 @@ gfx::Buffer *RenderingSubMesh::allocVertexIdBuffer(gfx::Device *device) { return vertexIdBuffer; } +void RenderingSubMesh::resetBuffers(const gfx::BufferList &vertexBuffers, + gfx::Buffer* indexBuffer, + uint32_t vertexCount, + uint32_t firstVertex, + uint32_t indexCount, + uint32_t firstIndex, + int32_t vertexOffset) { + _vertexBuffers = vertexBuffers; + _indexBuffer = indexBuffer; + _iaInfo.vertexBuffers = vertexBuffers; + _iaInfo.indexBuffer = indexBuffer; + _iaInfo.vertexCount = vertexCount; + _iaInfo.firstVertex = firstVertex; + _iaInfo.indexCount = indexCount; + _iaInfo.firstIndex = firstIndex; + _iaInfo.vertexOffset = vertexOffset; +} + } // namespace cc diff --git a/native/cocos/core/assets/RenderingSubMesh.h b/native/cocos/core/assets/RenderingSubMesh.h index f270d713b83..40b3923468c 100644 --- a/native/cocos/core/assets/RenderingSubMesh.h +++ b/native/cocos/core/assets/RenderingSubMesh.h @@ -194,6 +194,14 @@ class RenderingSubMesh : public RefCounted { inline void setMeshPoolIndex(uint32_t index) { _meshPoolIndex = index; } inline uint32_t getMeshPoolIndex() const { return _meshPoolIndex; } + void resetBuffers(const gfx::BufferList &vertexBuffers, + gfx::Buffer *indexBuffer, + uint32_t vertexCount, + uint32_t firstVertex, + uint32_t indexCount, + uint32_t firstIndex, + int32_t vertexOffset); + private: gfx::Buffer *allocVertexIdBuffer(gfx::Device *device); diff --git a/native/cocos/core/assets/SimpleTexture.cpp b/native/cocos/core/assets/SimpleTexture.cpp index b24457e5318..86fb47d5440 100644 --- a/native/cocos/core/assets/SimpleTexture.cpp +++ b/native/cocos/core/assets/SimpleTexture.cpp @@ -134,7 +134,9 @@ void SimpleTexture::createTexture(gfx::Device *device) { auto flags = gfx::TextureFlagBit::NONE; auto usage = gfx::TextureUsageBit::SAMPLED | gfx::TextureUsageBit::TRANSFER_DST; if (_mipFilter != Filter::NONE && canGenerateMipmap(_width, _height)) { - _mipmapLevel = getMipLevel(_width, _height); + if (!isCompressed()) { + _mipmapLevel = getMipLevel(_width, _height); + } if (!isUsingOfflineMipmaps() && !isCompressed()) { flags = gfx::TextureFlagBit::GEN_MIPMAP; } diff --git a/native/cocos/renderer/gfx-base/GFXDef-common.h b/native/cocos/renderer/gfx-base/GFXDef-common.h index bf0f329685a..1e8a4e6ada2 100644 --- a/native/cocos/renderer/gfx-base/GFXDef-common.h +++ b/native/cocos/renderer/gfx-base/GFXDef-common.h @@ -1314,6 +1314,11 @@ struct InputAssemblerInfo { AttributeList attributes; BufferList vertexBuffers; Buffer *indexBuffer{nullptr}; // @ts-nullable + uint32_t vertexCount{0}; + uint32_t firstVertex{0}; + uint32_t indexCount{0}; + uint32_t firstIndex{0}; + int32_t vertexOffset{0}; EXPOSE_COPY_FN(InputAssemblerInfo) }; diff --git a/native/cocos/renderer/gfx-base/GFXInputAssembler.cpp b/native/cocos/renderer/gfx-base/GFXInputAssembler.cpp index 616b6851e7c..4b5b702b0ee 100644 --- a/native/cocos/renderer/gfx-base/GFXInputAssembler.cpp +++ b/native/cocos/renderer/gfx-base/GFXInputAssembler.cpp @@ -43,12 +43,12 @@ void InputAssembler::initialize(const InputAssemblerInfo &info) { _attributesHash = computeAttributesHash(_attributes); if (_indexBuffer) { - _drawInfo.indexCount = _indexBuffer->getCount(); - _drawInfo.firstIndex = 0; + _drawInfo.indexCount = info.indexCount > 0 ? info.indexCount : _indexBuffer->getCount(); + _drawInfo.firstIndex = info.firstIndex; + _drawInfo.vertexOffset = info.vertexOffset; } else if (!_vertexBuffers.empty()) { - _drawInfo.vertexCount = _vertexBuffers[0]->getCount(); - _drawInfo.firstVertex = 0; - _drawInfo.vertexOffset = 0; + _drawInfo.vertexCount = info.vertexCount > 0 ? info.vertexCount : _vertexBuffers[0]->getCount(); + _drawInfo.firstVertex = info.firstVertex; } doInit(info); diff --git a/native/cocos/renderer/gfx-gles3/GLES3PrimaryCommandBuffer.cpp b/native/cocos/renderer/gfx-gles3/GLES3PrimaryCommandBuffer.cpp index a942f163c18..b0a1eb6eb9c 100644 --- a/native/cocos/renderer/gfx-gles3/GLES3PrimaryCommandBuffer.cpp +++ b/native/cocos/renderer/gfx-gles3/GLES3PrimaryCommandBuffer.cpp @@ -92,6 +92,11 @@ void GLES3PrimaryCommandBuffer::drawIndirect(Buffer *buffer, uint32_t offset, ui auto *glesBuffer = static_cast(buffer); auto *gpuBuffer = glesBuffer->gpuBuffer(); cmdFuncGLES3DrawIndirect(GLES3Device::getInstance(), gpuBuffer, offset, count, stride, false); + if (GLES3Device::getInstance()->constantRegistry()->multiDrawIndirect) { + ++_numDrawCalls; + } else { + _numDrawCalls += count; + } } void GLES3PrimaryCommandBuffer::drawIndexedIndirect(Buffer *buffer, uint32_t offset, uint32_t count, uint32_t stride) { @@ -102,6 +107,11 @@ void GLES3PrimaryCommandBuffer::drawIndexedIndirect(Buffer *buffer, uint32_t off auto *glesBuffer = static_cast(buffer); auto *gpuBuffer = glesBuffer->gpuBuffer(); cmdFuncGLES3DrawIndirect(GLES3Device::getInstance(), gpuBuffer, offset, count, stride, true); + if (GLES3Device::getInstance()->constantRegistry()->multiDrawIndirect) { + ++_numDrawCalls; + } else { + _numDrawCalls += count; + } } void GLES3PrimaryCommandBuffer::beginMarker(const MarkerInfo &marker) { diff --git a/native/cocos/renderer/gfx-metal/MTLCommandBuffer.mm b/native/cocos/renderer/gfx-metal/MTLCommandBuffer.mm index 23db390c4b8..c73e0743760 100644 --- a/native/cocos/renderer/gfx-metal/MTLCommandBuffer.mm +++ b/native/cocos/renderer/gfx-metal/MTLCommandBuffer.mm @@ -595,6 +595,7 @@ of this software and associated engine source code (the "Software"), a limited, indirectBuffer: mtlBuffer->mtlBuffer() indirectBufferOffset: off]; } + _numDrawCalls += count; } void CCMTLCommandBuffer::drawIndexedIndirect(Buffer *buffer, uint32_t offset, uint32_t count, uint32_t stride) { @@ -613,6 +614,7 @@ of this software and associated engine source code (the "Software"), a limited, indirectBuffer: mtlBuffer->mtlBuffer() indirectBufferOffset: off]; } + _numDrawCalls += count; } void CCMTLCommandBuffer::draw(const DrawInfo &info) { diff --git a/native/cocos/renderer/gfx-vulkan/VKCommandBuffer.cpp b/native/cocos/renderer/gfx-vulkan/VKCommandBuffer.cpp index d3e567c01f3..4e3ca777a98 100644 --- a/native/cocos/renderer/gfx-vulkan/VKCommandBuffer.cpp +++ b/native/cocos/renderer/gfx-vulkan/VKCommandBuffer.cpp @@ -472,6 +472,7 @@ void CCVKCommandBuffer::drawIndirect(Buffer *buffer, uint32_t offset, uint32_t c bufferOffset + offset, count, stride); + ++_numDrawCalls; } else { for (uint32_t i = 0U; i < count; ++i) { uint32_t currentOffset = bufferOffset + offset + i * stride; @@ -481,6 +482,7 @@ void CCVKCommandBuffer::drawIndirect(Buffer *buffer, uint32_t offset, uint32_t c 1, stride); } + _numDrawCalls += count; } } @@ -501,6 +503,7 @@ void CCVKCommandBuffer::drawIndexedIndirect(Buffer *buffer, uint32_t offset, uin bufferOffset + offset, count, stride); + ++_numDrawCalls; } else { for (uint32_t i = 0U; i < count; ++i) { uint32_t currentOffset = bufferOffset + offset + i * stride; @@ -510,6 +513,7 @@ void CCVKCommandBuffer::drawIndexedIndirect(Buffer *buffer, uint32_t offset, uin 1, stride); } + _numDrawCalls += count; } } diff --git a/native/cocos/renderer/pipeline/PipelineSceneData.cpp b/native/cocos/renderer/pipeline/PipelineSceneData.cpp index b750d63a595..90fb081330b 100644 --- a/native/cocos/renderer/pipeline/PipelineSceneData.cpp +++ b/native/cocos/renderer/pipeline/PipelineSceneData.cpp @@ -180,6 +180,7 @@ void PipelineSceneData::initGPUDrivenMaterial() { IMaterialInfo info; MacroRecord macros{ + {"CC_USE_SAMPLER_FILTER_MIN_MAX", filterMinMax}, {"CC_SUPPORT_FIRST_INSTANCE", firstInstance}, {"CC_USE_FRUSTUM_CULLING", frustumCulling}, {"CC_USE_OCCLUSION_CULLING", defines[i].useOcclusion}, diff --git a/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp b/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp index a547d88b6af..5b2cfc766ce 100644 --- a/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp @@ -43,6 +43,7 @@ #include "cocos/renderer/pipeline/Define.h" #include "cocos/renderer/pipeline/InstancedBuffer.h" #include "cocos/renderer/pipeline/PipelineStateManager.h" +#include "cocos/renderer/pipeline/helper/Utils.h" #include "cocos/scene/Model.h" #include "cocos/scene/Octree.h" #include "cocos/scene/Pass.h" @@ -1499,6 +1500,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { if (pass.showStatistics) { submitProfilerCommands(ctx, vertID, pass); +#if CC_USE_DEBUG_RENDERER + renderDebugRenderer(ctx.currentPass, ctx.cmdBuff, ctx.ppl->pipelineSceneData, nullptr); +#endif } ctx.cmdBuff->endRenderPass(); ctx.currentPass = nullptr; diff --git a/native/cocos/renderer/pipeline/custom/NativePipeline.cpp b/native/cocos/renderer/pipeline/custom/NativePipeline.cpp index 616db2fac66..5a908546de9 100644 --- a/native/cocos/renderer/pipeline/custom/NativePipeline.cpp +++ b/native/cocos/renderer/pipeline/custom/NativePipeline.cpp @@ -1135,8 +1135,15 @@ void NativePipeline::addBuiltinGpuCullingPass(uint32_t cullingID, gpuCullPass->addStorageBuffer(drawInstanceBuffer, AccessType::WRITE, "CCDrawInstanceBuffer"); gpuCullPass->addStorageBuffer(visibilityBuffer, AccessType::READ_WRITE, "CCVisibilityBuffer"); if (!hzbName.empty()) { - auto *sampler = device->getSampler({gfx::Filter::POINT, gfx::Filter::POINT, gfx::Filter::NONE, - gfx::Address::CLAMP, gfx::Address::CLAMP, gfx::Address::CLAMP}); + gfx::Sampler *sampler = nullptr; + if (device->getCapabilities().supportFilterMinMax) { + sampler = device->getSampler({gfx::Filter::LINEAR, gfx::Filter::LINEAR, gfx::Filter::NONE, + gfx::Address::CLAMP, gfx::Address::CLAMP, gfx::Address::CLAMP, + 0, gfx::ComparisonFunc::ALWAYS, gfx::Reduction::MAX}); + } else { + sampler = device->getSampler({gfx::Filter::POINT, gfx::Filter::POINT, gfx::Filter::NONE, + gfx::Address::CLAMP, gfx::Address::CLAMP, gfx::Address::CLAMP}); + } gpuCullPass->addTexture(hzbName + std::to_string(cullingID), "CCDepthMap", sampler, 0); } diff --git a/native/cocos/renderer/pipeline/helper/Utils.cpp b/native/cocos/renderer/pipeline/helper/Utils.cpp index 3c224906b3a..6d5d98ec718 100644 --- a/native/cocos/renderer/pipeline/helper/Utils.cpp +++ b/native/cocos/renderer/pipeline/helper/Utils.cpp @@ -86,7 +86,7 @@ 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) { - if (camera != profilerCamera) { + if (camera && camera != profilerCamera) { return; } diff --git a/native/cocos/scene/Model.cpp b/native/cocos/scene/Model.cpp index b21ed880264..d2effbf8164 100644 --- a/native/cocos/scene/Model.cpp +++ b/native/cocos/scene/Model.cpp @@ -466,13 +466,17 @@ bool Model::isInGPUScene(index_t subModelIndex) const { return mesh->isInGPUScene(); } -bool Model::supportGPUScene(index_t subModelIndex) const { +bool Model::supportGPUDriven(index_t subModelIndex) const { const auto *pipeline = Root::getInstance()->getPipeline(); const auto *sceneData = pipeline->getPipelineSceneData(); if (!sceneData || !sceneData->isGPUDrivenEnabled()) { return false; } + if (!_gpuDrivenEnabled) { + return false; + } + // skip light probe object if (_useLightProbe) { return false; @@ -552,7 +556,7 @@ ccstd::vector Model::getMacroPatches(index_t subModelIndex) { } } patches.emplace_back(IMacroPatch{CC_DISABLE_DIRECTIONAL_LIGHT, !_receiveDirLight}); - patches.emplace_back(IMacroPatch{CC_USE_GPU_DRIVEN, supportGPUScene(subModelIndex) && isInGPUScene(subModelIndex)}); + patches.emplace_back(IMacroPatch{CC_USE_GPU_DRIVEN, supportGPUDriven(subModelIndex) && isInGPUScene(subModelIndex)}); return patches; } diff --git a/native/cocos/scene/Model.h b/native/cocos/scene/Model.h index f1cf7df8256..ff501da94a8 100644 --- a/native/cocos/scene/Model.h +++ b/native/cocos/scene/Model.h @@ -253,6 +253,8 @@ class Model : public RefCounted { inline void setModelBounds(geometry::AABB *bounds) { _modelBounds = bounds; } inline bool isModelImplementedInJS() const { return (_type != Type::DEFAULT && _type != Type::SKINNING && _type != Type::BAKED_SKINNING); }; inline const gfx::Texture *getLightmap() const { return _lightmap ? _lightmap->getGFXTexture() : nullptr; } + inline void setGPUDrivenEnabled(bool b) { _gpuDrivenEnabled = b; } + inline bool isGPUDrivenEnabled() const { return _gpuDrivenEnabled; } protected: static SubModel *createSubModel(); @@ -260,7 +262,7 @@ class Model : public RefCounted { void updateAttributesAndBinding(index_t subModelIndex); bool isLightProbeAvailable() const; void updateSHBuffer(); - bool supportGPUScene(index_t subModelIndex) const; + bool supportGPUDriven(index_t subModelIndex) const; bool isInGPUScene(index_t subModelIndex) const; // Please declare variables in descending order of memory size occupied by variables. @@ -275,6 +277,7 @@ class Model : public RefCounted { int32_t _reflectionProbeId{-1}; int32_t _reflectionProbeBlendId{-1}; float _reflectionProbeBlendWeight{0.F}; + bool _gpuDrivenEnabled{true}; OctreeNode *_octreeNode{nullptr}; RenderScene *_scene{nullptr}; diff --git a/native/cocos/scene/RenderScene.cpp b/native/cocos/scene/RenderScene.cpp index 1ce2a7e8a6b..0cff55af33f 100644 --- a/native/cocos/scene/RenderScene.cpp +++ b/native/cocos/scene/RenderScene.cpp @@ -392,12 +392,6 @@ void RenderScene::removeModels() { _models.clear(); } -void RenderScene::addGPUMesh(Mesh *mesh) { - if (_gpuScene) { - _gpuScene->addMesh(mesh); - } -} - void RenderScene::addGPUModel(Model *model) { model->attachToScene(this); _gpuModels.emplace_back(model); diff --git a/native/cocos/scene/RenderScene.h b/native/cocos/scene/RenderScene.h index 60bf3b2e216..7af396ce477 100644 --- a/native/cocos/scene/RenderScene.h +++ b/native/cocos/scene/RenderScene.h @@ -106,7 +106,6 @@ class RenderScene : public RefCounted { void removeModel(Model *model); void removeModels(); - void addGPUMesh(Mesh *mesh); void addGPUModel(Model *model); void removeGPUModel(Model *model); void removeGPUModels(); diff --git a/native/cocos/scene/SubModel.cpp b/native/cocos/scene/SubModel.cpp index 92be251cc8f..db0c77ca17b 100644 --- a/native/cocos/scene/SubModel.cpp +++ b/native/cocos/scene/SubModel.cpp @@ -133,9 +133,6 @@ void SubModel::initialize(RenderingSubMesh *subMesh, const SharedPassArray &pPas CC_ASSERT(!pPasses->empty()); gfx::DescriptorSetInfo dsInfo; dsInfo.layout = (*pPasses)[0]->getLocalSetLayout(); - if (!subMesh->getIaInfo().vertexBuffers.empty()) { - _inputAssembler = _device->createInputAssembler(subMesh->getIaInfo()); - } _descriptorSet = _device->createDescriptorSet(dsInfo); const auto *pipeline = Root::getInstance()->getPipeline(); @@ -147,6 +144,7 @@ void SubModel::initialize(RenderingSubMesh *subMesh, const SharedPassArray &pPas } _subMesh = subMesh; + initInputAssembler(); ccstd::vector tmp = patches; std::sort(tmp.begin(), tmp.end(), IMacroPatch::compare); _patches = tmp; @@ -193,6 +191,16 @@ void SubModel::initialize(RenderingSubMesh *subMesh, const SharedPassArray &pPas } } +void SubModel::initInputAssembler() { + if (_subMesh->getVertexBuffers().empty()) { + return; + } + + if (!_inputAssembler) { + _inputAssembler = _device->createInputAssembler(_subMesh->getIaInfo()); + } +} + void SubModel::destroy() { CC_SAFE_DESTROY_NULL(_descriptorSet); CC_SAFE_DESTROY_NULL(_inputAssembler); diff --git a/native/cocos/scene/SubModel.h b/native/cocos/scene/SubModel.h index 7ce204b5297..aa6e7564afd 100644 --- a/native/cocos/scene/SubModel.h +++ b/native/cocos/scene/SubModel.h @@ -86,6 +86,7 @@ class SubModel : public RefCounted { int32_t getInstancedAttributeIndex(const ccstd::string &name) const; void initialize(RenderingSubMesh *subMesh, const SharedPassArray &passes, const ccstd::vector &patches); + void initInputAssembler(); void destroy(); void onPipelineStateChanged(); void onMacroPatchesStateChanged(const ccstd::vector &patches); diff --git a/native/cocos/scene/gpu-scene/GPUMeshPool.cpp b/native/cocos/scene/gpu-scene/GPUMeshPool.cpp index 0e56e7c9b7b..119d92af0b1 100644 --- a/native/cocos/scene/gpu-scene/GPUMeshPool.cpp +++ b/native/cocos/scene/gpu-scene/GPUMeshPool.cpp @@ -31,6 +31,7 @@ #include "core/assets/RenderingSubMesh.h" #include "3d/assets/Mesh.h" #include "base/memory/Memory.h" +#include "base/std/container/unordered_set.h" namespace cc { namespace scene { @@ -69,11 +70,21 @@ void GPUMeshPool::destroy() { } void GPUMeshPool::build(const ccstd::vector& meshes) { - for (const auto& mesh : meshes) { + ccstd::unordered_set meshSet; + for (auto* mesh : meshes) { + meshSet.insert(mesh); + } + + for (const auto& mesh : meshSet) { addMesh(mesh); } updateBuffers(); + + // Note: must redirect mesh after updateBuffers + for (const auto& mesh : meshSet) { + redirectMesh(mesh); + } } void GPUMeshPool::addMesh(Mesh* mesh) { @@ -119,28 +130,43 @@ void GPUMeshPool::addMesh(Mesh* mesh) { vb->second.push(buffer->getData() + vertexBundle.view.offset, vertexBundle.view.length, vbCount); ib->second.push(buffer->getData() + indexView.offset, indexView.length, ibCount); - - // Destroy subMesh's private buffers - subMesh->destroy(); } mesh->setInGPUScene(true); // Release CPU side data if necessary +#if !CC_EDITOR if (!mesh->isAllowDataAccess()) { mesh->releaseData(); } +#endif _dirty = true; } +void GPUMeshPool::redirectMesh(Mesh* mesh) { + const auto& subMeshes = mesh->getRenderingSubMeshes(); + + for (const auto& subMesh : subMeshes) { + const auto meshIdx = subMesh->getMeshPoolIndex(); + const auto& meshData = _meshes[meshIdx]; + + auto* vb = getVertexBuffer(meshData.attributesHash); + auto* ib = getIndexBuffer(meshData.indexStride); + gfx::BufferList vbs = {vb}; + + // Reset subMesh's buffers to GPUMeshPool. + subMesh->resetBuffers(vbs, ib, meshData.vertexCount, meshData.firstVertex, meshData.indexCount, meshData.firstIndex, meshData.firstVertex); + } +} + void GPUMeshPool::updateBuffers() { if (!_dirty) { return; } auto* device = gfx::Device::getInstance(); - for (const auto& iter : _vbs) { + for (auto& iter : _vbs) { const auto size = iter.second.stride * iter.second.count; auto vb = _vertexBuffers.find(iter.first); if (vb == _vertexBuffers.cend()) { @@ -158,9 +184,13 @@ void GPUMeshPool::updateBuffers() { buffer->update(iter.second.buffer.data(), size); } } + + // Release CPU side buffer + iter.second.buffer.clear(); + iter.second.buffer.shrink_to_fit(); } - for (const auto& iter : _ibs) { + for (auto& iter : _ibs) { const auto size = iter.second.stride * iter.second.count; auto ib = _indexBuffers.find(iter.first); if (ib == _indexBuffers.cend()) { @@ -178,6 +208,10 @@ void GPUMeshPool::updateBuffers() { buffer->update(iter.second.buffer.data(), size); } } + + // Release CPU side buffer + iter.second.buffer.clear(); + iter.second.buffer.shrink_to_fit(); } _dirty = false; diff --git a/native/cocos/scene/gpu-scene/GPUMeshPool.h b/native/cocos/scene/gpu-scene/GPUMeshPool.h index 747d1685afe..598fd048a9a 100644 --- a/native/cocos/scene/gpu-scene/GPUMeshPool.h +++ b/native/cocos/scene/gpu-scene/GPUMeshPool.h @@ -32,11 +32,13 @@ #include "renderer/gfx-base/GFXBuffer.h" #include "scene/gpu-scene/Const.h" + namespace cc { class Mesh; namespace scene { class GPUScene; +class Model; struct SubMeshData { ccstd::hash_t attributesHash{0U}; @@ -68,13 +70,14 @@ class CC_DLL GPUMeshPool final : public RefCounted { void destroy(); void build(const ccstd::vector& meshes); - void addMesh(Mesh* mesh); - + inline const SubMeshData& getSubMeshData(uint32_t index) const { return _meshes[index]; } inline gfx::Buffer* getVertexBuffer(ccstd::hash_t key) { return _vertexBuffers[key].get(); } inline gfx::Buffer* getIndexBuffer(uint32_t key) { return _indexBuffers[key].get(); } private: + void addMesh(Mesh* mesh); + void redirectMesh(Mesh* mesh); void updateBuffers(); GPUScene* _gpuScene{nullptr}; diff --git a/native/cocos/scene/gpu-scene/GPUScene.cpp b/native/cocos/scene/gpu-scene/GPUScene.cpp index daee19afa09..3fc477e4357 100644 --- a/native/cocos/scene/gpu-scene/GPUScene.cpp +++ b/native/cocos/scene/gpu-scene/GPUScene.cpp @@ -59,11 +59,6 @@ void GPUScene::build(const ccstd::vector& meshes) { _meshPool->build(meshes); } -void GPUScene::addMesh(Mesh* mesh) { - _meshPool->addMesh(mesh); - _meshPool->updateBuffers(); -} - void GPUScene::addModel(const Model* model) { _objectPool->addModel(model); _batchPool->addModel(model); diff --git a/native/cocos/scene/gpu-scene/GPUScene.h b/native/cocos/scene/gpu-scene/GPUScene.h index 9029a0d809a..04bb55bef07 100644 --- a/native/cocos/scene/gpu-scene/GPUScene.h +++ b/native/cocos/scene/gpu-scene/GPUScene.h @@ -49,7 +49,6 @@ class CC_DLL GPUScene final : public RefCounted { void build(const ccstd::vector& meshes); - void addMesh(Mesh* mesh); void addModel(const Model* model); void removeModel(const Model* model); void removeAllModels(); diff --git a/native/tools/swig-config/scene.i b/native/tools/swig-config/scene.i index e84367d3111..6b5868393df 100644 --- a/native/tools/swig-config/scene.i +++ b/native/tools/swig-config/scene.i @@ -518,6 +518,7 @@ using namespace cc; %attribute(cc::scene::Model, int32_t, reflectionProbeId, getReflectionProbeId, setReflectionProbeId); %attribute(cc::scene::Model, int32_t, reflectionProbeBlendId, getReflectionProbeBlendId, setReflectionProbeBlendId); %attribute(cc::scene::Model, float, reflectionProbeBlendWeight, getReflectionProbeBlendWeight, setReflectionProbeBlendWeight); +%attribute(cc::scene::Model, bool, gpuDrivenEnabled, isGPUDrivenEnabled, setGPUDrivenEnabled); %attribute(cc::scene::SubModel, cc::scene::SharedPassArray &, passes, getPasses, setPasses); %attribute(cc::scene::SubModel, ccstd::vector> &, shaders, getShaders, setShaders);