Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 69 additions & 14 deletions Sources/Rendering/OpenGL/RenderWindow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,63 @@ function checkRenderTargetSupport(gl, internalFormat, format, type) {
return status === gl.FRAMEBUFFER_COMPLETE;
}

// ----------------------------------------------------------------------------
// GL informations helpers
// ----------------------------------------------------------------------------

// Returns a factory of lazy getters for the parameters of a WebGL extension.
// The extension is only requested (once) when one of the getters is first
// called. This matters for extensions that are costly or noisy to request,
// e.g. Firefox logs a deprecation warning whenever WEBGL_debug_renderer_info
// is requested.
function createLazyExtensionParameterGetter(gl, extensionName) {
let extension;
return (parameterName) => () => {
if (extension === undefined) {
extension = gl.getExtension(extensionName);
}
return extension && gl.getParameter(extension[parameterName]);
};
}

// Defines an enumerable `object[name]` property that is only evaluated with
// `evaluate()` on first access, then cached as a regular writable value.
function defineLazyProperty(object, name, evaluate) {
Object.defineProperty(object, name, {
get() {
const value = evaluate();
Object.defineProperty(object, name, {
value,
enumerable: true,
writable: true,
});
return value;
},
enumerable: true,
configurable: true,
});
}

// Converts a list of [label, key, value] entries into a { [key]: { label,
// value } } map. A function value is evaluated lazily, on first read of the
// entry value. Entries without a key are skipped.
function buildGLInformations(params) {
const result = {};
while (params.length) {
const [label, key, value] = params.pop();
if (key) {
if (typeof value === 'function') {
const entry = { label };
defineLazyProperty(entry, 'value', value);
result[key] = entry;
} else {
result[key] = { label, value };
}
}
}
return result;
}

// ----------------------------------------------------------------------------
// Monitor the usage of GL context across vtkOpenGLRenderWindow instances
// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -663,7 +720,14 @@ function vtkOpenGLRenderWindow(publicAPI, model) {
// Float/halfFloat textures and multiple render targets are core in
// WebGL2; only float color renderability still hangs off an extension.
const glColorBufferFloat = gl.getExtension('EXT_color_buffer_float');
const glDebugRendererInfo = gl.getExtension('WEBGL_debug_renderer_info');
// Firefox deprecated WEBGL_debug_renderer_info (RENDERER/VENDOR already
// report the unmasked strings there) and logs a warning every time the
// extension is requested, so only request it when the unmasked values
// are actually read.
const getDebugRendererInfoParameter = createLazyExtensionParameterGetter(
gl,
'WEBGL_debug_renderer_info'
);
const glAnisotropic =
gl.getExtension('EXT_texture_filter_anisotropic') ||
gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic');
Expand Down Expand Up @@ -998,27 +1062,18 @@ function vtkOpenGLRenderWindow(publicAPI, model) {
[
'Unmasked Renderer',
'UNMASKED_RENDERER',
glDebugRendererInfo &&
gl.getParameter(glDebugRendererInfo.UNMASKED_RENDERER_WEBGL),
getDebugRendererInfoParameter('UNMASKED_RENDERER_WEBGL'),
],
[
'Unmasked Vendor',
'UNMASKED_VENDOR',
glDebugRendererInfo &&
gl.getParameter(glDebugRendererInfo.UNMASKED_VENDOR_WEBGL),
getDebugRendererInfoParameter('UNMASKED_VENDOR_WEBGL'),
],
['WebGL Version', 'WEBGL_VERSION', 2],
];

const result = {};
while (params.length) {
const [label, key, value] = params.pop();
if (key) {
result[key] = { label, value };
}
}
model._glInformation = result;
return result;
model._glInformation = buildGLInformations(params);
return model._glInformation;
};

publicAPI.traverseAllPasses = () => {
Expand Down
56 changes: 56 additions & 0 deletions Sources/Rendering/OpenGL/RenderWindow/test/testGLInformations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { it, expect, vi } from 'vitest';

import vtkOpenGLRenderWindow from 'vtk.js/Sources/Rendering/OpenGL/RenderWindow';

it.skipIf(__VTK_TEST_NO_WEBGL__)(
'only requests WEBGL_debug_renderer_info when unmasked values are read',
() => {
const getExtension = vi.spyOn(
WebGL2RenderingContext.prototype,
'getExtension'
);
const glWindow = vtkOpenGLRenderWindow.newInstance();

try {
const info = glWindow.getGLInformations();
expect(info.RENDERER.value).toBeTruthy();
expect(info.UNMASKED_RENDERER.label).toBe('Unmasked Renderer');
// Firefox logs a deprecation warning whenever this extension is
// requested, so getting the regular GL informations must not do it.
expect(getExtension).not.toHaveBeenCalledWith(
'WEBGL_debug_renderer_info'
);

const unmaskedRenderer = info.UNMASKED_RENDERER.value;
const unmaskedVendor = info.UNMASKED_VENDOR.value;
expect(getExtension).toHaveBeenCalledWith('WEBGL_debug_renderer_info');
const calls = getExtension.mock.calls.filter(
([name]) => name === 'WEBGL_debug_renderer_info'
).length;
expect(calls).toBe(1);

const debugInfo = glWindow
.get3DContext()
.getExtension('WEBGL_debug_renderer_info');
if (debugInfo) {
const gl = glWindow.get3DContext();
expect(unmaskedRenderer).toBe(
gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL)
);
expect(unmaskedVendor).toBe(
gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL)
);
} else {
expect(unmaskedRenderer).toBeFalsy();
expect(unmaskedVendor).toBeFalsy();
}

// values are cached and enumerable like the other entries
expect(info.UNMASKED_RENDERER.value).toBe(unmaskedRenderer);
expect(Object.keys(info.UNMASKED_RENDERER)).toEqual(['label', 'value']);
} finally {
getExtension.mockRestore();
glWindow.delete();
}
}
);
Loading