Skip to content
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

Hello, WebGL 2.0 Compute #7612

Closed
wants to merge 11 commits into from

Conversation

hujiajie
Copy link
Contributor

@hujiajie hujiajie commented Dec 5, 2018

WebGL 2.0 Compute is an experimental superset of WebGL 2.0, provided by Chrome/Chromium behind the flag --enable-webgl2-compute-context. It exposes OpenGL ES 3.1 functionality, notably compute shader, to WebGL via the webgl2-compute context[1][2]. The patchset adds a new flag -s USE_WEBGL2_COMPUTE= and implements preliminary support for this feature, in the hope that it can benefit applications in the field of machine learning, image processing, gaming, etc. A ray tracing demo is also provided as a test, but sadly it's skipped in CI because the Mesa software renderer is too slow and lacks certain OpenGL extensions for WebGL 2.0 Compute to work.

[1] https://www.khronos.org/registry/webgl/specs/latest/2.0-compute/
[2] https://groups.google.com/a/chromium.org/d/topic/blink-dev/bPD47wqY-r8/discussion

@hujiajie
Copy link
Contributor Author

hujiajie commented Dec 5, 2018

The patchset is temporarily based on #7105.

@hujiajie
Copy link
Contributor Author

hujiajie commented Dec 5, 2018

cc @gyagp @qjia7 @kainino0x @kenrussell

@@ -8,6 +8,58 @@

//"use strict";

var makePPEnum = (function() {
Copy link
Member

Choose a reason for hiding this comment

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

this change could be a separate PR?

I think the topic was discussed before, and the hope was we can avoid adding a full parser like this, and can use eval instead?

emscripten.py Outdated
@@ -582,10 +582,13 @@ def read_proxied_function_signatures(asmConsts):


def compile_settings(compiler_engine, libraries, temp_files):
settings = shared.Settings.to_dict()
settings.update((key, int(value)) for key, value in settings.items() if isinstance(value, bool))

Copy link
Member

Choose a reason for hiding this comment

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

these changes also seem like they should be in separate PRs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it's part of #7105

@@ -33,14 +33,14 @@ var LibraryGL = {
currentContext: null,
offscreenCanvases: {}, // DOM ID -> OffscreenCanvas mappings of <canvas> elements that have their rendering control transferred to offscreen.
timerQueriesEXT: [],
#if USE_WEBGL2
#if USE_WEBGL2 || USE_WEBGL2_COMPUTE
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps WEBGL2_COMPUTE should require that WEBGL2 is on, so we don't need to || here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did hesitate for some time if USE_WEBGL2_COMPUTE should imply USE_WEBGL2. I made the current choice because

  • WebGL 2.0 is provided by the webgl2 context (WebGL2RenderingContext) and WebGL 2.0 Compute is provided by the webgl2-compute context (WebGL2ComputeRenderingContext), so WebGL 2.0 Compute is not simply an extension of WebGL 2.0.
  • For a native application in C/C++, the two contexts are usually created with different minor version number.

It gives me the impression that USE_WEBGL2 and USE_WEBGL2_COMPUTE should exist at the same level but be exclusive to each other. Tens of USE_WEBGL2 || USE_WEBGL2_COMPUTE is bad, but the other style sounds slightly worse to me at the moment.

Copy link
Collaborator

@kainino0x kainino0x Dec 6, 2018

Choose a reason for hiding this comment

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

You could probably introduce another symbol, like HAVE_WEBGL2 or AT_LEAST_WEBGL2 ALLOW_WEBGL2, that would be more clear in its intent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As another proposal, use #if WEBGL_MAJOR_VERSION >= 2. Not sure if it's better or not.

@kripken
Copy link
Member

kripken commented Dec 5, 2018

I don't have the full context for compute (will need to look into it), but some initial comments above.

Overall this is very nice work! The compute-specific changes in this PR look generally ok to me, my only concerns at this point are some of the support code like the parsing and python changes etc., but those can be separated out I think.

@hujiajie
Copy link
Contributor Author

hujiajie commented Dec 6, 2018

Glad to know the overall opinion. Will address the preprocessor concerns next.

@kainino0x
Copy link
Collaborator

Perhaps it would be useful for review to link to the compare between #7105 and here, so that the changes are not squashed together:
hujiajie/emscripten@preprocessor-rework...webgl2-compute

@kenrussell
Copy link
Collaborator

This is tremendously exciting @hujiajie ! Please follow @kripken and @kainino0x 's advice to get this landed. Great work!

@kripken
Copy link
Member

kripken commented Dec 13, 2018

Ok, the preprocessor issues should be sorted out now after #7651 which just landed. Rebasing this to contain just the WebGL 2 Compute stuff should now be possible.

@hujiajie
Copy link
Contributor Author

Really appreciate the effort! I'll post an update in a day or two.

@juj juj added the GL label Dec 13, 2018
@juj
Copy link
Collaborator

juj commented Dec 13, 2018

Given that WEBGL2_COMPUTE implies required WebGL2 support, and now that Alon landed that excellent more flexible preprocessor support, how about introducing a new flag

-s MAX_WEBGL_VERSION=xy

which can take one of values 10, 20 or 21, for WebGL 1.0, WebGL 2.0, and WebGL 2.0 Compute respectively. Because this is a tower of dependencies that imply each other, then we change to

-#if USE_WEBGL2 || USE_WEBGL2_COMPUTE
+#if MAX_WEBGL_VERSION >= 20

-#if USE_WEBGL2 && USE_WEBGL2_COMPUTE
+#if MAX_WEBGL_VERSION >= 21

Then let's change the -s USE_WEBGL2=1 flag in command line frontend to alias as if one had passed in -s MAX_WEBGL_VERSION=20, and so, if developer wants to enable WebGL 2.0 Compute support, they would pass in linker flag -s MAX_WEBGL_VERSION=21. Then we can drop the -s USE_WEBGL2=1 and -s USE_WEBGL2_COMPUTE=1 flags altogether.

Note that the meaning of current -s USE_WEBGL2=1 flag does not mean that the compiled page would strictly use WebGL 2.0, but instead, it means that the compiled page will be enabled to target WebGL 2.0 (but it may also choose to create WebGL 1.0 contexts at runtime).

In the future, the above scheme will allow us to add another flag, -s MIN_WEBGL_VERSION=10/20/21 in the same manner to enable a developer to e.g. say -s MIN_WEBGL_VERSION=20 to mean "This application will never work on WebGL 1.0, so do not even bother compiling in WebGL 1.0 support code, that would just waste generated code space". With MIN_WEBGL_VERSION and MAX_WEBGL_VERSION, developers can specify at compile time the API range they wish to care about, e.g. -s MIN_WEBGL_VERSION=10 -s MAX_WEBGL_VERSION=20 would mean that the page will target 1.0 and 2.0, and -s MIN_WEBGL_VERSION=10 -s MAX_WEBGL_VERSION=10 would mean the page is strictly WebGL 1.0 only, and so on.

// WebGL 2.0 Compute is deemed to be a design mistake in naming. The
// following constants will make the life easier.
WEBGL2_COMPUTE_MAJOR_VERSION: 2,
WEBGL2_COMPUTE_MINOR_VERSION: 1,
Copy link
Collaborator

Choose a reason for hiding this comment

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

These two lines would introduce regression on pages that target WebGL 2.0 only, but not WebGL 2.0 Compute. Btw about the comment here - is there a future direction to rename WebGL 2.0 Compute to WebGL 2.1 officially? Or how does the comment relate to current thinking? (If the API name "WebGL 2.0 Compute" is deemed to be permanent, we do not want to retain judgement in comments, so could leave that out, even if we do feel it was the wrong call)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, I forgot to remove the subjective comment before committing.

I do not think WebGL 2.0 Compute will be renamed to WebGL 2.1 or 3.0 in the near future. Maybe @kenrussell or @gyagp have more insight on this.

And can you elaborate more on the regression? Is it about code size?

Copy link

Choose a reason for hiding this comment

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

As Apple stops to advance OpenGL, WebGL 2.0 Compute can't run on macOS/iOS. Given it's not a general solution for all platforms, the decision from Khronos WebGL Working Group was to make it an extension, rather than a concrete version.

Copy link
Collaborator

Choose a reason for hiding this comment

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

And can you elaborate more on the regression? Is it about code size?

Oh sorry, yeah, meant here a regression on code size specifically.

I do not think WebGL 2.0 Compute will be renamed to WebGL 2.1 or 3.0 in the near future.

If WebGL 2.0 Compute will not be renamed to WebGL 2.1, but we still have items like

    WEBGL2_COMPUTE_MAJOR_VERSION: 2,
    WEBGL2_COMPUTE_MINOR_VERSION: 1,

making Emscripten treat WebGL 2.0 Compute as if it was WebGL 2.1, why should we diverge from them and call it 2.1 if Khronos WebGL WG does not? Is there a danger of the working group introducing a "real" WebGL 2.1 at some point after this, and then we would be left with a collision with version numbering?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The WebGL version number is heavily used in library_gl.js for comparison, so I suppose WebGL 2.0 Compute also needs a numerical representation for this, but it looks inappropriate to just use 2.0. A real WebGL 2.1 could be the risk, and I haven't got a better idea yet.

webGLContextAttributes['minorVersion'] = defaultContext.minorVersion;
break;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

The above code would introduce a code size regression for WebGL 1.0 and WebGL 2.0 code when WebGL 2.0 Compute was not being targeted. I can appreciate the desire to build simple looking code, though we need to come up with a way to do this in a manner that will at least Closure optimize away well when WebGL 2.0 Compute is not being targeted (and when WebGL 2.0 is not being targeted either)

@@ -720,18 +760,19 @@ var LibraryGL = {
var context = {
handle: handle,
attributes: webGLContextAttributes,
version: webGLContextAttributes['majorVersion'],
majorVersion: webGLContextAttributes['majorVersion'],
minorVersion: webGLContextAttributes['minorVersion'],
Copy link
Collaborator

Choose a reason for hiding this comment

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

The three above code blocks also would regress code size for WebGL1 and WebGL 2. Instead of majorVersion and minorVersion, I think it'd be better to do integers 10, 20 and 21, the generated code size will be smaller that way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was over-engineered because 210 can mean either 2.10 or 21.0, and 3.0 should be larger than 2.10. But I'm not opposed of your suggestion considering it's pretty safe to leave the situation unhandled until it really happens. Just a little concerned about the readability for now, so looking forward to others' opinion.

} else if (GL.currentContext.majorVersion == 2 && GL.currentContext.minorVersion == 0) {
glVersion = 'OpenGL ES 3.0';
} else if (GL.currentContext.majorVersion == 1 && GL.currentContext.minorVersion == 0) {
glVersion = 'OpenGL ES 2.0';
Copy link
Collaborator

Choose a reason for hiding this comment

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

This would also regress WebGL 1.0/WebGL 2.0 code size.

} else if (GL.currentContext.majorVersion == 2 && GL.currentContext.minorVersion == 0) {
major = 3;
minor = 0;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

likewise

prelude = '#version 310 es\n';
} else if (GL.currentContext.majorVersion == 2 && GL.currentContext.minorVersion == 0) {
prelude = '#version 300 es\n';
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

likewise

if (!strcmp(name, "glDispatchCompute")) return emscripten_glDispatchCompute;
if (!strcmp(name, "glBindImageTexture")) return emscripten_glBindImageTexture;
if (!strcmp(name, "glMemoryBarrier")) return emscripten_glMemoryBarrier;
if (!strcmp(name, "glMemoryBarrierByRegion")) return emscripten_glMemoryBarrierByRegion;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This also regresses code size, in a particularly tricky way. For this we should migrate to codegenning this differently per API level.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

FYI, many ES 3.1 APIs are still not added here, so the code size is expected to be increased further. Can we leave codegenning for future improvement?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Is the regression from adding all these strings? Or is it somehow specific to glMemoryBarrierByRegion? Can this entire block just be inside of an ifdef?

Copy link
Member

@kripken kripken Feb 28, 2019

Choose a reason for hiding this comment

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

I think the regression is that these lines keep alive each of those functions whose address they return. So the compiler can't dce them out, they are all used.

We do build this file separately for when GL emulation is present. We could build separate versions for the different GL levels, to reduce the problem. That code is in tools/system_libraries.py.

Copy link
Collaborator

Choose a reason for hiding this comment

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

+1, sounds like a reasonable fix to me. FYI @hujiajie: In tools/system_libs.py, I'm looking at create_gl. It does flags += legacy_gl_emulation_flags(libname). I think the suggestion is to add another flags += webgl2_compute_flags() similar to that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

IIUC, the idea has already been implemented in ef230f5. I'm rebasing on top of that.

@juj
Copy link
Collaborator

juj commented Dec 13, 2018

Cool work!

One other direction we should investigate here is that instead of adding WebGL 2.0 Compute support in to library_gl.js, which currently has accumulated WebGL 1.0, WebGL 2.0 and Legacy GL emulation, it would be better to not go further down that hole, and instead introduce WebGL 2.0 Compute support in e.g. library_webgl21.js file for all entry points that are unique to it. The existing monolithic library_gl.js is a bad practice, it's something that has been long desired to be split up into library_webgl10.js, library_webgl20.js and library_glemu.js files, so introducing WebGL 2.0 Compute support to its own file library_webgl21.js would keep the strain from such a split lighter and avoid continuing the bad practice.

Another benefit of separate files is that if we did the above -s MAX_WEBGL_VERSION=21 kind of scheme, we could then easily control that only if -s MAX_WEBGL_VERSION=21 was passed, then we parse and link in library_webgl21.js. Currently we have an issue that we parse and link in all the src/library_x.js files, even if one does not use them, which is dirty - with MIN/MAX directives on the linking, we could use those to better control which files to link in.

@kainino0x
Copy link
Collaborator

I agree it would be nice to split up library_gl.js. One major piece of work that I think will eventually be necessary is to rewrite some stuff as library_gl*.c when host bindings are available. (I forget whether I've talked to you about this.)

Host Bindings (with Anyref) would allow WASM to call directly into Web API methods for better performance, which means all of the ES-to-WebGL translation has to happen in C rather than JS. In order to write library_gl.c at all, browser support for Anyref may be necessary.

Also, it's not necessary to move the entire thing wholesale to use host bindings - it's only really going to be important for the more performance critical GL calls. So maybe it's better to keep most of library_gl as JS.

@kainino0x
Copy link
Collaborator

Another thought on MIN_/MAX_WEBGL_VERSION: I'd prefer not to take the "21" name for WebGL compute just in case there is eventually a 2.1 that isn't exactly the same as webgl2-compute.

How about ALLOW_WEBGL_VERSION=1,2,2compute or 10,20,20compute or webgl1,webgl2,webgl2-compute? Then just assert on invalid combinations of those.

@hujiajie
Copy link
Contributor Author

I like the idea of {MAX,MIN}_WEBGL_VERSION, and I'm seeing if code size regressions can be mitigated with this. I have the same concern with @kainino0x, and some random thoughts:

  • use OpenGL ES 2.0/3.0/3.1 instead of WebGL 1.0/2.0/2.0 Compute
  • I suppose WebGL 2.0 Compute will likely be part of the next WebGL revision, if there will be one. WebGL 2.0 Compute should be deprecated if that happens.

@kripken
Copy link
Member

kripken commented Dec 17, 2018

@kainino0x

One major piece of work that I think will eventually be necessary is to rewrite some stuff as library_gl*.c when host bindings are available.

Definitely, yeah, eventually we will want to migrate most or all of our GL bindings into compiled code, when wasm supports that. We definitely block on anyref for that, and maybe host bindings, but not sure those will be enough.

@hujiajie hujiajie force-pushed the webgl2-compute branch 2 times, most recently from 5e9686d to 2eb13d6 Compare December 20, 2018 11:21
@hujiajie
Copy link
Contributor Author

The PR has been rebased with the following major changes:

  • To workaround the GL versioning difficulties, I'm proposing to use a canonical internal representation ("API level"). People may have to map it to the corresponding OpenGL ES or WebGL version mentally though.
  • Most OpenGL ES 3.1 entry points were added with stub implementation. They remain to be materialized in the future.

Thoughts?

@jjxtra
Copy link

jjxtra commented Jul 16, 2019

Will this also include read/write support for compute shaders reading from and writing to textures?

@hujiajie
Copy link
Contributor Author

@jjxtra Yes. You can bind a level of a texture to an image unit with glBindImageTexture() and read/write the image with imageLoad()/imageStore() in your compute shader.

WebGL 2.0 Compute is an experimental superset of WebGL 2.0, provided by
Chrome/Chromium behind the flag `--enable-webgl2-compute-context`. It
exposes OpenGL ES 3.1 functionality, notably compute shader, to WebGL
via the `webgl2-compute` context.[1][2]

The patch adds a new flag `-s USE_WEBGL2_COMPUTE=` to opt-in support for
WebGL 2.0 Compute, which is similar to but exclusive with `-s
USE_WEBGL2=`. For now, only existing OpenGL ES 3.0 entry points are
exposed in this context, and others (those introduced by OpenGL ES 3.1)
will be added next.

[1] https://www.khronos.org/registry/webgl/specs/latest/2.0-compute/
[2] https://groups.google.com/a/chromium.org/d/topic/blink-dev/bPD47wqY-r8/discussion
The test is expected to work on Windows and Linux with
EMTEST_BROWSER="chrome --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough --enable-webgl2-compute-context".

Sadly, the Mesa software renderer is too slow and lacks certain OpenGL
extensions for WebGL 2.0 Compute to work, so the test is not enabled in
CI.
@sbc100
Copy link
Collaborator

sbc100 commented Jan 30, 2020

The deletion of the incoming branch triggered the automatic closing of this branch. My apologies. This was an unexpected side effect.

Re-opening and re-targeting to master.

@sbc100 sbc100 reopened this Jan 30, 2020
@sbc100 sbc100 changed the base branch from incoming to master January 30, 2020 20:16
@stale
Copy link

stale bot commented Jan 31, 2021

This issue has been automatically marked as stale because there has been no activity in the past year. It will be closed automatically if no further activity occurs in the next 30 days. Feel free to re-open at any time if this issue is still relevant.

@stale stale bot added the wontfix label Jan 31, 2021
@kainino0x kainino0x closed this Feb 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants