Skip to content

Commit a8fa031

Browse files
recude code size
1 parent 8ee32e7 commit a8fa031

3 files changed

Lines changed: 37 additions & 43 deletions

File tree

cocos/rendering/instanced-buffer.ts

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,6 @@ export function instancingCompareFn (l: InstancedBuffer, r: InstancedBuffer): nu
3939
return (ls.hash - rs.hash) || (ls.shaderId - rs.shaderId);
4040
}
4141

42-
// Combines a running hash `seed` with `value` into a single 32-bit integer.
43-
// Mirrors the native `ccstd::hash_combine` formula so merge keys can be plain
44-
// numbers instead of formatted strings, avoiding per-merge string allocation.
45-
function hashCombine (seed: number, value: number): number {
46-
// eslint-disable-next-line no-bitwise
47-
return (seed ^ (value + 0x9e3779b9 + (seed << 6) + (seed >> 2))) | 0;
48-
}
49-
5042
export interface IInstancedItem {
5143
count: number;
5244
capacity: number;
@@ -74,7 +66,7 @@ export class InstancedBuffer {
7466
public sortRender: IRenderPass;
7567
private declare _passPool: RecyclePool<IRenderPass>;
7668
private declare _device: Device;
77-
private readonly _instanceMap = new Map<number, IInstancedItem[]>();
69+
private readonly _instanceMap = new Map<string, IInstancedItem[]>();
7870
constructor (pass: Pass) {
7971
this._device = pass.device;
8072
this.pass = pass;
@@ -118,13 +110,9 @@ export class InstancedBuffer {
118110
this.sortRender.shaderId = shader.typedID;
119111
this.sortRender.passIdx = passIdx;
120112

121-
let key = hashCombine(2, sourceIA.indexBuffer?.objectID ?? 0);
122-
key = hashCombine(key, lightingMap.objectID);
123-
key = hashCombine(key, useReflectionProbeType);
124-
key = hashCombine(key, reflectionProbeCubemap.objectID);
125-
key = hashCombine(key, reflectionProbePlanarMap.objectID);
126-
key = hashCombine(key, ENABLE_PROBE_BLEND ? reflectionProbeBlendCubemap!.objectID : 0);
127-
key = hashCombine(key, stride);
113+
const key = `${sourceIA.indexBuffer?.objectID ?? 0}/${lightingMap.objectID}/${useReflectionProbeType}/`
114+
+ `${reflectionProbeCubemap.objectID}/${reflectionProbePlanarMap.objectID}/`
115+
+ `${ENABLE_PROBE_BLEND ? reflectionProbeBlendCubemap!.objectID : 0}/${stride}`;
128116
const mappedInstances = this._instanceMap.get(key);
129117
if (mappedInstances) {
130118
for (let i = 0; i < mappedInstances.length; ++i) {
@@ -167,7 +155,7 @@ export class InstancedBuffer {
167155
}
168156

169157
private _createInstance (
170-
key: number,
158+
key: string,
171159
sourceIA: InputAssembler,
172160
instancedAttributes: Attribute[],
173161
instanceData: Uint8Array,

native/cocos/renderer/pipeline/InstancedBuffer.cpp

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "gfx-base/GFXDescriptorSet.h"
3232
#include "gfx-base/GFXDevice.h"
3333
#include "gfx-base/GFXInputAssembler.h"
34+
#include "base/StringUtil.h"
3435

3536
namespace cc {
3637
namespace pipeline {
@@ -84,26 +85,24 @@ void InstancedBuffer::merge(scene::SubModel *subModel, uint32_t passIdx, gfx::Sh
8485
_sortRender.hash = hash;
8586
_sortRender.shaderID = shaderId;
8687
_sortRender.passIndex = passIdx;
87-
const ccstd::hash_t key = [&]() {
88-
ccstd::hash_t seed = 2;
89-
ccstd::hash_combine(seed, gfx::GFXObject::getObjectID(sourceIA->getIndexBuffer()));
90-
ccstd::hash_combine(seed, gfx::GFXObject::getObjectID(lightingMap));
91-
ccstd::hash_combine(seed, reflectionProbeType);
92-
ccstd::hash_combine(seed, gfx::GFXObject::getObjectID(reflectionProbeCubemap));
93-
ccstd::hash_combine(seed, gfx::GFXObject::getObjectID(reflectionProbePlanarMap));
94-
ccstd::hash_combine(seed, gfx::GFXObject::getObjectID(reflectionProbeBlendCubemap));
95-
ccstd::hash_combine(seed, stride);
96-
return seed;
97-
}();
88+
const ccstd::string key = StringUtil::format("%u/%u/%u/%u/%u/%u/%u",
89+
sourceIA->getIndexBuffer() ? sourceIA->getIndexBuffer()->getObjectID() : 0,
90+
lightingMap ? lightingMap->getObjectID() : 0,
91+
reflectionProbeType,
92+
reflectionProbeCubemap ? reflectionProbeCubemap->getObjectID() : 0,
93+
reflectionProbePlanarMap ? reflectionProbePlanarMap->getObjectID() : 0,
94+
reflectionProbeBlendCubemap ? reflectionProbeBlendCubemap->getObjectID() : 0,
95+
stride);
9896
const auto iter = _instancesMap.find(key);
9997
if (iter != _instancesMap.end()) {
100-
for (size_t idx : iter->second) {
98+
uint32_t idx = iter->second;
99+
while (idx != InstancedItem::INVALID_INDEX) {
101100
auto &instance = _instances[idx];
102-
if (instance.drawInfo.instanceCount >= MAX_CAPACITY) {
103-
continue;
101+
if (instance.drawInfo.instanceCount < MAX_CAPACITY) {
102+
appendInstance(instance, attrs.buffer, shader, descriptorSet);
103+
return;
104104
}
105-
appendInstance(instance, attrs.buffer, shader, descriptorSet);
106-
return;
105+
idx = instance.nextWithSameKey;
107106
}
108107
}
109108

@@ -147,7 +146,7 @@ void InstancedBuffer::appendInstance(InstancedItem &instance,
147146
_hasPendingModels = true;
148147
}
149148

150-
void InstancedBuffer::createInstance(ccstd::hash_t key,
149+
void InstancedBuffer::createInstance(const ccstd::string &key,
151150
gfx::InputAssembler *sourceIA,
152151
const ccstd::vector<gfx::Attribute> &attributes,
153152
Uint8Array &buffer,
@@ -194,8 +193,10 @@ void InstancedBuffer::createInstance(ccstd::hash_t key,
194193
reflectionProbeBlendCubemap,
195194
ia->getDrawInfo()};
196195
item.drawInfo.instanceCount = 1;
196+
const auto headIter = _instancesMap.find(key);
197+
item.nextWithSameKey = headIter != _instancesMap.end() ? headIter->second : InstancedItem::INVALID_INDEX;
197198
_instances.emplace_back(item);
198-
_instancesMap[key].emplace_back(_instances.size() - 1);
199+
_instancesMap[key] = static_cast<uint32_t>(_instances.size() - 1);
199200
_hasPendingModels = true;
200201
}
201202

native/cocos/renderer/pipeline/InstancedBuffer.h

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
#include "Define.h"
2828
#include "base/RefCounted.h"
2929
#include "base/std/container/unordered_map.h"
30-
#include "base/std/hash/hash.h"
3130
#include "scene/Model.h"
3231
#include "scene/Pass.h"
3332

@@ -39,6 +38,8 @@ namespace pipeline {
3938
struct PSOInfo;
4039

4140
struct CC_DLL InstancedItem {
41+
static constexpr uint32_t INVALID_INDEX = 0xFFFFFFFF;
42+
4243
uint32_t capacity = 0;
4344
gfx::Buffer *vb = nullptr;
4445
uint8_t *data = nullptr;
@@ -52,6 +53,11 @@ struct CC_DLL InstancedItem {
5253
uint32_t reflectionProbeType = 0;
5354
gfx::Texture *reflectionProbeBlendCubemap = nullptr;
5455
gfx::DrawInfo drawInfo;
56+
// Index (into `InstancedBuffer::_instances`) of the next item sharing the same merge
57+
// key, or `INVALID_INDEX` if this is the last one. Forms an intrusive singly-linked
58+
// list per key, so `_instancesMap` only needs to store one head index per key instead
59+
// of a `ccstd::vector<size_t>`, avoiding an extra container template instantiation.
60+
uint32_t nextWithSameKey = INVALID_INDEX;
5561
};
5662
using InstancedItemList = ccstd::vector<InstancedItem>;
5763
using DynamicOffsetList = ccstd::vector<uint32_t>;
@@ -83,7 +89,7 @@ class InstancedBuffer : public RefCounted {
8389
Uint8Array &buffer,
8490
gfx::Shader *shader,
8591
gfx::DescriptorSet *descriptorSet);
86-
void createInstance(ccstd::hash_t key,
92+
void createInstance(const ccstd::string &key,
8793
gfx::InputAssembler *sourceIA,
8894
const ccstd::vector<gfx::Attribute> &attributes,
8995
Uint8Array &buffer,
@@ -106,12 +112,11 @@ class InstancedBuffer : public RefCounted {
106112

107113
InstancedItemList _instances;
108114
DynamicOffsetList _dynamicOffsets;
109-
// Maps a merge key to the indices (into `_instances`) of items sharing that key.
110-
// Storing indices (not copies or references) keeps this in sync with `_instances`
111-
// and remains valid across `_instances` reallocation, since indices don't dangle.
112-
// The key is a combined hash (see `ccstd::hash_combine`) instead of a formatted
113-
// string, avoiding per-merge string allocation/formatting overhead.
114-
ccstd::unordered_map<ccstd::hash_t, ccstd::vector<size_t>> _instancesMap;
115+
// Maps a merge key to the index (into `_instances`) of the head item with that key.
116+
// Remaining items sharing the key are reached via `InstancedItem::nextWithSameKey`,
117+
// so this map only needs `uint32_t` values instead of `ccstd::vector<size_t>`,
118+
// avoiding an extra map-of-vectors template instantiation.
119+
ccstd::unordered_map<ccstd::string, uint32_t> _instancesMap;
115120
};
116121

117122
} // namespace pipeline

0 commit comments

Comments
 (0)