mirror of
https://github.com/visgl/luma.gl.git
synced 2026-01-18 14:03:42 +00:00
feat: Add shader inputs (uniform buffer) debug trace (#1918)
This commit is contained in:
parent
ab4f510bf9
commit
f6848a93b0
@ -1,8 +1,8 @@
|
||||
//
|
||||
//
|
||||
import type {ShaderUniformType, NumberArray} from '@luma.gl/core';
|
||||
import {Device, Framebuffer, makeRandomNumberGenerator, UniformStore, glsl} from '@luma.gl/core';
|
||||
import {Device, Framebuffer, makeRandomNumberGenerator, glsl} from '@luma.gl/core';
|
||||
import type {AnimationProps, ModelProps} from '@luma.gl/engine';
|
||||
import {AnimationLoopTemplate, CubeGeometry, Timeline, Model} from '@luma.gl/engine';
|
||||
import {AnimationLoopTemplate, CubeGeometry, Timeline, Model, _ShaderInputs} from '@luma.gl/engine';
|
||||
import {readPixelsToArray} from '@luma.gl/webgl';
|
||||
import {picking, dirlight} from '@luma.gl/shadertools';
|
||||
import {Matrix4, radians} from '@math.gl/core';
|
||||
@ -39,19 +39,6 @@ uniform appUniforms {
|
||||
out vec3 color;
|
||||
|
||||
void main(void) {
|
||||
// vec3 normal = vec3(uModel * vec4(normals, 1.0));
|
||||
|
||||
// // Set up data for modules
|
||||
// color = instanceColors;
|
||||
// project_setNormal(normal);
|
||||
// // vec4 pickColor = vec4(0., instancePickingColors, 1.0);
|
||||
// picking_setPickingColor(vec3(0., instancePickingColors));
|
||||
|
||||
// // Vertex position (z coordinate undulates with time), and model rotates around center
|
||||
// float delta = length(instanceOffsets);
|
||||
// vec4 offset = vec4(instanceOffsets, sin((uTime + delta) * 0.1) * 16.0, 0);
|
||||
// gl_Position = uProjection * uView * (uModel * vec4(positions * 1., 1.0) + offset);
|
||||
// Set up data for modules
|
||||
color = instanceColors;
|
||||
|
||||
vec3 normal = vec3(app.modelMatrix * vec4(normals, 1.0));
|
||||
@ -72,7 +59,6 @@ const fs = glsl`\
|
||||
precision highp float;
|
||||
|
||||
in vec3 color;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main(void) {
|
||||
@ -86,7 +72,6 @@ const SIDE = 256;
|
||||
|
||||
// Make a cube with 65K instances and attributes to control offset and color of each instance
|
||||
class InstancedCube extends Model {
|
||||
|
||||
// uniformBuffer: Buffer;
|
||||
|
||||
constructor(device: Device, props?: Partial<ModelProps>) {
|
||||
@ -139,8 +124,8 @@ class InstancedCube extends Model {
|
||||
bufferLayout: [
|
||||
{name: 'instanceOffsets', format: 'float32x2'},
|
||||
{name: 'instanceColors', format: 'unorm8x4'},
|
||||
{name: 'instancePickingColors', format: 'unorm8x2'},
|
||||
// TODO - normalizing picking colors breaks picking
|
||||
{name: 'instancePickingColors', format: 'unorm8x2'}
|
||||
// TODO - normalizing picking colors breaks picking
|
||||
// {name: 'instancePickingColors', format: 'unorm8x2'},
|
||||
],
|
||||
attributes: {
|
||||
@ -182,10 +167,10 @@ export default class AppAnimationLoopTemplate extends AnimationLoopTemplate {
|
||||
timelineChannels: Record<string, number>;
|
||||
pickingFramebuffer: Framebuffer;
|
||||
|
||||
uniformStore = new UniformStore<{
|
||||
app: AppUniforms,
|
||||
dirlight: typeof dirlight.uniforms,
|
||||
picking: typeof picking.uniforms
|
||||
shaderInputs = new _ShaderInputs<{
|
||||
app: AppUniforms;
|
||||
dirlight: typeof dirlight.props;
|
||||
picking: typeof picking.props;
|
||||
}>({
|
||||
app,
|
||||
dirlight,
|
||||
@ -210,13 +195,10 @@ export default class AppAnimationLoopTemplate extends AnimationLoopTemplate {
|
||||
colorAttachments: ['rgba8unorm'],
|
||||
depthStencilAttachment: 'depth24plus'
|
||||
});
|
||||
|
||||
|
||||
this.cube = new InstancedCube(device, {
|
||||
bindings: {
|
||||
app: this.uniformStore.getManagedUniformBuffer(device, 'app'),
|
||||
dirlight: this.uniformStore.getManagedUniformBuffer(device, 'dirlight'),
|
||||
picking: this.uniformStore.getManagedUniformBuffer(device, 'picking'),
|
||||
}
|
||||
// @ts-ignore
|
||||
shaderInputs: this.shaderInputs
|
||||
});
|
||||
}
|
||||
|
||||
@ -225,11 +207,16 @@ export default class AppAnimationLoopTemplate extends AnimationLoopTemplate {
|
||||
const {_mousePosition} = animationProps;
|
||||
const {timeChannel, eyeXChannel, eyeYChannel, eyeZChannel} = this.timelineChannels;
|
||||
|
||||
this.uniformStore.setUniforms({
|
||||
this.shaderInputs.setProps({
|
||||
app: {
|
||||
time: this.timeline.getTime(timeChannel),
|
||||
// Basic projection matrix
|
||||
projectionMatrix: new Matrix4().perspective({fovy: radians(60), aspect, near: 1, far: 2048.0}),
|
||||
projectionMatrix: new Matrix4().perspective({
|
||||
fovy: radians(60),
|
||||
aspect,
|
||||
near: 1,
|
||||
far: 2048.0
|
||||
}),
|
||||
// Move the eye around the plane
|
||||
viewMatrix: new Matrix4().lookAt({
|
||||
center: [0, 0, 0],
|
||||
@ -244,9 +231,7 @@ export default class AppAnimationLoopTemplate extends AnimationLoopTemplate {
|
||||
}
|
||||
});
|
||||
|
||||
if (_mousePosition) {
|
||||
this.pickInstance(device, _mousePosition, this.cube, this.pickingFramebuffer);
|
||||
}
|
||||
this.pickInstance(device, _mousePosition, this.cube, this.pickingFramebuffer);
|
||||
|
||||
// Draw the cubes
|
||||
const renderPass = device.beginRenderPass({
|
||||
@ -265,25 +250,34 @@ export default class AppAnimationLoopTemplate extends AnimationLoopTemplate {
|
||||
|
||||
pickInstance(
|
||||
device: Device,
|
||||
mousePosition: number[],
|
||||
mousePosition: number[] | null | undefined,
|
||||
model: Model,
|
||||
framebuffer: Framebuffer
|
||||
) {
|
||||
if (!mousePosition) {
|
||||
this.shaderInputs.setProps({picking: {highlightedObjectColor: null}});
|
||||
return;
|
||||
}
|
||||
|
||||
// use the center pixel location in device pixel range
|
||||
const devicePixels = device.canvasContext.cssToDevicePixels(mousePosition);
|
||||
const devicePixels = device.canvasContext!.cssToDevicePixels(mousePosition);
|
||||
const pickX = devicePixels.x + Math.floor(devicePixels.width / 2);
|
||||
const pickY = devicePixels.y + Math.floor(devicePixels.height / 2);
|
||||
|
||||
// Render picking colors
|
||||
framebuffer.resize(device.canvasContext.getPixelSize());
|
||||
framebuffer.resize(device.canvasContext!.getPixelSize());
|
||||
|
||||
this.uniformStore.setUniforms({picking: {isActive: true}});
|
||||
this.shaderInputs.setProps({picking: {isActive: true}});
|
||||
|
||||
const pickingPass = device.beginRenderPass({framebuffer, clearColor: [0, 0, 0, 0], clearDepth: 1});
|
||||
const pickingPass = device.beginRenderPass({
|
||||
framebuffer,
|
||||
clearColor: [0, 0, 0, 0],
|
||||
clearDepth: 1
|
||||
});
|
||||
model.draw(pickingPass);
|
||||
pickingPass.end();
|
||||
|
||||
// Read back
|
||||
// Read back
|
||||
const color255 = readPixelsToArray(framebuffer, {
|
||||
sourceX: pickX,
|
||||
sourceY: pickY,
|
||||
@ -292,10 +286,12 @@ export default class AppAnimationLoopTemplate extends AnimationLoopTemplate {
|
||||
});
|
||||
// console.log(color255);
|
||||
|
||||
const highlightedObjectColor = new Float32Array(color255).map((x) => x / 255);
|
||||
const isHighlightActive = highlightedObjectColor[0] + highlightedObjectColor[1] + highlightedObjectColor[2] > 0;
|
||||
|
||||
this.uniformStore.setUniforms({picking: {isActive: false, isHighlightActive, highlightedObjectColor}});
|
||||
}
|
||||
}
|
||||
const highlightedObjectColor = new Float32Array(color255).map(x => x / 255);
|
||||
const isHighlightActive =
|
||||
highlightedObjectColor[0] + highlightedObjectColor[1] + highlightedObjectColor[2] > 0;
|
||||
|
||||
this.shaderInputs.setProps({
|
||||
picking: {isActive: false, isHighlightActive, highlightedObjectColor}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,10 +163,6 @@ export {requestAnimationFrame, cancelAnimationFrame} from './utils/request-anima
|
||||
*/
|
||||
export const glsl = (x: TemplateStringsArray) => `${x}`;
|
||||
|
||||
// DEBUG
|
||||
|
||||
export {getDebugTableForShaderLayout} from './lib/debug/debug-shader-layout';
|
||||
|
||||
// INTERNAL
|
||||
|
||||
export type {
|
||||
|
||||
@ -14,7 +14,7 @@ declare global {
|
||||
function initializeLuma(): string {
|
||||
// Version detection using babel plugin
|
||||
// @ts-expect-error
|
||||
const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'untranspiled source';
|
||||
const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'running from source';
|
||||
|
||||
const STARTUP_MESSAGE = 'set luma.log.level=1 (or higher) to trace rendering';
|
||||
// Assign luma.log.level in console to control logging: \
|
||||
@ -27,7 +27,7 @@ function initializeLuma(): string {
|
||||
|
||||
if (!globalThis.luma) {
|
||||
if (isBrowser()) {
|
||||
log.log(1, `luma.gl ${VERSION} - ${STARTUP_MESSAGE}`)();
|
||||
log.log(1, `${VERSION} - ${STARTUP_MESSAGE}`)();
|
||||
}
|
||||
|
||||
globalThis.luma = globalThis.luma || {
|
||||
|
||||
@ -22,6 +22,3 @@ import './lib/uniforms/uniform-buffer-layout.spec';
|
||||
|
||||
// compiler logs
|
||||
import './lib/compiler-log/format-compiler-log.spec';
|
||||
|
||||
// debug
|
||||
import './lib/debug/get-debug-table-from-shader-layout.spec';
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
import type {ShaderLayout} from '../../adapter/types/shader-layout';
|
||||
// luma.gl
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) vis.gl contributors
|
||||
|
||||
import type {ShaderLayout} from '@luma.gl/core';
|
||||
|
||||
/**
|
||||
* Extracts a table suitable for `console.table()` from a shader layout to assist in debugging.
|
||||
@ -6,13 +6,14 @@ import type {BufferLayout, VertexArray, TransformFeedback} from '@luma.gl/core';
|
||||
import type {AttributeInfo, Binding, UniformValue, PrimitiveTopology} from '@luma.gl/core';
|
||||
import {Device, Buffer, RenderPipeline, RenderPass, UniformStore} from '@luma.gl/core';
|
||||
import {log, uid, deepEqual, splitUniformsAndBindings} from '@luma.gl/core';
|
||||
import {getAttributeInfosFromLayouts, getDebugTableForShaderLayout} from '@luma.gl/core';
|
||||
import {getAttributeInfosFromLayouts} from '@luma.gl/core';
|
||||
import type {ShaderModule, PlatformInfo} from '@luma.gl/shadertools';
|
||||
import {ShaderAssembler} from '@luma.gl/shadertools';
|
||||
import {ShaderInputs} from '../shader-inputs';
|
||||
import type {Geometry} from '../geometry/geometry';
|
||||
import {GPUGeometry, makeGPUGeometry} from '../geometry/gpu-geometry';
|
||||
import {PipelineFactory} from '../lib/pipeline-factory';
|
||||
import {getDebugTableForShaderLayout} from '../debug/debug-shader-layout';
|
||||
|
||||
const LOG_DRAW_PRIORITY = 2;
|
||||
const LOG_DRAW_TIMEOUT = 10000;
|
||||
@ -304,7 +305,7 @@ export class Model {
|
||||
_setGeometryAttributes(gpuGeometry: GPUGeometry): void {
|
||||
// TODO - delete previous geometry?
|
||||
this.vertexCount = gpuGeometry.vertexCount;
|
||||
this.setAttributes(gpuGeometry.attributes);
|
||||
this.setAttributes(gpuGeometry.attributes, 'ignore-unknown');
|
||||
this.setIndexBuffer(gpuGeometry.indices);
|
||||
}
|
||||
|
||||
@ -438,7 +439,7 @@ export class Model {
|
||||
* Sets attributes (buffers)
|
||||
* @note Overrides any attributes previously set with the same name
|
||||
*/
|
||||
setAttributes(buffers: Record<string, Buffer>): void {
|
||||
setAttributes(buffers: Record<string, Buffer>, _option?: 'ignore-unknown'): void {
|
||||
if (buffers.indices) {
|
||||
log.warn(
|
||||
`Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`
|
||||
@ -463,7 +464,7 @@ export class Model {
|
||||
set = true;
|
||||
}
|
||||
}
|
||||
if (!set) {
|
||||
if (!set && _option !== 'ignore-unknown') {
|
||||
log.warn(
|
||||
`Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`
|
||||
)();
|
||||
@ -552,6 +553,13 @@ export class Model {
|
||||
// log.table(logLevel, uniformTable)();
|
||||
log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
|
||||
|
||||
const uniformTable = this.shaderInputs.getDebugTable();
|
||||
// Add any global uniforms
|
||||
for (const [name, value] of Object.entries(this.uniforms)) {
|
||||
uniformTable[name] = {value};
|
||||
}
|
||||
log.table(LOG_DRAW_PRIORITY, uniformTable)();
|
||||
|
||||
log.groupEnd(LOG_DRAW_PRIORITY)();
|
||||
this._logOpen = false;
|
||||
}
|
||||
|
||||
@ -134,4 +134,17 @@ export class ShaderInputs<
|
||||
}
|
||||
return bindings;
|
||||
}
|
||||
|
||||
getDebugTable(): Record<string, Record<string, unknown>> {
|
||||
const table: Record<string,Record<string, unknown>> = {};
|
||||
for (const [moduleName, module] of Object.entries(this.moduleUniforms)) {
|
||||
for (const [key, value] of Object.entries(module)) {
|
||||
table[`${moduleName}.${key}`] = {
|
||||
type: this.modules[moduleName].uniformTypes?.[key],
|
||||
value: String(value)
|
||||
};
|
||||
}
|
||||
}
|
||||
return table;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
// Copyright (c) vis.gl contributors
|
||||
|
||||
import test from 'tape-promise/tape';
|
||||
import {getDebugTableForShaderLayout, ShaderLayout} from '@luma.gl/core';
|
||||
|
||||
import type {ShaderLayout} from '@luma.gl/core';
|
||||
import {getDebugTableForShaderLayout} from '../../src/debug/debug-shader-layout';
|
||||
|
||||
const SHADER_LAYOUT: ShaderLayout = {
|
||||
attributes: [
|
||||
@ -20,3 +20,7 @@ import './scenegraph/model-node.spec';
|
||||
import './shader-inputs.spec';
|
||||
import './transform/buffer-transform.spec';
|
||||
import './transform/texture-transform.spec';
|
||||
|
||||
|
||||
// debug
|
||||
import './debug/get-debug-table-from-shader-layout.spec';
|
||||
|
||||
@ -38,9 +38,13 @@ function initializeExtensions(gl: WebGLRenderingContext): void {
|
||||
const contextState = getContextData(gl);
|
||||
// `getSupportedExtensions` can return null when context is lost.
|
||||
const EXTENSIONS = gl.getSupportedExtensions() || [];
|
||||
// Generates warnings in Chrome
|
||||
const IGNORE_EXTENSIONS = ['WEBGL_polygon_mode'];
|
||||
for (const extensionName of EXTENSIONS) {
|
||||
const extension = gl.getExtension(extensionName);
|
||||
contextState._extensions[extensionName] = extension;
|
||||
if (!IGNORE_EXTENSIONS.includes(extensionName)) {
|
||||
const extension = gl.getExtension(extensionName);
|
||||
contextState._extensions[extensionName] = extension;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user