Skip to content

Minor improvements for WebGL 2 and development. #317

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
52 changes: 37 additions & 15 deletions src/javascript/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,26 @@ function checkObject (object) {
(object === undefined)
}

function isInstanceOfType (instance, type) {
return Object.prototype.toString.call(instance) === `[object ${type}]`
}

function checkUniform (program, location) {
return location instanceof WebGLUniformLocation &&
location._program === program &&
location._linkCount === program._linkCount
}

function isTypedArray (data) {
return data instanceof Uint8Array ||
data instanceof Uint8ClampedArray ||
data instanceof Int8Array ||
data instanceof Uint16Array ||
data instanceof Int16Array ||
data instanceof Uint32Array ||
data instanceof Int32Array ||
data instanceof Float32Array ||
data instanceof Float64Array
return isInstanceOfType(data, 'Uint8Array') ||
Copy link
Member

@dhritzkiv dhritzkiv May 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you briefly describe the issues in node with checking if it's a typed array with instanceof ? Something to do with tests / use in a vm module?

This looks fine, I'm more curious for myself – and perhaps there's a another, simpler yet still foolproof version.

isInstanceOfType(data, 'Uint8ClampedArray') ||
isInstanceOfType(data, 'Int8Array') ||
isInstanceOfType(data, 'Uint16Array') ||
isInstanceOfType(data, 'Int16Array') ||
isInstanceOfType(data, 'Uint32Array') ||
isInstanceOfType(data, 'Int32Array') ||
isInstanceOfType(data, 'Float32Array') ||
isInstanceOfType(data, 'Float64Array')
}

// Don't allow: ", $, `, @, \, ', \0
Expand Down Expand Up @@ -159,6 +163,23 @@ function extractImageData (pixels) {
return null
}

function convertPixelFormats (ctx, pixels, srcFormat, dstFormat) {
switch (srcFormat) {
case ctx.RGBA:
switch (dstFormat) {
case ctx.RGBA:
return pixels
case ctx.RED:
// extract the red channel from pixels, which is in typed array format, and convert to Uint8Array
return new Uint8Array(pixels.filter((_, i) => i % 4 === 0))
default:
throw new Error('unsupported destination format')
}
default:
throw new Error('unsupported source format')
}
}

function formatSize (internalFormat) {
switch (internalFormat) {
case gl.ALPHA:
Expand All @@ -176,14 +197,14 @@ function formatSize (internalFormat) {

function convertPixels (pixels) {
if (typeof pixels === 'object' && pixels !== null) {
if (pixels instanceof ArrayBuffer) {
if (isInstanceOfType(pixels, 'ArrayBuffer')) {
return new Uint8Array(pixels)
} else if (pixels instanceof Uint8Array ||
pixels instanceof Uint16Array ||
pixels instanceof Uint8ClampedArray ||
pixels instanceof Float32Array) {
} else if (isInstanceOfType(pixels, 'Uint8Array') ||
isInstanceOfType(pixels, 'Uint16Array') ||
isInstanceOfType(pixels, 'Uint8ClampedArray') ||
isInstanceOfType(pixels, 'Float32Array')) {
return unpackTypedArray(pixels)
} else if (pixels instanceof Buffer) {
} else if (isInstanceOfType(pixels, 'Buffer')) {
return new Uint8Array(pixels)
}
}
Expand Down Expand Up @@ -218,6 +239,7 @@ module.exports = {
uniformTypeSize,
unpackTypedArray,
extractImageData,
convertPixelFormats,
formatSize,
checkFormat,
checkUniform,
Expand Down
27 changes: 27 additions & 0 deletions src/javascript/webgl-rendering-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const {
typeSize,
uniformTypeSize,
extractImageData,
convertPixelFormats,
isTypedArray,
unpackTypedArray,
convertPixels,
Expand Down Expand Up @@ -2296,6 +2297,8 @@ class WebGLRenderingContextHelper extends NativeWebGLRenderingContext {
width = pixels.width
height = pixels.height
pixels = pixels.data

pixels = convertPixelFormats(this, pixels, this.RGBA, format)
}

target |= 0
Expand Down Expand Up @@ -2366,6 +2369,8 @@ class WebGLRenderingContextHelper extends NativeWebGLRenderingContext {
width = pixels.width
height = pixels.height
pixels = pixels.data

pixels = convertPixelFormats(this, pixels, this.RGBA, format)
}

if (typeof pixels !== 'object') {
Expand All @@ -2386,6 +2391,28 @@ class WebGLRenderingContextHelper extends NativeWebGLRenderingContext {
data)
}

texSubImage3D (target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels) {
if (pixels === null || pixels === undefined) {
return
}

if (typeof pixels !== 'object') {
throw new TypeError('texSubImage3D(GLenum, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLenum, GLenum, Uint8Array)')
}

if (
typeof pixels === 'object' &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likely don't need this line, as it's checked for in the previous if statement

typeof pixels.width !== 'undefined' &&
typeof pixels.height !== 'undefined'
) {
pixels = extractImageData(pixels).data
pixels = convertPixelFormats(this, pixels, this.RGBA, format)
}
const data = convertPixels(pixels)

super.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data)
}

texParameterf (target, pname, param) {
target |= 0
pname |= 0
Expand Down
62 changes: 51 additions & 11 deletions src/native/webgl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@

#include "webgl.h"

#if defined(RENDERDOC_ENABLED)
#include "renderdoc_app.h"

RENDERDOC_API_1_1_2 *rdoc_api = NULL;
#endif

const char *GetDebugMessageSourceString(GLenum source) {
switch (source) {
case GL_DEBUG_SOURCE_API:
Expand Down Expand Up @@ -143,6 +149,19 @@ std::string JoinStringSet(const std::set<std::string> &inputSet,
return oss.str();
}

// Helper function to potentially end RenderDoc frame capture after a few draw calls
#if defined(RENDERDOC_ENABLED)
static void MaybeEndRenderDocCapture() {
static int drawCallCounter = 0;
if (drawCallCounter++ == 100 && rdoc_api) {
rdoc_api->EndFrameCapture(NULL, NULL);
printf("RenderDoc capture ended after %d draw calls.\n", drawCallCounter);
drawCallCounter = 0;
rdoc_api->StartFrameCapture(NULL, NULL);
}
}
#endif

bool WebGLRenderingContext::HAS_DISPLAY = false;
EGLDisplay WebGLRenderingContext::DISPLAY;
WebGLRenderingContext *WebGLRenderingContext::ACTIVE = NULL;
Expand Down Expand Up @@ -191,6 +210,23 @@ WebGLRenderingContext::WebGLRenderingContext(int width, int height, bool alpha,
unpack_colorspace_conversion(0x9244), unpack_alignment(4),
webGLToANGLEExtensions(&CaseInsensitiveCompare), next(NULL), prev(NULL) {

// Uncomment on Windows to attach debugger
// MessageBox(NULL, "Hello", "Hello", MB_OK);

#if defined(RENDERDOC_ENABLED)
if (!rdoc_api) {
if (HMODULE mod = GetModuleHandleA("renderdoc.dll")) {
pRENDERDOC_GetAPI RENDERDOC_GetAPI =
(pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI");
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void **)&rdoc_api);
printf("RENDERDOC_GetAPI ret: %d %p\n", ret, rdoc_api);
assert(ret == 1);
} else {
printf("renderdoc.dll not found\n");
}
}
#endif

if (!eglGetProcAddress) {
if (!eglLibrary.open("libEGL")) {
errorMessage = "Error opening ANGLE shared library.";
Expand Down Expand Up @@ -342,6 +378,12 @@ WebGLRenderingContext::WebGLRenderingContext(int width, int height, bool alpha,
supportedWebGLExtensions.insert(webGLExtension);
}
}

#if defined(RENDERDOC_ENABLED)
if (rdoc_api) {
rdoc_api->StartFrameCapture(NULL, NULL);
}
#endif
}

bool WebGLRenderingContext::setActive() {
Expand Down Expand Up @@ -1056,6 +1098,10 @@ GL_METHOD(TexSubImage2D) {
Nan::TypedArrayContents<unsigned char> pixels(info[8]);

if (inst->unpack_flip_y || inst->unpack_premultiply_alpha) {
if (!*pixels) {
inst->setError(GL_INVALID_OPERATION);
return;
}
std::vector<uint8_t> unpacked = inst->unpackPixels(type, format, width, height, *pixels);
glTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, width, height, format, type,
unpacked.size(), unpacked.data());
Expand Down Expand Up @@ -2410,6 +2456,7 @@ GL_METHOD(TexImage3D) {
GLint border = Nan::To<int32_t>(info[6]).ToChecked();
GLenum format = Nan::To<int32_t>(info[7]).ToChecked();
GLenum type = Nan::To<int32_t>(info[8]).ToChecked();

if (info[9]->IsUndefined()) {
glTexImage3D(target, level, internalformat, width, height, depth, border, format, type,
nullptr);
Expand All @@ -2435,17 +2482,10 @@ GL_METHOD(TexSubImage3D) {
GLsizei depth = Nan::To<int32_t>(info[7]).ToChecked();
GLenum format = Nan::To<int32_t>(info[8]).ToChecked();
GLenum type = Nan::To<int32_t>(info[9]).ToChecked();
if (info[10]->IsUndefined()) {
glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type,
nullptr);
} else if (info[10]->IsArrayBufferView()) {
auto buffer = info[10].As<v8::ArrayBufferView>();
void *bufferPtr = buffer->Buffer()->GetBackingStore()->Data();
glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type,
bufferPtr);
} else {
Nan::ThrowTypeError("Invalid data type for TexSubImage3D");
}
Nan::TypedArrayContents<unsigned char> pixels(info[10]);

glTexSubImage3DRobustANGLE(target, level, xoffset, yoffset, zoffset, width, height, depth, format,
type, pixels.length(), *pixels);
}

GL_METHOD(CopyTexSubImage3D) {
Expand Down