第13.2章:UBO内存布局优化

Uniform Buffer Object(UBO)是现代图形API中重要的性能优化技术。本教程将深入讲解UBO的内存布局规则、优化策略和在Cocos Creator中的实际应用。

🎯 学习目标

  • 理解UBO的工作原理和内存布局规则
  • 掌握GLSL中UBO的标准布局(std140)
  • 学会设计高效的UBO数据结构
  • 了解UBO在不同场景中的优化应�?

📋 前置知识

  • 熟悉OpenGL/WebGL渲染管线
  • 理解GPU内存架构
  • 掌握GLSL编程基础

🔧 UBO基础概念

UBO vs 传统Uniform

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 传统Uniform方式的问�?class TraditionalUniforms {
public setMaterialUniforms(material: Material): void {
// 每次都需要单独设置每个uniform
this.gl.uniform3fv(this.locations.albedo, material.albedo);
this.gl.uniform1f(this.locations.metallic, material.metallic);
this.gl.uniform1f(this.locations.roughness, material.roughness);
this.gl.uniform3fv(this.locations.emission, material.emission);
// ... 20多个uniform调用

// 问题�? // 1. CPU开销�?- 每个uniform都是一次API调用
// 2. 状态切换频�?- GPU状态机频繁变化
// 3. 难以批量操作 - 无法一次性更新多个材�? }
}

// UBO方式的优�?class UBOUniforms {
public setMaterialUniforms(materials: Material[]): void {
// 一次性更新多个材质到UBO
const uboData = this.packMaterialsToUBO(materials);
this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, this.materialUBO);
this.gl.bufferSubData(this.gl.UNIFORM_BUFFER, 0, uboData);

// 优势�? // 1. 减少API调用 - 一次bufferSubData替代多次uniform调用
// 2. 批量更新 - 一次性更新多个对象的数据
// 3. 内存对齐 - GPU友好的内存布局
// 4. 状态缓�?- UBO可以在多个着色器间共�? }
}

UBO内存布局规则(std140)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// GLSL std140布局规则示例
CCProgram ubo-layout-examples %{
// std140布局规则�? // - 基础类型(float, int, bool)�?字节对齐
// - vec2�?字节对齐
// - vec3, vec4�?6字节对齐
// - mat3:按3个vec3处理,每�?6字节对齐
// - mat4�?4字节对齐
// - 数组:每个元素按照最大对齐要求对�?
layout(std140) uniform MaterialData {
// 偏移�? 0, 大小: 4
float metallic;

// 偏移�? 4, 大小: 4 (自动填充�?字节边界)
float roughness;

// 偏移�? 8, 大小: 8
vec2 uvScale;

// 偏移�? 16, 大小: 12 (但占�?6字节)
vec3 albedo;

// 偏移�? 32, 大小: 16
vec4 emission;

// 偏移�? 48, 大小: 64
mat4 textureTransform;

// 偏移�? 112, 数组每个元素16字节对齐
vec3 lightColors[4]; // 实际占用64字节
};

// 内存布局可视化:
// 0-3: metallic (4字节)
// 4-7: roughness (4字节)
// 8-15: uvScale (8字节)
// 16-31: albedo (12字节实际 + 4字节填充)
// 32-47: emission (16字节)
// 48-111: textureTransform (64字节)
// 112-175: lightColors[4] (每个16字节 * 4)
// 总大�? 176字节
}%

🏗�?UBO设计模式

分层UBO架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 全局渲染数据UBO
layout(std140) uniform GlobalRenderData {
mat4 viewMatrix;
mat4 projMatrix;
mat4 viewProjMatrix;
mat4 invViewMatrix;
mat4 invProjMatrix;

vec4 cameraPos;
vec4 cameraDir;
vec4 screenSize; // xy: size, zw: inv_size
vec4 timeData; // x: time, y: deltaTime, z: frameCount, w: unused

vec4 fogParams; // x: start, y: end, z: density, w: type
vec4 fogColor;

// 环境光数�? vec4 ambientSky;
vec4 ambientGround;
vec4 ambientEquator;
} u_globalData;

// 光照数据UBO
layout(std140) uniform LightingData {
// 主光�? vec4 mainLightDir;
vec4 mainLightColor;

// 点光源数�? vec4 pointLightPositions[8]; // xyz: position, w: range
vec4 pointLightColors[8]; // xyz: color, w: intensity

// 聚光灯数�? vec4 spotLightPositions[4]; // xyz: position, w: range
vec4 spotLightDirections[4]; // xyz: direction, w: angle
vec4 spotLightColors[4]; // xyz: color, w: intensity

// 光照统计
ivec4 lightCounts; // x: point, y: spot, z: area, w: total
} u_lightingData;

// 材质数据UBO
layout(std140) uniform MaterialData {
vec4 albedo; // rgb: color, a: transparency
vec4 pbrParams; // x: metallic, y: roughness, z: ao, w: unused
vec4 emission; // rgb: emission, a: intensity
vec4 uvTransform; // xy: scale, zw: offset

// 材质标志�? ivec4 materialFlags; // x: hasNormalMap, y: hasEmission, z: hasAO, w: blendMode

// 动画参数
vec4 animParams; // 用于材质动画
} u_materialData;

// 实例化数据UBO
layout(std140) uniform InstanceData {
mat4 instanceMatrices[100]; // 每个实例的变换矩�? vec4 instanceColors[100]; // 每个实例的颜�? vec4 instanceParams[100]; // 每个实例的自定义参数
} u_instanceData;

TypeScript UBO管理�?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// UBO内存布局管理�?class UBOLayoutManager {
interface UBOField {
name: string;
type: 'float' | 'vec2' | 'vec3' | 'vec4' | 'mat3' | 'mat4' | 'int' | 'ivec2' | 'ivec3' | 'ivec4';
arraySize?: number;
offset: number;
size: number;
alignment: number;
}

interface UBOLayout {
name: string;
fields: UBOField[];
totalSize: number;
alignment: number;
}

private static readonly STD140_ALIGNMENTS = {
'float': 4,
'vec2': 8,
'vec3': 16,
'vec4': 16,
'mat3': 16,
'mat4': 16,
'int': 4,
'ivec2': 8,
'ivec3': 16,
'ivec4': 16
};

private static readonly STD140_SIZES = {
'float': 4,
'vec2': 8,
'vec3': 12,
'vec4': 16,
'mat3': 48, // 3 * vec3, each padded to 16 bytes
'mat4': 64,
'int': 4,
'ivec2': 8,
'ivec3': 12,
'ivec4': 16
};

public static calculateLayout(fields: Partial<UBOField>[]): UBOLayout {
const layout: UBOLayout = {
name: '',
fields: [],
totalSize: 0,
alignment: 16
};

let currentOffset = 0;

for (const fieldDef of fields) {
const field: UBOField = {
name: fieldDef.name!,
type: fieldDef.type!,
arraySize: fieldDef.arraySize || 1,
offset: 0,
size: 0,
alignment: 0
};

// 计算对齐和大�? const baseAlignment = this.STD140_ALIGNMENTS[field.type];
const baseSize = this.STD140_SIZES[field.type];

field.alignment = baseAlignment;

if (field.arraySize > 1) {
// 数组:每个元素按照最大对齐要求对�? const elementAlignment = Math.max(baseAlignment, 16);
field.alignment = elementAlignment;
field.size = elementAlignment * field.arraySize;
} else {
field.size = baseSize;
}

// 计算偏移量(必须对齐�? currentOffset = this.alignOffset(currentOffset, field.alignment);
field.offset = currentOffset;

layout.fields.push(field);
currentOffset += field.size;
}

// UBO总大小必须是16的倍数
layout.totalSize = this.alignOffset(currentOffset, 16);

return layout;
}

private static alignOffset(offset: number, alignment: number): number {
return Math.ceil(offset / alignment) * alignment;
}

public static generateUBOStruct(layout: UBOLayout, uboName: string): string {
let glslCode = `layout(std140) uniform ${uboName} {\n`;

for (const field of layout.fields) {
const arrayPart = field.arraySize > 1 ? `[${field.arraySize}]` : '';
glslCode += ` ${field.type} ${field.name}${arrayPart};\n`;
}

glslCode += '};\n';

// 添加注释显示内存布局
glslCode += `\n// Memory Layout (std140):\n`;
glslCode += `// Total Size: ${layout.totalSize} bytes\n`;

for (const field of layout.fields) {
glslCode += `// ${field.name}: offset=${field.offset}, size=${field.size}, align=${field.alignment}\n`;
}

return glslCode;
}
}

// UBO数据打包�?class UBODataPacker {
private buffer: ArrayBuffer;
private view: DataView;
private layout: UBOLayout;

constructor(layout: UBOLayout) {
this.layout = layout;
this.buffer = new ArrayBuffer(layout.totalSize);
this.view = new DataView(this.buffer);
}

public setFloat(fieldName: string, value: number, arrayIndex: number = 0): void {
const field = this.getField(fieldName);
const offset = field.offset + arrayIndex * 16; // 数组元素间距至少16字节
this.view.setFloat32(offset, value, true);
}

public setVec2(fieldName: string, value: [number, number], arrayIndex: number = 0): void {
const field = this.getField(fieldName);
const offset = field.offset + arrayIndex * 16;
this.view.setFloat32(offset + 0, value[0], true);
this.view.setFloat32(offset + 4, value[1], true);
}

public setVec3(fieldName: string, value: [number, number, number], arrayIndex: number = 0): void {
const field = this.getField(fieldName);
const offset = field.offset + arrayIndex * 16;
this.view.setFloat32(offset + 0, value[0], true);
this.view.setFloat32(offset + 4, value[1], true);
this.view.setFloat32(offset + 8, value[2], true);
}

public setVec4(fieldName: string, value: [number, number, number, number], arrayIndex: number = 0): void {
const field = this.getField(fieldName);
const offset = field.offset + arrayIndex * 16;
this.view.setFloat32(offset + 0, value[0], true);
this.view.setFloat32(offset + 4, value[1], true);
this.view.setFloat32(offset + 8, value[2], true);
this.view.setFloat32(offset + 12, value[3], true);
}

public setMat4(fieldName: string, matrix: Float32Array | number[], arrayIndex: number = 0): void {
const field = this.getField(fieldName);
const offset = field.offset + arrayIndex * 64;

// mat4按列存储
for (let i = 0; i < 16; i++) {
this.view.setFloat32(offset + i * 4, matrix[i], true);
}
}

public setInt(fieldName: string, value: number, arrayIndex: number = 0): void {
const field = this.getField(fieldName);
const offset = field.offset + arrayIndex * 16;
this.view.setInt32(offset, value, true);
}

private getField(fieldName: string): UBOField {
const field = this.layout.fields.find(f => f.name === fieldName);
if (!field) {
throw new Error(`UBO字段不存�? ${fieldName}`);
}
return field;
}

public getBuffer(): ArrayBuffer {
return this.buffer;
}

public getUint8Array(): Uint8Array {
return new Uint8Array(this.buffer);
}
}

🎮 实际应用示例

材质批处理系�?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// 材质批处理UBO系统
class MaterialBatchingSystem {
private gl: WebGL2RenderingContext;
private materialUBO: WebGLBuffer;
private materialLayout: UBOLayout;
private materialPacker: UBODataPacker;
private maxMaterials: number = 256;

interface BatchedMaterial {
id: string;
albedo: vec4;
pbrParams: vec4; // metallic, roughness, ao, unused
emission: vec4;
uvTransform: vec4;
flags: ivec4;
batchIndex: number;
}

constructor(gl: WebGL2RenderingContext) {
this.gl = gl;
this.initMaterialUBO();
}

private initMaterialUBO(): void {
// 定义材质UBO布局
const materialFields = [
{ name: 'albedos', type: 'vec4' as const, arraySize: this.maxMaterials },
{ name: 'pbrParams', type: 'vec4' as const, arraySize: this.maxMaterials },
{ name: 'emissions', type: 'vec4' as const, arraySize: this.maxMaterials },
{ name: 'uvTransforms', type: 'vec4' as const, arraySize: this.maxMaterials },
{ name: 'materialFlags', type: 'ivec4' as const, arraySize: this.maxMaterials }
];

this.materialLayout = UBOLayoutManager.calculateLayout(materialFields);
this.materialPacker = new UBODataPacker(this.materialLayout);

// 创建GPU缓冲�? this.materialUBO = this.gl.createBuffer()!;
this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, this.materialUBO);
this.gl.bufferData(this.gl.UNIFORM_BUFFER, this.materialLayout.totalSize, this.gl.DYNAMIC_DRAW);

console.log(`🎨 材质UBO初始化完�?`);
console.log(` 最大材质数: ${this.maxMaterials}`);
console.log(` 总内存大�? ${(this.materialLayout.totalSize / 1024).toFixed(2)}KB`);
console.log(` 布局:\n${UBOLayoutManager.generateUBOStruct(this.materialLayout, 'MaterialBatch')}`);
}

public batchMaterials(materials: Material[]): BatchedMaterial[] {
console.log(`🔄 开始批处理 ${materials.length} 个材�?..`);

if (materials.length > this.maxMaterials) {
console.warn(`材质数量 ${materials.length} 超过最大限�?${this.maxMaterials},将截断`);
materials = materials.slice(0, this.maxMaterials);
}

const batchedMaterials: BatchedMaterial[] = [];

// 打包材质数据
materials.forEach((material, index) => {
const batchedMaterial: BatchedMaterial = {
id: material.id,
albedo: material.albedo,
pbrParams: [material.metallic, material.roughness, material.ao, 0],
emission: material.emission,
uvTransform: material.uvTransform,
flags: this.encodeMaterialFlags(material),
batchIndex: index
};

// 写入UBO数据
this.materialPacker.setVec4('albedos', batchedMaterial.albedo, index);
this.materialPacker.setVec4('pbrParams', batchedMaterial.pbrParams, index);
this.materialPacker.setVec4('emissions', batchedMaterial.emission, index);
this.materialPacker.setVec4('uvTransforms', batchedMaterial.uvTransform, index);
this.materialPacker.setInt('materialFlags', this.packFlags(batchedMaterial.flags), index);

batchedMaterials.push(batchedMaterial);
});

// 上传到GPU
this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, this.materialUBO);
this.gl.bufferSubData(this.gl.UNIFORM_BUFFER, 0, this.materialPacker.getUint8Array());

console.log(`�?材质批处理完成,批次大小: ${batchedMaterials.length}`);
return batchedMaterials;
}

private encodeMaterialFlags(material: Material): ivec4 {
return [
material.hasNormalMap ? 1 : 0,
material.hasEmission ? 1 : 0,
material.hasAO ? 1 : 0,
material.blendMode
];
}

private packFlags(flags: ivec4): number {
// �?个int打包成一�?2位整�? return (flags[0] & 0xFF) |
((flags[1] & 0xFF) << 8) |
((flags[2] & 0xFF) << 16) |
((flags[3] & 0xFF) << 24);
}

public bindMaterialUBO(bindingPoint: number): void {
this.gl.bindBufferBase(this.gl.UNIFORM_BUFFER, bindingPoint, this.materialUBO);
}
}

实例化UBO优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// 优化的实例化着色器(使用UBO�?CCProgram optimized-instanced-vs %{
precision highp float;

// 顶点属�? in vec3 a_position;
in vec3 a_normal;
in vec2 a_texCoord;
in int a_instanceId; // 实例索引

// 全局UBO
layout(std140) uniform GlobalData {
mat4 viewMatrix;
mat4 projMatrix;
mat4 viewProjMatrix;
vec4 cameraPos;
vec4 timeData;
} u_global;

// 实例化UBO
layout(std140) uniform InstanceData {
mat4 instanceMatrices[256];
vec4 instanceColors[256];
vec4 instanceParams[256]; // xyz: scale, rotation, custom; w: materialIndex
} u_instances;

// 材质UBO
layout(std140) uniform MaterialData {
vec4 albedos[64];
vec4 pbrParams[64];
vec4 emissions[64];
vec4 uvTransforms[64];
} u_materials;

out vec3 v_worldPos;
out vec3 v_normal;
out vec2 v_uv;
out vec4 v_instanceColor;
out vec4 v_materialData; // xy: metallic/roughness, zw: uv_scale

void vert() {
// 获取实例数据
mat4 instanceMatrix = u_instances.instanceMatrices[a_instanceId];
vec4 instanceColor = u_instances.instanceColors[a_instanceId];
vec4 instanceParams = u_instances.instanceParams[a_instanceId];

// 材质索引
int materialIndex = int(instanceParams.w);
vec4 uvTransform = u_materials.uvTransforms[materialIndex];
vec4 pbrData = u_materials.pbrParams[materialIndex];

// 变换顶点
vec4 worldPos = instanceMatrix * vec4(a_position, 1.0);
v_worldPos = worldPos.xyz;

// 变换法线
v_normal = normalize((instanceMatrix * vec4(a_normal, 0.0)).xyz);

// UV变换
v_uv = a_texCoord * uvTransform.xy + uvTransform.zw;

// 传递数�? v_instanceColor = instanceColor;
v_materialData = vec4(pbrData.xy, uvTransform.xy);

gl_Position = u_global.viewProjMatrix * worldPos;
}
}%

CCProgram optimized-instanced-fs %{
precision highp float;

in vec3 v_worldPos;
in vec3 v_normal;
in vec2 v_uv;
in vec4 v_instanceColor;
in vec4 v_materialData;

layout(location = 0) out vec4 fragColor;

uniform sampler2D mainTexture;

// 光照UBO
layout(std140) uniform LightingData {
vec4 mainLightDir;
vec4 mainLightColor;
vec4 ambientColor;
} u_lighting;

void frag() {
vec4 baseColor = texture(mainTexture, v_uv);
baseColor *= v_instanceColor;

// 从vertex shader获取材质参数
float metallic = v_materialData.x;
float roughness = v_materialData.y;

// 简单光�? vec3 normal = normalize(v_normal);
vec3 lightDir = normalize(-u_lighting.mainLightDir.xyz);
float NdotL = max(dot(normal, lightDir), 0.0);

vec3 lighting = u_lighting.mainLightColor.rgb * NdotL + u_lighting.ambientColor.rgb;

fragColor = vec4(baseColor.rgb * lighting, baseColor.a);
}
}%

📊 UBO性能优化策略

动态UBO更新系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// 智能UBO更新管理�?class SmartUBOUpdater {
private dirtyRegions: Map<string, DirtyRegion[]> = new Map();
private updateQueues: Map<string, UpdateQueue> = new Map();

interface DirtyRegion {
offset: number;
size: number;
priority: number;
framesSinceDirty: number;
}

interface UpdateQueue {
immediate: DirtyRegion[];
deferred: DirtyRegion[];
nextFrame: DirtyRegion[];
}

public markDirty(uboName: string, offset: number, size: number, priority: 'immediate' | 'high' | 'normal' | 'low' = 'normal'): void {
const dirtyRegion: DirtyRegion = {
offset: offset,
size: size,
priority: this.getPriorityValue(priority),
framesSinceDirty: 0
};

let regions = this.dirtyRegions.get(uboName) || [];

// 合并重叠区域
regions = this.mergeOverlappingRegions(regions, dirtyRegion);
this.dirtyRegions.set(uboName, regions);

// 加入更新队列
this.queueUpdate(uboName, dirtyRegion, priority);
}

private mergeOverlappingRegions(existing: DirtyRegion[], newRegion: DirtyRegion): DirtyRegion[] {
const merged: DirtyRegion[] = [];
let hasOverlap = false;

for (const region of existing) {
if (this.regionsOverlap(region, newRegion)) {
// 合并区域
const mergedRegion: DirtyRegion = {
offset: Math.min(region.offset, newRegion.offset),
size: Math.max(region.offset + region.size, newRegion.offset + newRegion.size) - Math.min(region.offset, newRegion.offset),
priority: Math.max(region.priority, newRegion.priority),
framesSinceDirty: Math.min(region.framesSinceDirty, newRegion.framesSinceDirty)
};
merged.push(mergedRegion);
hasOverlap = true;
} else {
merged.push(region);
}
}

if (!hasOverlap) {
merged.push(newRegion);
}

return merged;
}

private regionsOverlap(a: DirtyRegion, b: DirtyRegion): boolean {
return !(a.offset + a.size <= b.offset || b.offset + b.size <= a.offset);
}

public updateUBOs(frameTimebudget: number = 2.0): void {
const startTime = performance.now();

// 按优先级处理更新
for (const [uboName, queue] of this.updateQueues.entries()) {
if (performance.now() - startTime > frameTimebudget) {
console.log(`�?UBO更新时间预算用完,推迟剩余更新`);
break;
}

// 立即更新
if (queue.immediate.length > 0) {
this.performUBOUpdate(uboName, queue.immediate);
queue.immediate = [];
}

// 高优先级更新
if (queue.deferred.length > 0) {
const updateCount = Math.min(queue.deferred.length, 3); // 限制每帧更新数量
const toUpdate = queue.deferred.splice(0, updateCount);
this.performUBOUpdate(uboName, toUpdate);
}
}

// 老化dirty区域
this.ageDirtyRegions();
}

private performUBOUpdate(uboName: string, regions: DirtyRegion[]): void {
if (regions.length === 0) return;

// 优化:合并相邻的更新区域
const mergedRegions = this.mergeAdjacentRegions(regions);

console.log(`🔄 更新UBO ${uboName},区域数: ${mergedRegions.length}`);

mergedRegions.forEach(region => {
// 这里执行实际的GPU缓冲区更�? // this.gl.bufferSubData(this.gl.UNIFORM_BUFFER, region.offset, data);
console.log(` 更新区域: offset=${region.offset}, size=${region.size}`);
});
}

private mergeAdjacentRegions(regions: DirtyRegion[]): DirtyRegion[] {
if (regions.length <= 1) return regions;

// 按偏移量排序
regions.sort((a, b) => a.offset - b.offset);

const merged: DirtyRegion[] = [regions[0]];

for (let i = 1; i < regions.length; i++) {
const current = regions[i];
const last = merged[merged.length - 1];

// 如果区域相邻或重叠,合并它们
if (current.offset <= last.offset + last.size + 64) { // 64字节间隙内也合并
last.size = Math.max(last.offset + last.size, current.offset + current.size) - last.offset;
last.priority = Math.max(last.priority, current.priority);
} else {
merged.push(current);
}
}

return merged;
}

public getMemoryUsageStats(): UBOMemoryStats {
let totalAllocated = 0;
let totalDirty = 0;
let totalRegions = 0;

for (const [uboName, regions] of this.dirtyRegions.entries()) {
totalRegions += regions.length;
totalDirty += regions.reduce((sum, region) => sum + region.size, 0);
// 这里需要从UBO管理器获取总大�? // totalAllocated += this.getUBOSize(uboName);
}

return {
totalAllocated: totalAllocated,
totalDirty: totalDirty,
dirtyRatio: totalAllocated > 0 ? totalDirty / totalAllocated : 0,
activeRegions: totalRegions,
updatePressure: this.calculateUpdatePressure()
};
}

private calculateUpdatePressure(): number {
let totalPressure = 0;
let totalRegions = 0;

for (const regions of this.dirtyRegions.values()) {
for (const region of regions) {
totalPressure += region.priority * (region.framesSinceDirty + 1);
totalRegions++;
}
}

return totalRegions > 0 ? totalPressure / totalRegions : 0;
}
}

interface UBOMemoryStats {
totalAllocated: number;
totalDirty: number;
dirtyRatio: number;
activeRegions: number;
updatePressure: number;
}

📝 本章小结

通过本教程,你应该掌握了�?

  1. UBO原理: 理解UBO的工作机制和std140布局规则
  2. 内存优化: 学会设计高效的UBO数据结构
  3. *批处理系�?: 掌握材质和实例的批处理技�?4. 性能监控: 了解UBO性能分析和优化策�?5. 实际应用: 学会在复杂场景中合理使用UBO

🚀 下一步学�?

继续学习多pass渲染技术!🎮�?