Skip to content

Commit 66c3464

Browse files
committed
Fix MiniStats rendering with multiple cameras
1 parent aa0aee3 commit 66c3464

3 files changed

Lines changed: 118 additions & 30 deletions

File tree

src/extras/mini-stats/mini-stats.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { math } from '../../core/math/math.js';
22
import { Texture } from '../../platform/graphics/texture.js';
33
import { ADDRESS_REPEAT, FILTER_NEAREST } from '../../platform/graphics/constants.js';
4-
import { LAYERID_UI } from '../../scene/constants.js';
54
import { CpuTimer } from './cpu-timer.js';
65
import { GpuTimer } from './gpu-timer.js';
76
import { StatsTimer } from './stats-timer.js';
@@ -11,6 +10,7 @@ import { Render2d } from './render2d.js';
1110

1211
/**
1312
* @import { AppBase } from '../../framework/app-base.js'
13+
* @import { FrameGraph } from '../../scene/frame-graph.js'
1414
* @import { GraphicsDevice } from '../../platform/graphics/graphics-device.js'
1515
*/
1616

@@ -157,9 +157,9 @@ class MiniStats {
157157
device.on('resizecanvas', this.updateDiv, this);
158158
device.on('losecontext', this.loseContext, this);
159159
app.on('postrender', this.postRender, this);
160+
app.on('framegraphbuild', this._frameGraphBuild, this);
160161

161162
this.app = app;
162-
this.drawLayer = app.scene.layers.getLayerById(LAYERID_UI);
163163
this.device = device;
164164
this.render2d = new Render2d(device);
165165
this.div = div;
@@ -201,13 +201,15 @@ class MiniStats {
201201
this.device.off('resizecanvas', this.updateDiv, this);
202202
this.device.off('losecontext', this.loseContext, this);
203203
this.app.off('postrender', this.postRender, this);
204+
this.app.off('framegraphbuild', this._frameGraphBuild, this);
204205

205206
this.graphs.forEach(graph => graph.destroy());
206207
this.gpuPassGraphs.clear();
207208
this.cpuGraphs.clear();
208209
this.vramGraphs.clear();
209210
this.wordAtlas.destroy();
210211
this.texture.destroy();
212+
this.render2d.destroy();
211213
this.div.remove();
212214
}
213215

@@ -550,7 +552,21 @@ class MiniStats {
550552
}
551553
}
552554

553-
render2d.render(this.app, this.drawLayer, this.texture, this.wordAtlas.texture, this.clr, height);
555+
render2d.render(this.texture, this.wordAtlas.texture, this.clr);
556+
}
557+
558+
/**
559+
* Called when the frame graph has been built, to add the overlay render pass at the end of the
560+
* frame. This keeps the overlay independent of cameras and layers, rendering it over the whole
561+
* backbuffer.
562+
*
563+
* @param {FrameGraph} frameGraph - The frame graph.
564+
* @private
565+
*/
566+
_frameGraphBuild(frameGraph) {
567+
if (this._enabled && this.render2d.prim.count > 0) {
568+
frameGraph.addRenderPass(this.render2d.renderPass);
569+
}
554570
}
555571

556572
/**

src/extras/mini-stats/render2d.js

Lines changed: 95 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
BINDGROUP_MESH, BINDGROUP_MESH_UB, BINDGROUP_VIEW,
23
BLENDEQUATION_ADD, BLENDMODE_SRC_ALPHA, BLENDMODE_ONE_MINUS_SRC_ALPHA, BLENDMODE_ONE,
34
BUFFER_STATIC,
45
BUFFER_STREAM,
@@ -12,13 +13,14 @@ import {
1213
import { Debug } from '../../core/debug.js';
1314
import { DepthState } from '../../platform/graphics/depth-state.js';
1415
import { BlendState } from '../../platform/graphics/blend-state.js';
15-
import { GraphNode } from '../../scene/graph-node.js';
16-
import { MeshInstance } from '../../scene/mesh-instance.js';
17-
import { Mesh } from '../../scene/mesh.js';
16+
import { BindGroup, DynamicBindGroup } from '../../platform/graphics/bind-group.js';
1817
import { IndexBuffer } from '../../platform/graphics/index-buffer.js';
18+
import { RenderPass } from '../../platform/graphics/render-pass.js';
19+
import { ShaderProcessorOptions } from '../../platform/graphics/shader-processor-options.js';
20+
import { UniformBuffer } from '../../platform/graphics/uniform-buffer.js';
1921
import { VertexBuffer } from '../../platform/graphics/vertex-buffer.js';
2022
import { VertexFormat } from '../../platform/graphics/vertex-format.js';
21-
import { ShaderMaterial } from '../../scene/materials/shader-material.js';
23+
import { ShaderUtils } from '../../scene/shader-lib/shader-utils.js';
2224

2325
// Graph colors for MiniStats
2426
const graphColorDefault = '1.0, 0.412, 0.380'; // Pastel Red
@@ -154,6 +156,26 @@ const fragmentShaderWGSL = /* wgsl */ `
154156
}
155157
`;
156158

159+
const dynamicBindGroup = new DynamicBindGroup();
160+
161+
// render pass drawing the overlay quads directly to the backbuffer, after the frame has rendered.
162+
// This makes the overlay independent of cameras and layers, with a full-canvas viewport.
163+
class RenderPassMiniStats extends RenderPass {
164+
constructor(device, render2d) {
165+
super(device);
166+
this.render2d = render2d;
167+
168+
// render to the backbuffer, preserving its content
169+
this.init(null);
170+
this.colorOps.clear = false;
171+
this.depthStencilOps.clearDepth = false;
172+
}
173+
174+
execute() {
175+
this.render2d._draw();
176+
}
177+
}
178+
157179
// render 2d textured quads
158180
class Render2d {
159181
constructor(device, maxQuads = 2048) {
@@ -189,30 +211,33 @@ class Render2d {
189211
};
190212
this.quads = 0;
191213

192-
this.mesh = new Mesh(device);
193-
this.mesh.vertexBuffer = this.buffer;
194-
this.mesh.indexBuffer[0] = this.indexBuffer;
195-
this.mesh.primitive = [this.prim];
196-
197-
const material = new ShaderMaterial({
214+
let shader = ShaderUtils.createShader(device, {
198215
uniqueName: 'MiniStats',
199-
vertexGLSL: vertexShaderGLSL,
200-
fragmentGLSL: fragmentShaderGLSL,
201-
vertexWGSL: vertexShaderWGSL,
202-
fragmentWGSL: fragmentShaderWGSL,
203216
attributes: {
204217
vertex_position: SEMANTIC_POSITION,
205218
vertex_texCoord0: SEMANTIC_TEXCOORD0
206-
}
219+
},
220+
vertexGLSL: vertexShaderGLSL,
221+
fragmentGLSL: fragmentShaderGLSL,
222+
vertexWGSL: vertexShaderWGSL,
223+
fragmentWGSL: fragmentShaderWGSL
207224
});
208-
this.material = material;
209-
material.cull = CULLFACE_NONE;
210-
material.depthState = DepthState.NODEPTH;
211-
material.blendState = new BlendState(true, BLENDEQUATION_ADD, BLENDMODE_SRC_ALPHA, BLENDMODE_ONE_MINUS_SRC_ALPHA,
225+
226+
// devices using uniform buffers need the shader processed and bind groups set up
227+
if (device.supportsUniformBuffers) {
228+
shader = ShaderUtils.processShader(shader, new ShaderProcessorOptions());
229+
const ubFormat = shader.meshUniformBufferFormat;
230+
if (ubFormat) {
231+
this.uniformBuffer = new UniformBuffer(device, ubFormat, false);
232+
}
233+
this.bindGroup = new BindGroup(device, shader.meshBindGroupFormat);
234+
}
235+
this.shader = shader;
236+
237+
this.blendState = new BlendState(true, BLENDEQUATION_ADD, BLENDMODE_SRC_ALPHA, BLENDMODE_ONE_MINUS_SRC_ALPHA,
212238
BLENDEQUATION_ADD, BLENDMODE_ONE, BLENDMODE_ONE);
213-
material.update();
214239

215-
this.meshInstance = new MeshInstance(this.mesh, material, new GraphNode('MiniStatsMesh'));
240+
this.renderPass = new RenderPassMiniStats(device, this);
216241

217242
this.uniforms = {
218243
clr: new Float32Array(4)
@@ -224,6 +249,14 @@ class Render2d {
224249
};
225250
}
226251

252+
destroy() {
253+
this.renderPass.destroy();
254+
this.uniformBuffer?.destroy();
255+
this.bindGroup?.destroy();
256+
this.buffer.destroy();
257+
this.indexBuffer.destroy();
258+
}
259+
227260
quad(x, y, w, h, u, v, uw, uh, texture, wordFlag = 0) {
228261
// bounds check to prevent buffer overflow
229262
if (this.quads >= this.maxQuads) {
@@ -264,19 +297,54 @@ class Render2d {
264297
this.targetSize.height = this.device.canvas.scrollHeight;
265298
}
266299

267-
render(app, layer, graphTexture, wordsTexture, clr, height) {
300+
render(graphTexture, wordsTexture, clr) {
268301

269302
// set vertex data (swap storage)
270303
this.buffer.setData(this.data.buffer);
271304

272305
this.uniforms.clr.set(clr, 0);
306+
this.graphTexture = graphTexture;
307+
this.wordsTexture = wordsTexture;
308+
}
309+
310+
// called by the render pass to draw the prepared quads
311+
_draw() {
312+
const { device, prim } = this;
313+
if (!prim.count) {
314+
return;
315+
}
273316

274-
// material params
275-
this.material.setParameter('clr', this.uniforms.clr);
276-
this.material.setParameter('graphTex', graphTexture);
277-
this.material.setParameter('wordsTex', wordsTexture);
317+
// full backbuffer viewport, as a viewport of a previous camera can be active when the
318+
// frame graph merges this pass into the previous one
319+
const { width, height } = device;
320+
device.setViewport(0, 0, width, height);
321+
device.setScissor(0, 0, width, height);
322+
323+
device.setDrawStates(this.blendState, DepthState.NODEPTH, CULLFACE_NONE);
324+
325+
const scope = device.scope;
326+
scope.resolve('clr').setValue(this.uniforms.clr);
327+
scope.resolve('graphTex').setValue(this.graphTexture);
328+
scope.resolve('wordsTex').setValue(this.wordsTexture);
329+
330+
device.setVertexBuffer(this.buffer);
331+
device.setShader(this.shader);
332+
333+
if (device.supportsUniformBuffers) {
334+
device.setBindGroup(BINDGROUP_VIEW, device.emptyBindGroup);
335+
336+
this.bindGroup.update();
337+
device.setBindGroup(BINDGROUP_MESH, this.bindGroup);
338+
339+
if (this.uniformBuffer) {
340+
this.uniformBuffer.update(dynamicBindGroup);
341+
device.setBindGroup(BINDGROUP_MESH_UB, dynamicBindGroup.bindGroup, dynamicBindGroup.offsets);
342+
} else {
343+
device.setBindGroup(BINDGROUP_MESH_UB, device.emptyBindGroup);
344+
}
345+
}
278346

279-
app.drawMeshInstance(this.meshInstance, layer);
347+
device.draw(prim, this.indexBuffer);
280348
}
281349
}
282350

src/framework/app-base.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,10 @@ class AppBase extends EventHandler {
11561156
this.renderer.update(layerComposition);
11571157

11581158
this.renderer.buildFrameGraph(this.frameGraph, layerComposition);
1159+
1160+
// allow render passes to be added to the frame graph before it renders
1161+
this.fire('framegraphbuild', this.frameGraph);
1162+
11591163
this.frameGraph.render(this.graphicsDevice);
11601164
}
11611165

0 commit comments

Comments
 (0)