Emscripten: WebGPU cannot create shader

Created on 11 Jun 2020  路  14Comments  路  Source: emscripten-core/emscripten

I use this C++ code to create WGPUShaderModule:

  static const std::uint32_t vsCode[] = {
  // raw SPIR-V byte-code output using https://unpkg.com/@webgpu/[email protected]/dist/web-devel/glslang.js 
  // 119734787,65536,524296,40, .....
  };

  auto device = emscripten_webgpu_get_device();

  WGPUShaderModuleDescriptor vertDesc;
  WGPUChainedStruct chain { nullptr };
  chain.sType = WGPUSType_ShaderModuleSPIRVDescriptor;
  vertDesc.nextInChain = &chain;
  vertDesc.codeSize = sizeof(vsCode) / sizeof(uint32_t);
  vertDesc.code = vsCode;
  auto vsModule = wgpuDeviceCreateShaderModule(device, &vertDesc);

When running, Web Console shows:

Uncaught TypeError: device.createShaderModule is not a function
    _wgpuDeviceCreateShaderModule http://localhost/bin/01_window.js:5311

Browser:

  • Firefox Linux Nightly 79.0a1 (2020-06-08) (64-bit)
  • Build: -s USE_WEBGPU=1 -s WASM=1

emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.39.17
clang version 11.0.0 (/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project cc2349e3cf0e1f492433941b359a03fc3f746410)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /home/manh/emsdk/upstream/bin
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.5.0
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
Candidate multilib: .;@m64
Candidate multilib: 32;@m32
Candidate multilib: x32;@mx32
Selected multilib: .;@m64
shared:INFO: (Emscripten: Running sanity checks)

All 14 comments

If you could point out how to implement these functions, I may be able to help on it too. AFAIK, wgpuCreateInstance is also unimplemented (link error after compiling C++).

cc @kainino0x

Emscripten's WebGPU support has not been tested with Firefox - perhaps @kvark can answer why this appears to be missing.

Note also that the WGSL path in Emscripten has not been tested since there were (still are?) no browsers that implement it.

Uncaught TypeError: device.createShaderModule is not a function

There is no way this can be missing. Firefox wouldn't be able to run anything without createShaderModule. To check the basics first, @manhnt9 did you follow instructions to enable WebGPU in Nightly? Does it run, say, "http://wgpu.rs/examples/index.html?example=cube" on your side? It clearly uses createShaderModule.

I can run WebGPU samples.

Maybe library_webgpu.js of emscripten is buggy? Here is my minimal reproduction of this case:

#include <emscripten.h>
#include <emscripten/html5.h>
#include <webgpu/webgpu.h>
#include <cstdint>

EM_JS(void, JS_wgpu_init, (), {
  async function init() {
    Module.preinitializedWebGPUDevice = -1;
    const adapter = await navigator.gpu.requestAdapter();
    const device = await adapter.requestDevice();
    Module.preinitializedWebGPUDevice = device;

    const context = canvas.getContext('gpupresent');
    const swapChainFormat = "bgra8unorm";
    const swapChain = context.configureSwapChain({
      device,
      format: swapChainFormat,
    });
  };
  init();
});

EM_JS(bool, JS_wgpu_check, (), {
  return Module.preinitializedWebGPUDevice == -1;
});

static const std::uint32_t vsCode[] = {
119734787,65536,524296,40,0,131089,1,393227,1,1280527431,1685353262,808793134,0,196622,0,1,458767,0,4,1852399981,0,13,27,196611,2,450,262149,4,1852399981,0,393221,11,1348430951,1700164197,2019914866,0,393222,11,0,1348430951,1953067887,7237481,458758,11,1,1348430951,1953393007,1702521171,0,458758,11,2,1130327143,1148217708,1635021673,6644590,458758,11,3,1130327143,1147956341,1635021673,6644590,196613,13,0,393221,27,1449094247,1702130277,1684949368,30821,327685,30,1701080681,1818386808,101,327752,11,0,11,0,327752,11,1,11,1,327752,11,2,11,3,327752,11,3,11,4,196679,11,2,262215,27,11,42,131091,2,196641,3,2,196630,6,32,262167,7,6,4,262165,8,32,0,262187,8,9,1,262172,10,6,9,393246,11,7,6,10,10,262176,12,3,11,262203,12,13,3,262165,14,32,1,262187,14,15,0,262167,16,6,2,262187,8,17,3,262172,18,16,17,262187,6,19,0,262187,6,20,1056964608,327724,16,21,19,20,262187,6,22,3204448256,327724,16,23,22,22,327724,16,24,20,22,393260,18,25,21,23,24,262176,26,1,14,262203,26,27,1,262176,29,7,18,262176,31,7,16,262187,6,34,1065353216,262176,38,3,7,327734,2,4,0,3,131320,5,262203,29,30,7,262205,14,28,27,196670,30,25,327745,31,32,30,28,262205,16,33,32,327761,6,35,33,0,327761,6,36,33,1,458832,7,37,35,36,19,34,327745,38,39,13,15,196670,39,37,65789,65592
};

int main() {
  JS_wgpu_init();
  while (1) {
    if (JS_wgpu_check()) {
      break;
    }
    emscripten_sleep(5);
  }

  auto device = emscripten_webgpu_get_device();

  WGPUShaderModuleDescriptor vertDesc;
  WGPUChainedStruct chain { nullptr };
  chain.sType = WGPUSType_ShaderModuleSPIRVDescriptor;
  vertDesc.nextInChain = &chain;
  vertDesc.codeSize = sizeof(vsCode) / sizeof(uint32_t);
  vertDesc.code = vsCode;
  auto vsModule = wgpuDeviceCreateShaderModule(device, &vertDesc);
}

em++ wgpu.cpp -o w.html -s USE_WEBGPU=1 -s WASM=1 -s ASYNCIFY -std=c++14
It outputs w.html, w.js, w.asm and these need to be served under a http server.

I tried editing the generated JS manually and found out that device is -1 (maybe not good I think ?):

var device = WebGPU["mgrDevice"].get(deviceId);
console.log(device);

deviceId is 1, which seems normal.

Interesting, somehow the WebGPU must be initialized before loading the JS module.
So I can't init it in C++ code, I have to write JS in .html instead. Then creating shader works!
@kainino0x mentioned it in another issue but I went off-road to try initializing WebGPU from C++ code.
Please let me know if that's actually possible too.

The issue here is with using await keyword in an EM_JS block: that does not work. (Perhaps it could be made to work by expanding -s ASYNCIFY=1 support?).

Doing the async part of the initialization in HTML code before launching the page is one way to work around the issue for now - Emscripten WebGPU API needs to get async init functionality to get around this proper. I have been looking to rewrite the WebGPU support at some point to resolve this and other interop aspects.

Oh that's why @floooh used JS promise syntax with .then() in his C++ code to init WebGPU. I'm gonna try it!

Okay sorry guys everything works fine. I messed up the initialization by checking it the wrong way.
Async/Await should work!

The only problem is my comparison code.

EM_JS(bool, JS_wgpu_check, (), {
  return Module.preinitializedWebGPUDevice == -1;
});

which should have been:

EM_JS(bool, JS_wgpu_check, (), {
  return Module.preinitializedWebGPUDevice != -1;
});

That makes sense. Thanks for reporting back on what went wrong.

Was the issue specifically occurring on Firefox? I would have expected that to behave the same on both Chrome and Firefox.

Yes I'm testing on Firefox only because there isn't a pre-built Chrome Canary for Linux right now.

As long as Emscripten's Asyncify works, I can init WebGPU from C++ calling JS. The bug happens because my waiting condition is wrong so it went straight to shader creation before initialization completed.

Gotcha. FYI WebGPU is also in Chrome Dev Channel (which is packaged for Linux), but it still doesn't include WebGPU as we've only gotten the canvas compositing integration working properly on Mac and Windows.

Ah OK thanks for the info. I've managed to find the download link for Chrome linux dev version. Look forward to try WebGPU with it soon!

Was this page helpful?
0 / 5 - 0 ratings