From 11784009dbbfcbca5f321e2c7ce28b15eabcb151 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Wed, 3 Jul 2024 19:46:03 +0000 Subject: [PATCH] Generate files to use Emscripten bindings from outside Emscripten We are stabilizing webgpu.h, and that process requires many (mostly small) breaking changes to the header. Right now, some projects are relying on Emscripten's and Dawn's implementations of webgpu.h to be in sync, but this creates challenges because we need to make many separate breaking changes. This change forks Emscripten's WebGPU bindings into Dawn (in //third_party) in such a way that they don't have to be checked into the Emscripten repository. This way we can keep Dawn's bindings and Dawn's fork of the Emscripten bindings in sync (approximately) in the Dawn repository. Projects depending on Emscripten and Dawn to be in sync should use this fork instead, until we've finished the webgpu.h changes and contributed the bindings back up to Emscripten in one big breaking release that should have minimal to no breakage afterward. For ease of diffing this commit overwrites the verbatim copies of library_{,html5_}webgpu.js added in the previous commit, with a few updates to bring it in sync with Dawn (though it may not be fully in sync - it has only been minimally tested so far). Example of using the resulting generated files from outside GN: https://github.com/kainino0x/webgpu-cross-platform-demo/compare/dawnwasm The generated files can only be generated using the GN build; CMake rules haven't been implemented, and the existing Emscripten build rules for CMake have been removed. Note, since these bindings were designed to live inside Emscripten, they rely on some Emscripten internals. Symbols in rough order from less stable to more stable: - `$stackSave` - changed recently - `$stackRestore` - changed recently - `$callUserCallback` - `$stringToUTF8OnStack` - `$stringToNewUTF8` - `$stringToUTF8` - `$lengthBytesUTF8` - `$readI53FromI64` - `$findCanvasEventTarget` - `$warnOnce` Generator internals, these seem stable: - `mergeInto` - `C_STRUCTS` - `LibraryManager.library` The build rules also depend on: - the location of `gen_struct_info.py` (which recently changed from `tools/` to `tools/maint/`) Bug: 346806934 Change-Id: I6ca10ea37bc7307d4d9571dcdc696e7d025663d3 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/193722 Reviewed-by: Loko Kung Auto-Submit: Kai Ninomiya Reviewed-by: dan sinclair Commit-Queue: dan sinclair --- BUILD.gn | 12 + build_overrides/dawn.gni | 7 + docs/dawn/codegen.md | 2 +- generator/CMakeLists.txt | 1 + generator/dawn_json_generator.py | 74 +- generator/templates/api_cpp.h | 2 +- .../library_webgpu_enum_tables.js | 91 ++ .../library_webgpu_generated_sig_info.js | 77 ++ .../webgpu_struct_info.json} | 11 +- .../templates/library_api_enum_tables.js | 48 - scripts/dawn_overrides_with_defaults.gni | 4 + src/dawn/BUILD.gn | 13 +- src/dawn/CMakeLists.txt | 12 - src/dawn/dawn.json | 44 +- src/emdawnwebgpu/BUILD.gn | 187 +++ src/emdawnwebgpu/README.md | 40 + src/emdawnwebgpu/concat.py | 15 + .../library_webgpu_struct_info_part1.txt | 3 + .../library_webgpu_struct_info_part2.txt | 3 + .../library_webgpu_struct_info_part3.txt | 27 + .../emdawnwebgpu/library_html5_webgpu.js | 2 +- third_party/emdawnwebgpu/library_webgpu.js | 1001 +++++------------ third_party/emdawnwebgpu/webgpu.cpp | 19 +- 23 files changed, 851 insertions(+), 844 deletions(-) create mode 100644 generator/templates/emdawnwebgpu/library_webgpu_enum_tables.js create mode 100644 generator/templates/emdawnwebgpu/library_webgpu_generated_sig_info.js rename generator/templates/{api_struct_info.json => emdawnwebgpu/webgpu_struct_info.json} (83%) delete mode 100644 generator/templates/library_api_enum_tables.js create mode 100644 src/emdawnwebgpu/BUILD.gn create mode 100644 src/emdawnwebgpu/README.md create mode 100644 src/emdawnwebgpu/concat.py create mode 100644 src/emdawnwebgpu/snippets/library_webgpu_struct_info_part1.txt create mode 100644 src/emdawnwebgpu/snippets/library_webgpu_struct_info_part2.txt create mode 100644 src/emdawnwebgpu/snippets/library_webgpu_struct_info_part3.txt diff --git a/BUILD.gn b/BUILD.gn index ea6ebced7ec..6d4fe0166ea 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -72,12 +72,24 @@ group("android") { deps = [ "tools/android" ] } +group("emdawnwebgpu") { + # Without a copy of Emscripten, we can only generate a few files. + deps = [ + "src/emdawnwebgpu:emdawnwebgpu_headers_gen", + "src/emdawnwebgpu:emdawnwebgpu_js_gen", + ] + if (dawn_emscripten_dir != "") { + deps += [ "src/emdawnwebgpu:emdawnwebgpu" ] + } +} + group("all") { testonly = true deps = [ ":android", ":benchmarks", ":cmds", + ":emdawnwebgpu", ":fuzzers", ":libs", ":tests", diff --git a/build_overrides/dawn.gni b/build_overrides/dawn.gni index 74109adb619..bbe1f6784c5 100644 --- a/build_overrides/dawn.gni +++ b/build_overrides/dawn.gni @@ -55,6 +55,13 @@ dawn_vulkan_validation_layers_dir = "//third_party/vulkan-validation-layers/src" dawn_dxc_dir = "//third_party/dxc" dawn_dxheaders_dir = "//third_party/dxheaders" +declare_args() { + # Enables generating the Emscripten WebGPU bindings. + # Note this must point at an emscripten source checkout; the emsdk copy + # installed at emsdk/upstream/emscripten does not have gen_struct_info.py. + dawn_emscripten_dir = "" +} + # PartitionAlloc is an optional dependency. # It does not fully support the MSVC compiler at the moment. if (is_clang || !is_win) { diff --git a/docs/dawn/codegen.md b/docs/dawn/codegen.md index a81ce9f8412..d41eb9a7935 100644 --- a/docs/dawn/codegen.md +++ b/docs/dawn/codegen.md @@ -13,7 +13,7 @@ At this time it is used to generate: - the Dawn, Emscripten, and upstream webgpu-native `webgpu.h` C header - the Dawn and Emscripten `webgpu_cpp.h` C++ wrapper over the C header - libraries that implements `webgpu.h` by calling in a static or `thread_local` proc table - - other parts of the [Emscripten](https://emscripten.org/) WebGPU implementation + - the [Emscripten](https://emscripten.org/) WebGPU binding implementation - a GMock version of the API with its proc table for testing - validation helper functions for dawn_native - the definition of dawn_native's proc table diff --git a/generator/CMakeLists.txt b/generator/CMakeLists.txt index 6bcda79562a..38d39a87e14 100644 --- a/generator/CMakeLists.txt +++ b/generator/CMakeLists.txt @@ -52,6 +52,7 @@ function(DawnGenerator) "SCRIPT;OUTPUT_HEADERS;OUTPUT_SOURCES;PRINT_NAME" "EXTRA_PARAMETERS" ) + message(STATUS "Dawn: Configuring DawnGenerator for ${arg_PRINT_NAME}.") if (arg_UNPARSED_ARGUMENTS) message(FATAL_ERROR diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py index 5f344d3be89..b1a1b59ac6b 100644 --- a/generator/dawn_json_generator.py +++ b/generator/dawn_json_generator.py @@ -928,6 +928,37 @@ def as_jsEnumValue(value): return "'" + value.name.js_enum_case() + "'" +def has_wasmType(return_type, args): + return all(map(lambda x: len(as_wasmType(x)) == 1, [return_type] + args)) + + +# Returns a single character wasm type (v/p/i/j/f/d) if valid, a "(longer string)" if not +def as_wasmType(x): + if isinstance(x, RecordMember): + if x.annotation == 'value': + x = x.type + elif '*' in x.annotation: + return 'p' + else: + return f'({x})' + + if isinstance(x, Type): + if x.category == 'enum': + return 'i' + elif x.category == 'bitmask': + # TODO(crbug.com/347732150): Change to 'j' when bitmasks are 64-bit + return 'i' + elif x.category in ['object', 'function pointer']: + return 'p' + elif x.category == 'native': + return x.json_data.get('wasm type', f'({x.name.name})') + elif x.category in ['structure', 'callback info']: + return f'({x.name.name})' # Invalid + else: + assert False, 'Type -> ' + x.category + assert False, x + + def convert_cType_to_cppType(typ, annotation, arg, indent=0): if typ.category == 'native': return arg @@ -1122,6 +1153,8 @@ def as_cProc(type_name, method_name): 'as_cType': lambda name: as_cType(c_prefix, name), 'as_cppType': as_cppType, 'as_jsEnumValue': as_jsEnumValue, + 'has_wasmType': has_wasmType, + 'as_wasmType': as_wasmType, 'convert_cType_to_cppType': convert_cType_to_cppType, 'as_varName': as_varName, 'decorate': decorate, @@ -1270,38 +1303,45 @@ def get_file_renders(self, args): FileRender('api.h', 'webgpu-headers/' + api + '.h', [RENDER_PARAMS_BASE, params_upstream])) - if 'emscripten_bits' in targets: + if 'emdawnwebgpu_headers' in targets: assert api == 'webgpu' params_emscripten = parse_json( loaded_json, enabled_tags=['compat', 'emscripten']) # system/include/webgpu renders.append( - FileRender('api.h', - 'emscripten-bits/system/include/webgpu/webgpu.h', + FileRender('api.h', 'src/emdawnwebgpu/include/webgpu/webgpu.h', [RENDER_PARAMS_BASE, params_emscripten])) renders.append( - FileRender( - 'api_cpp.h', - 'emscripten-bits/system/include/webgpu/webgpu_cpp.h', [ - RENDER_PARAMS_BASE, params_emscripten, { - 'c_header': api + '/' + api + '.h', - 'c_namespace': None, - } - ])) + FileRender('api_cpp.h', + 'src/emdawnwebgpu/include/webgpu/webgpu_cpp.h', [ + RENDER_PARAMS_BASE, params_emscripten, { + 'c_header': api + '/' + api + '.h', + 'c_namespace': None, + } + ])) renders.append( FileRender( 'api_cpp_chained_struct.h', - 'emscripten-bits/system/include/webgpu/webgpu_cpp_chained_struct.h', + 'src/emdawnwebgpu/include/webgpu/webgpu_cpp_chained_struct.h', [RENDER_PARAMS_BASE, params_emscripten])) - # Snippets to paste into existing Emscripten files + + if 'emdawnwebgpu_js' in targets: + assert api == 'webgpu' + params_emscripten = parse_json( + loaded_json, enabled_tags=['compat', 'emscripten']) renders.append( - FileRender('api_struct_info.json', - 'emscripten-bits/webgpu_struct_info.json', + FileRender('emdawnwebgpu/webgpu_struct_info.json', + 'src/emdawnwebgpu/webgpu_struct_info.json', [RENDER_PARAMS_BASE, params_emscripten])) renders.append( - FileRender('library_api_enum_tables.js', - 'emscripten-bits/library_webgpu_enum_tables.js', + FileRender('emdawnwebgpu/library_webgpu_enum_tables.js', + 'src/emdawnwebgpu/library_webgpu_enum_tables.js', [RENDER_PARAMS_BASE, params_emscripten])) + renders.append( + FileRender( + 'emdawnwebgpu/library_webgpu_generated_sig_info.js', + 'src/emdawnwebgpu/library_webgpu_generated_sig_info.js', + [RENDER_PARAMS_BASE, params_emscripten])) if 'mock_api' in targets: mock_params = [ diff --git a/generator/templates/api_cpp.h b/generator/templates/api_cpp.h index 7c3f85dc934..dd585acfa9a 100644 --- a/generator/templates/api_cpp.h +++ b/generator/templates/api_cpp.h @@ -29,7 +29,7 @@ {% set CAPI = metadata.c_prefix %} {% if 'dawn' in enabled_tags %} #ifdef __EMSCRIPTEN__ - #error "Do not include this header. Emscripten already provides headers needed for {{metadata.api}}." + #error "This header is for native Dawn. Use Dawn's or Emscripten's Emscripten bindings instead." #endif {% endif %} {% set PREFIX = "" if not c_namespace else c_namespace.SNAKE_CASE() + "_" %} diff --git a/generator/templates/emdawnwebgpu/library_webgpu_enum_tables.js b/generator/templates/emdawnwebgpu/library_webgpu_enum_tables.js new file mode 100644 index 00000000000..26f7988ded7 --- /dev/null +++ b/generator/templates/emdawnwebgpu/library_webgpu_enum_tables.js @@ -0,0 +1,91 @@ +//* Copyright 2024 The Dawn & Tint Authors +//* +//* Redistribution and use in source and binary forms, with or without +//* modification, are permitted provided that the following conditions are met: +//* +//* 1. Redistributions of source code must retain the above copyright notice, this +//* list of conditions and the following disclaimer. +//* +//* 2. Redistributions in binary form must reproduce the above copyright notice, +//* this list of conditions and the following disclaimer in the documentation +//* and/or other materials provided with the distribution. +//* +//* 3. Neither the name of the copyright holder nor the names of its +//* contributors may be used to endorse or promote products derived from +//* this software without specific prior written permission. +//* +//* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +//* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +//* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +//* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +//* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +//* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +//* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +//* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +//* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//* +//* +//* This generator is used to produce the number-to-string mappings for +//* Emscripten's library_webgpu.js. +//* https://github.com/emscripten-core/emscripten/blob/main/src/library_webgpu.js +//* +{{'{{{'}} + globalThis.__HAVE_EMDAWNWEBGPU_ENUM_TABLES = true; + + // Constant values used at code-generation time in an Emscripten build. + // These will not appear in the final build result, so we can just dump + // every enum here without affecting binary size. + globalThis.WEBGPU_ENUM_CONSTANT_TABLES = { + {% for type in by_category["enum"] + by_category["bitmask"] %} + {{type.name.CamelCase()}}: { + {% for value in type.values %} + '{{value.name.CamelCase()}}': {{value.value}}, + {% endfor %} + }, + {% endfor %} + }; + + // Maps from enum string back to enum number, for callbacks. + // These appear in the final build result so should be kept minimal. + globalThis.WEBGPU_STRING_TO_INT_TABLES = ` + {% for type in by_category["enum"] if type.json_data.get("emscripten_string_to_int", False) %} + Int_{{type.name.CamelCase()}}: { + {% for value in type.values if value.json_data.get("emscripten_string_to_int", True) %} + {% if type.name.name == 'device lost reason' and value.name.name == 'unknown' %} + 'undefined': {{value.value}}, // For older browsers + {% endif %} + {{as_jsEnumValue(value)}}: {{value.value}}, + {% endfor %} + }, + {% endfor %} + Int_PreferredFormat: { + {% for value in types['texture format'].values if value.name.name in ['RGBA8 unorm', 'BGRA8 unorm'] %} + {{as_jsEnumValue(value)}}: {{value.value}}, + {% endfor %} + }, +`; + + // Maps from enum number to enum string. + // These appear in the final build result so should be kept minimal. + globalThis.WEBGPU_INT_TO_STRING_TABLES = ` + {% for type in by_category["enum"] if not type.json_data.get("emscripten_no_enum_table") %} + {{type.name.CamelCase()}}: {% if type.contiguousFromZero -%} + [ + {% for value in type.values %} + {{as_jsEnumValue(value)}}, + {% endfor %} + ] + {%- else -%} + { + {% for value in type.values %} + {{value.value}}: {{as_jsEnumValue(value)}}, + {% endfor %} + } + {%- endif -%} + , + {% endfor %} +`; + + null; +{{'}}}'}} diff --git a/generator/templates/emdawnwebgpu/library_webgpu_generated_sig_info.js b/generator/templates/emdawnwebgpu/library_webgpu_generated_sig_info.js new file mode 100644 index 00000000000..98abf085ce2 --- /dev/null +++ b/generator/templates/emdawnwebgpu/library_webgpu_generated_sig_info.js @@ -0,0 +1,77 @@ +//* Copyright 2024 The Dawn & Tint Authors +//* +//* Redistribution and use in source and binary forms, with or without +//* modification, are permitted provided that the following conditions are met: +//* +//* 1. Redistributions of source code must retain the above copyright notice, this +//* list of conditions and the following disclaimer. +//* +//* 2. Redistributions in binary form must reproduce the above copyright notice, +//* this list of conditions and the following disclaimer in the documentation +//* and/or other materials provided with the distribution. +//* +//* 3. Neither the name of the copyright holder nor the names of its +//* contributors may be used to endorse or promote products derived from +//* this software without specific prior written permission. +//* +//* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +//* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +//* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +//* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +//* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +//* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +//* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +//* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +//* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//* +//* +//* This generator is used to produce the Wasm signatures for any C functions implemented in JS. +//* It is equivalent to the emscripten_webgpu_* and wgpu* signatures in: +//* https://github.com/emscripten-core/emscripten/blob/main/src/library_sigs.js +//* +{{'{{{'}} + globalThis.__HAVE_EMDAWNWEBGPU_SIG_INFO = true; + null; +{{'}}}'}} +{%- macro render_function_or_method_sig(object_or_none, function) -%} + {%- if not has_wasmType(function.return_type, function.arguments) -%} + // + {%- endif -%} + {{as_cMethod(object_or_none.name if object_or_none else None, function.name)}}__sig: ' + {{- as_wasmType(function.return_type) -}} + {%- if object_or_none -%} + {{- as_wasmType(object_or_none) -}} + {%- endif -%} + {%- for arg in function.arguments -%} + {{- as_wasmType(arg) -}} + {%- endfor -%} + ', +{%- endmacro -%} +const webgpuSigs = { + // emscripten_webgpu_* + emscripten_webgpu_get_device__sig: 'p', + emscripten_webgpu_release_js_handle__sig: 'vi', + {% for type in by_category["object"] %} + emscripten_webgpu_export_{{type.name.snake_case()}}__sig: 'ip', + emscripten_webgpu_import_{{type.name.snake_case()}}__sig: 'pi', + {% endfor %} + + // wgpu* + {% for function in by_category["function"] %} + {{render_function_or_method_sig(None, function)}} + {% endfor %} + {% for type in by_category["object"] %} + {% for method in c_methods(type) %} + {{render_function_or_method_sig(type, method)}} + {% endfor %} + {% endfor %} +}; + +// Delete all of the WebGPU sig info from Emscripten's builtins first. +for (const k of Object.keys(LibraryManager.library)) { + if (k.endsWith('__sig') && (k.startsWith('emscripten_webgpu_') || k.startsWith('wgpu'))) { + delete LibraryManager.library[k]; + } +} +mergeInto(LibraryManager.library, webgpuSigs, {allowMissing: true}); diff --git a/generator/templates/api_struct_info.json b/generator/templates/emdawnwebgpu/webgpu_struct_info.json similarity index 83% rename from generator/templates/api_struct_info.json rename to generator/templates/emdawnwebgpu/webgpu_struct_info.json index e9cb6163a3a..5ab80952dca 100644 --- a/generator/templates/api_struct_info.json +++ b/generator/templates/emdawnwebgpu/webgpu_struct_info.json @@ -26,10 +26,13 @@ //* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //* //* -//* This generator is used to produce part of Emscripten's struct_info.json, -//* which is a list of struct fields that it uses to generate field offset -//* information for its own code generators. -//* https://github.com/emscripten-core/emscripten/blob/main/src/struct_info.json +//* This generator is used to produce a struct info file, in the format of +//* Emscripten's struct_info.json file, which is simply a list of struct fields +//* that it uses to generate field offset information for JS code generation. +//* +//* It can be fed directly into gen_struct_info.py, or copy-pasted into the +//* struct_info.json as part of an update to Emscripten's built-in bindings. +//* //* [ // =========================================== diff --git a/generator/templates/library_api_enum_tables.js b/generator/templates/library_api_enum_tables.js deleted file mode 100644 index 8d0e8b9fd31..00000000000 --- a/generator/templates/library_api_enum_tables.js +++ /dev/null @@ -1,48 +0,0 @@ -//* Copyright 2020 The Dawn & Tint Authors -//* -//* Redistribution and use in source and binary forms, with or without -//* modification, are permitted provided that the following conditions are met: -//* -//* 1. Redistributions of source code must retain the above copyright notice, this -//* list of conditions and the following disclaimer. -//* -//* 2. Redistributions in binary form must reproduce the above copyright notice, -//* this list of conditions and the following disclaimer in the documentation -//* and/or other materials provided with the distribution. -//* -//* 3. Neither the name of the copyright holder nor the names of its -//* contributors may be used to endorse or promote products derived from -//* this software without specific prior written permission. -//* -//* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -//* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -//* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -//* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -//* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -//* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -//* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -//* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -//* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -//* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//* -//* -//* This generator is used to produce the number-to-string mappings for -//* Emscripten's library_webgpu.js. -//* https://github.com/emscripten-core/emscripten/blob/main/src/library_webgpu.js -//* - {% for type in by_category["enum"] if not type.json_data.get("emscripten_no_enum_table") %} - {{type.name.CamelCase()}}: {% if type.contiguousFromZero -%} - [ - {% for value in type.values %} - {{as_jsEnumValue(value)}}, - {% endfor %} - ] - {%- else -%} - { - {% for value in type.values %} - {{value.value}}: {{as_jsEnumValue(value)}}, - {% endfor %} - } - {%- endif -%} - , - {% endfor %} diff --git a/scripts/dawn_overrides_with_defaults.gni b/scripts/dawn_overrides_with_defaults.gni index e6b98371ce3..2c6e43e3e68 100644 --- a/scripts/dawn_overrides_with_defaults.gni +++ b/scripts/dawn_overrides_with_defaults.gni @@ -114,3 +114,7 @@ if (!defined(dawn_opengl_registry_dir)) { if (!defined(dawn_egl_registry_dir)) { dawn_egl_registry_dir = "${dawn_root}/third_party/khronos/EGL-Registry" } + +if (!defined(dawn_emscripten_dir)) { + dawn_emscripten_dir = "" +} diff --git a/src/dawn/BUILD.gn b/src/dawn/BUILD.gn index ee5355b1529..9ccd66ff365 100644 --- a/src/dawn/BUILD.gn +++ b/src/dawn/BUILD.gn @@ -66,21 +66,10 @@ dawn_component("proc") { } ############################################################################### -# Other generated files (upstream header, emscripten header, emscripten bits) +# Generated header to match upstream webgpu-headers ############################################################################### dawn_json_generator("webgpu_headers_gen") { target = "webgpu_headers" outputs = [ "webgpu-headers/webgpu.h" ] } - -dawn_json_generator("emscripten_bits_gen") { - target = "emscripten_bits" - outputs = [ - "emscripten-bits/system/include/webgpu/webgpu.h", - "emscripten-bits/system/include/webgpu/webgpu_cpp.h", - "emscripten-bits/system/include/webgpu/webgpu_cpp_chained_struct.h", - "emscripten-bits/webgpu_struct_info.json", - "emscripten-bits/library_webgpu_enum_tables.js", - ] -} diff --git a/src/dawn/CMakeLists.txt b/src/dawn/CMakeLists.txt index 6c1d5163694..51560addb2e 100644 --- a/src/dawn/CMakeLists.txt +++ b/src/dawn/CMakeLists.txt @@ -137,15 +137,3 @@ DawnJSONGenerator( add_custom_target(webgpu_headers_gen DEPENDS ${WEBGPU_HEADERS_GEN_HEADERS} ) - -DawnJSONGenerator( - TARGET "emscripten_bits" - PRINT_NAME "Emscripten WebGPU bits" - OUTPUT_HEADERS EMSCRIPTEN_BITS_GEN_HEADERS - OUTPUT_SOURCES EMSCRIPTEN_BITS_GEN_SOURCES -) -add_custom_target(emscripten_bits_gen - DEPENDS - ${EMSCRIPTEN_BITS_GEN_HEADERS} - ${EMSCRIPTEN_BITS_GEN_SOURCES} -) diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json index b6749912238..2728a1c0ecc 100644 --- a/src/dawn/dawn.json +++ b/src/dawn/dawn.json @@ -39,6 +39,12 @@ "copyright_year": "2019" }, + "INTERNAL__HAVE_EMDAWNWEBGPU_HEADER": { + "category": "structure", + "tags": ["emscripten"], + "members": [] + }, + "create instance": { "category": "function", "returns": "instance", @@ -593,7 +599,8 @@ ] }, "bool": { - "category": "native" + "category": "native", + "wasm type": "i" }, "buffer": { "category": "object", @@ -764,6 +771,7 @@ }, "buffer map state": { "category": "enum", + "emscripten_string_to_int": true, "values": [ {"value": 1, "name": "unmapped"}, {"value": 2, "name": "pending"}, @@ -1049,6 +1057,7 @@ }, "compilation message type": { "category": "enum", + "emscripten_string_to_int": true, "emscripten_no_enum_table": true, "values": [ {"value": 1, "name": "error"}, @@ -1719,16 +1728,18 @@ }, "device lost reason": { "category": "enum", + "emscripten_string_to_int": true, "emscripten_no_enum_table": true, "values": [ {"value": 1, "name": "unknown"}, {"value": 2, "name": "destroyed"}, - {"value": 3, "name": "instance dropped"}, - {"value": 4, "name": "failed creation"} + {"value": 3, "name": "instance dropped", "emscripten_string_to_int": false}, + {"value": 4, "name": "failed creation", "emscripten_string_to_int": false} ] }, "double": { - "category": "native" + "category": "native", + "wasm type": "d" }, "error callback": { "category": "function pointer", @@ -2510,7 +2521,8 @@ ] }, "float": { - "category": "native" + "category": "native", + "wasm type": "f" }, "format capabilities": { "category": "structure", @@ -4459,32 +4471,40 @@ "category": "native" }, "void": { - "category": "native" + "category": "native", + "wasm type": "v" }, "void *": { - "category": "native" + "category": "native", + "wasm type": "p" }, "void const *": { - "category": "native" + "category": "native", + "wasm type": "p" }, "int": { - "category": "native" + "category": "native", + "wasm type": "i" }, "int32_t": { - "category": "native" + "category": "native", + "wasm type": "i" }, "size_t": { "category": "native", + "wasm type": "p", "wire transparent": false }, "uint16_t": { "category": "native" }, "uint32_t": { - "category": "native" + "category": "native", + "wasm type": "i" }, "uint64_t": { - "category": "native" + "category": "native", + "wasm type": "j" }, "uint8_t": { "category": "native" diff --git a/src/emdawnwebgpu/BUILD.gn b/src/emdawnwebgpu/BUILD.gn new file mode 100644 index 00000000000..205297cc8d3 --- /dev/null +++ b/src/emdawnwebgpu/BUILD.gn @@ -0,0 +1,187 @@ +# Copyright 2024 The Dawn & Tint Authors +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import("../../scripts/dawn_overrides_with_defaults.gni") + +import("${dawn_root}/generator/dawn_generator.gni") + +dawn_json_generator("emdawnwebgpu_headers_gen") { + target = "emdawnwebgpu_headers" + outputs = [ + "src/emdawnwebgpu/include/webgpu/webgpu.h", + "src/emdawnwebgpu/include/webgpu/webgpu_cpp.h", + "src/emdawnwebgpu/include/webgpu/webgpu_cpp_chained_struct.h", + ] +} + +dawn_json_generator("emdawnwebgpu_js_gen") { + target = "emdawnwebgpu_js" + outputs = [ + "src/emdawnwebgpu/webgpu_struct_info.json", + "src/emdawnwebgpu/library_webgpu_enum_tables.js", + "src/emdawnwebgpu/library_webgpu_generated_sig_info.js", + ] +} + +# When Emscripten is available, we can use one of its helper scripts to generate +# the struct info needed for our bindings fork (third_party/emdawnwebgpu). +# Those helpers, and their tree of generated dependencies, are: +# +# - library_webgpu_generated_struct_info.js +# is constructed by concatenating: +# - Some glue "snippets" from txt files +# - webgpu_generated_struct_info{32,64}.json +# which are generated using an Emscripten tool "gen_struct_info.py", from: +# - webgpu.h (generated from dawn.json) +# - webgpu_struct_info.json (generated from dawn.json) +# +# The bindings also require the following helpers (generated above): +# +# - library_webgpu_enum_tables.js +# - library_webgpu_generated_sig_info.js +# which we generate directly instead of using "gen_sig_info.py" + +if (dawn_emscripten_dir != "") { + template("webgpu_gen_struct_info") { + action(target_name) { + forward_variables_from(invoker, "*") + + deps = [ + ":emdawnwebgpu_headers_gen", # for webgpu.h + ":emdawnwebgpu_js_gen", # for webgpu_struct_info.json + ] + + script = rebase_path(dawn_emscripten_dir, root_build_dir) + + "/tools/maint/gen_struct_info.py" + args = [ + "-q", # quiet + rebase_path(sources[0], root_build_dir), # input file + "-o=" + rebase_path(outputs[0], root_build_dir), # output file + + # Include dir where webgpu/webgpu.h can be found + "-I=" + rebase_path(target_gen_dir, root_build_dir) + "/include", + ] + if (wasm64) { + args += [ "--wasm64" ] + } + } + } + + webgpu_gen_struct_info("webgpu_generated_struct_info32") { + sources = [ "${target_gen_dir}/webgpu_struct_info.json" ] + outputs = [ "${target_gen_dir}/webgpu_generated_struct_info32.json" ] + wasm64 = false + } + + webgpu_gen_struct_info("webgpu_generated_struct_info64") { + sources = [ "${target_gen_dir}/webgpu_struct_info.json" ] + outputs = [ "${target_gen_dir}/webgpu_generated_struct_info64.json" ] + wasm64 = true + } + + action("library_webgpu_generated_struct_info") { + deps = [ + ":webgpu_generated_struct_info32", + ":webgpu_generated_struct_info64", + ] + + source_files = [ + # The order of these files is important. + "snippets/library_webgpu_struct_info_part1.txt", + "${target_gen_dir}/webgpu_generated_struct_info32.json", + "snippets/library_webgpu_struct_info_part2.txt", + "${target_gen_dir}/webgpu_generated_struct_info64.json", + "snippets/library_webgpu_struct_info_part3.txt", + ] + + sources = source_files + outputs = [ "${target_gen_dir}/library_webgpu_generated_struct_info.js" ] + + script = "concat.py" + args = [] + foreach(source, [ outputs[0] ] + sources) { + args += [ rebase_path(source, root_build_dir) ] + } + } + + # NOTE: Do not use this directly, because it will not add dependencies on the + # the included files. Use :emdawnwebgpu instead. + config("emdawnwebgpu_config") { + include_dirs = [ + # Include directory where webgpu/webgpu{,_cpp}.h can be found. + # This needs to take precedent over Emscripten's built-in include + # directory which also has these files (at an older revision). + "${target_gen_dir}/include", + ] + ldflags = [ + # These will be processed in order; library_webgpu.js must come after + # the generated files, because it depends on them. + # (It will assert this, and also that -sUSE_WEBGPU is not enabled.) + "--js-library=" + + rebase_path("${target_gen_dir}/library_webgpu_enum_tables.js"), + "--js-library=" + rebase_path( + "${target_gen_dir}/library_webgpu_generated_struct_info.js"), + "--js-library=" + + rebase_path("${target_gen_dir}/library_webgpu_generated_sig_info.js"), + "--js-library=" + + rebase_path("../../third_party/emdawnwebgpu/library_webgpu.js"), + "--js-library=" + + rebase_path("../../third_party/emdawnwebgpu/library_html5_webgpu.js"), + + # Emscripten only enables webgpu-externs.js with -sUSE_WEBGPU=1, so add them explicitly. + "--closure-args=--externs=${dawn_emscripten_dir}/src/closure-externs/webgpu-externs.js", + ] + } + + # Dawn's WebGPU bindings for Emscripten. These don't actually use Dawn at all, + # but generating them in Dawn lets us keep them in sync more easily. + # When included in Emscripten they attempt to delete Emscripten's built-in + # implementation and replace it with ours. + # + # This can be built as a standalone target to generate all the files needed + # to feed into a separate project that is built using Emscripten. + # Reference emdawnwebgpu_config for how to actually do that. + # + # EXPERIMENTALLY, this should also be usable as a dependency of a gn-native + # Wasm build target, as an alternative to -sUSE_WEBGPU=1 which uses + # Emscripten's built-in WebGPU bindings. This has not been well-tested. + source_set("emdawnwebgpu") { + public_configs = [ ":emdawnwebgpu_config" ] + deps = [ + ":emdawnwebgpu_headers_gen", + ":emdawnwebgpu_js_gen", + ":library_webgpu_generated_struct_info", + ] + sources = get_target_outputs(":emdawnwebgpu_headers_gen") + + [ "../../third_party/emdawnwebgpu/webgpu.cpp" ] + inputs = [ + # FIXME: Changing these files still doesn't cause a relink. Why? + "../../third_party/emdawnwebgpu/library_webgpu.js", + "../../third_party/emdawnwebgpu/library_html5_webgpu.js", + ] + } +} diff --git a/src/emdawnwebgpu/README.md b/src/emdawnwebgpu/README.md new file mode 100644 index 00000000000..00e8f84ea82 --- /dev/null +++ b/src/emdawnwebgpu/README.md @@ -0,0 +1,40 @@ +Dawn temporarily maintains a fork of the Emscripten WebGPU bindings +(`library_webgpu.js` and friends). The forked files live in +[`//third_party/emdawnwebgpu`](../third_party/emdawnwebgpu/) +and the build targets in this directory produce the other files needed to build +an Emscripten-based project using these bindings. + +This allows the the webgpu.h interface to be kept roughly in sync\* between the +two implementations in a single place (the Dawn repository) instead of two, +while also avoiding constantly breaking the version of webgpu.h that is +currently in Emscripten. (\* Note we don't guarantee it will always be in sync, +though - we don't have any automated testing for this, so we'll periodically fix +it up as needed for import into other projects that use these bindings.) + +Changes to this code in the Dawn repository will be synced back out to the +upstream Emscripten repository after webgpu.h becomes stable, in what should +theoretically be one big final breaking update. Between then and now, projects +can use Dawn's fork of the bindings: + +- Get an emsdk toolchain: + [instructions](https://emscripten.org/docs/getting_started/downloads.html#installation-instructions-using-the-emsdk-recommended). + +- Get a separate source checkout of [Emscripten](https://github.com/emscripten-core/emscripten) + and set it up to point at the toolchain from emsdk by creating a file at `emscripten/.emscripten`: + + ``` + LLVM_ROOT = '/path/to/emsdk/upstream/bin' + BINARYEN_ROOT = '/path/to/emsdk/upstream' + NODE_JS = '/path/to/emsdk/node/18.20.3_64bit/bin/node' + ``` + + Note this must be a source checkout of Emscripten, + not emsdk's `upstream/emscripten` release, which excludes necessary tools. + +- Set up a Dawn GN build, with `dawn_emscripten_dir` in the GN args set to point to + your Emscripten source checkout. + +- Build the `emscripten_webgpu` GN build target. + +- Configure the Emscripten build with all of the linker flags listed in `emscripten_webgpu_config` + (and without `-sUSE_WEBGPU`, because we don't want the built-in bindings). diff --git a/src/emdawnwebgpu/concat.py b/src/emdawnwebgpu/concat.py new file mode 100644 index 00000000000..b3f5a971504 --- /dev/null +++ b/src/emdawnwebgpu/concat.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 + +# Concatenates files. +# Usage: python3 concat.py OUT_FILE IN_FILE [IN_FILE ...] + +import sys + +out_filename = sys.argv[1] +input_filenames = sys.argv[2:] + +with open(out_filename, 'w') as out_file: + for input_filename in input_filenames: + with open(input_filename) as input_file: + for line in input_file: + out_file.write(line) diff --git a/src/emdawnwebgpu/snippets/library_webgpu_struct_info_part1.txt b/src/emdawnwebgpu/snippets/library_webgpu_struct_info_part1.txt new file mode 100644 index 00000000000..64420015b37 --- /dev/null +++ b/src/emdawnwebgpu/snippets/library_webgpu_struct_info_part1.txt @@ -0,0 +1,3 @@ +{{{ + +const structInfo32 = ( diff --git a/src/emdawnwebgpu/snippets/library_webgpu_struct_info_part2.txt b/src/emdawnwebgpu/snippets/library_webgpu_struct_info_part2.txt new file mode 100644 index 00000000000..a6a4451a153 --- /dev/null +++ b/src/emdawnwebgpu/snippets/library_webgpu_struct_info_part2.txt @@ -0,0 +1,3 @@ +); + +const structInfo64 = ( diff --git a/src/emdawnwebgpu/snippets/library_webgpu_struct_info_part3.txt b/src/emdawnwebgpu/snippets/library_webgpu_struct_info_part3.txt new file mode 100644 index 00000000000..f7dcc6e7e59 --- /dev/null +++ b/src/emdawnwebgpu/snippets/library_webgpu_struct_info_part3.txt @@ -0,0 +1,27 @@ +); + +// Double check that the struct info was generated from the right header. +// (The include directory option of gen_struct_info.py affects this.) +if (!('WGPUINTERNAL__HAVE_EMDAWNWEBGPU_HEADER' in structInfo32.structs)) { + throw new Error('struct_info32 generation error - need webgpu.h from Dawn, got it from Emscripten'); +} +if (!('WGPUINTERNAL__HAVE_EMDAWNWEBGPU_HEADER' in structInfo64.structs)) { + throw new Error('struct_info64 generation error - need webgpu.h from Dawn, got it from Emscripten'); +} + +// Make sure we aren't inheriting any of the webgpu.h struct info from +// Emscripten's copy. +for (const k of Object.keys(C_STRUCTS)) { + if (k.startsWith('WGPU')) { + delete C_STRUCTS[k]; + } +} + +const WGPU_STRUCTS = (MEMORY64 ? structInfo64 : structInfo32).structs; +for (const [k, v] of Object.entries(WGPU_STRUCTS)) { + C_STRUCTS[k] = v; +} +globalThis.__HAVE_EMDAWNWEBGPU_STRUCT_INFO = true; + +null; +}}} diff --git a/third_party/emdawnwebgpu/library_html5_webgpu.js b/third_party/emdawnwebgpu/library_html5_webgpu.js index b7359799dce..bfd55a40bff 100644 --- a/third_party/emdawnwebgpu/library_html5_webgpu.js +++ b/third_party/emdawnwebgpu/library_html5_webgpu.js @@ -90,4 +90,4 @@ var LibraryHTML5WebGPU = { {{{ html5_gpu.makeImportExport('render_bundle_encoder', 'RenderBundleEncoder') }}} {{{ html5_gpu.makeImportExport('render_bundle', 'RenderBundle') }}} -addToLibrary(LibraryHTML5WebGPU); +mergeInto(LibraryManager.library, LibraryHTML5WebGPU); diff --git a/third_party/emdawnwebgpu/library_webgpu.js b/third_party/emdawnwebgpu/library_webgpu.js index f67f81f389a..8ae23366a97 100644 --- a/third_party/emdawnwebgpu/library_webgpu.js +++ b/third_party/emdawnwebgpu/library_webgpu.js @@ -5,22 +5,17 @@ */ /* - * WebGPU support. + * Dawn's fork of Emscripten's WebGPU bindings. This will be contributed back to + * Emscripten after reaching approximately webgpu.h "1.0". * - * This file implements the common C header on top of the - * browser's native JS WebGPU implementation. This allows applications targeting - * wgpu-native (https://github.com/gfx-rs/wgpu) or - * Dawn (https://dawn.googlesource.com/dawn/) to also target the Web with the - * same graphics API and fairly minimal changes - similar to OpenGL ES 2.0/3.0 - * on WebGL 1.0/2.0. - * - * To test this, run the following tests: - * - test/runner.py 'other.test_webgpu*' - * - EMTEST_BROWSER="/path/to/chrome --user-data-dir=chromeuserdata --enable-unsafe-webgpu" \ - * test/runner.py 'browser.test_webgpu*' + * IMPORTANT: See //src/emdawnwebgpu/README.md for info on how to use this. */ {{{ + if (USE_WEBGPU || !__HAVE_EMDAWNWEBGPU_STRUCT_INFO || !__HAVE_EMDAWNWEBGPU_ENUM_TABLES || !__HAVE_EMDAWNWEBGPU_SIG_INFO) { + throw new Error("To use Dawn's library_webgpu.js, disable -sUSE_WEBGPU and first include Dawn's library_webgpu_struct_info.js and library_webgpu_enum_tables.js (before library_webgpu.js)"); + } + // Helper functions for code generation globalThis.gpu = { makeInitManager: function(type) { @@ -28,9 +23,9 @@ return `${mgr} = ${mgr} || new Manager();`; }, - makeReferenceRelease: function(type) { + makeAddRefRelease: function(type) { return ` -wgpu${type}Reference: (id) => WebGPU.mgr${type}.reference(id), +wgpu${type}AddRef: (id) => WebGPU.mgr${type}.addRef(id), wgpu${type}Release: (id) => WebGPU.mgr${type}.release(id),`; }, @@ -76,108 +71,7 @@ wgpu${type}Release: (id) => WebGPU.mgr${type}.release(id),`; LIMIT_U32_UNDEFINED: 0xFFFFFFFF, MIP_LEVEL_COUNT_UNDEFINED: 0xFFFFFFFF, ARRAY_LAYER_COUNT_UNDEFINED: 0xFFFFFFFF, - AdapterType: { - CPU: 3, - Unknown: 4, - }, - BackendType: { - WebGPU: 2, - }, - BufferMapAsyncStatus: { - Success: 0, - ValidationError: 1, - Unknown: 2, - DeviceLost: 3, - DestroyedBeforeCallback: 4, - UnmappedBeforeCallback: 5, - MappingAlreadyPending: 6, - OffsetOutOfRange: 7, - SizeOutOfRange: 8, - }, - CompilationInfoRequestStatus: { - Success: 0, - Error: 1, - DeviceLost: 2, - Unknown: 3, - }, - CompositeAlphaMode: { - Auto: 0, - Opaque: 1, - }, - CreatePipelineAsyncStatus: { - Success: 0, - ValidationError: 1, - InternalError: 2, - DeviceLost: 3, - DeviceDestroyed: 4, - Unknown: 5, - }, - ErrorType: { - NoError: 0, - Validation: 1, - OutOfMemory: 2, - Internal: 3, - Unknown: 4, - DeviceLost: 5, - }, - PresentMode: { - Fifo: 1, - Immediate: 3, - Mailbox: 4, - }, - LoadOp: { - Undefined: 0, - Clear: 1, - Load: 2, - }, - StoreOp: { - Undefined: 0, - Store: 1, - Discard: 2, - }, - MapMode: { - None: 0, - Read: 1, - Write: 2 - }, - RequestAdapterStatus: { - Success: 0, - Unavailable: 1, - Error: 2, - Unknown: 3, - }, - RequestDeviceStatus: { - Success: 0, - Error: 1, - Unknown: 1, - }, - SType: { - SurfaceDescriptorFromCanvasHTMLSelector: 0x4, - ShaderModuleSPIRVDescriptor: 0x5, - ShaderModuleWGSLDescriptor: 0x6, - PrimitiveDepthClipControl: 0x7, - RenderPassDescriptorMaxDrawCount: 0xF, - TextureBindingViewDimensionDescriptor: 0x11, - }, - SurfaceGetCurrentTextureStatus: { - Success: 0, - DeviceLost: 5, - }, - QueueWorkDoneStatus: { - Success: 0, - Error: 1, - Unknown: 2, - DeviceLost: 3, - }, - TextureFormat: { - Undefined: 0, - }, - VertexStepMode: { - Undefined: 0, - VertexBufferNotUsed: 1, - Vertex: 2, - Instance: 3, - }, + ...WEBGPU_ENUM_CONSTANT_TABLES, }; null; }}} @@ -214,7 +108,7 @@ var LibraryWebGPU = { {{{ gpu.makeCheckDefined('o') }}} return o.object; }; - this.reference = function(id) { + this.addRef = function(id) { var o = this.objects[id]; {{{ gpu.makeCheckDefined('o') }}} o.refcount++; @@ -286,7 +180,7 @@ var LibraryWebGPU = { }, makeImageCopyTexture: (ptr) => { - {{{ gpu.makeCheckDescriptor('ptr') }}} + {{{ gpu.makeCheck('ptr') }}} return { "texture": WebGPU.mgrTexture.get( {{{ makeGetValue('ptr', C_STRUCTS.WGPUImageCopyTexture.texture, '*') }}}), @@ -308,7 +202,7 @@ var LibraryWebGPU = { }, makeImageCopyBuffer: (ptr) => { - {{{ gpu.makeCheckDescriptor('ptr') }}} + {{{ gpu.makeCheck('ptr') }}} var layoutPtr = ptr + {{{ C_STRUCTS.WGPUImageCopyBuffer.layout }}}; var bufferCopyView = WebGPU.makeTextureDataLayout(layoutPtr); bufferCopyView["buffer"] = WebGPU.mgrBuffer.get( @@ -347,6 +241,225 @@ var LibraryWebGPU = { return desc; }, + makeComputePipelineDesc: (descriptor) => { + {{{ gpu.makeCheckDescriptor('descriptor') }}} + + var desc = { + "label": undefined, + "layout": WebGPU.makePipelineLayout( + {{{ makeGetValue('descriptor', C_STRUCTS.WGPUComputePipelineDescriptor.layout, '*') }}}), + "compute": WebGPU.makeProgrammableStageDescriptor( + descriptor + {{{ C_STRUCTS.WGPUComputePipelineDescriptor.compute }}}), + }; + var labelPtr = {{{ makeGetValue('descriptor', C_STRUCTS.WGPUComputePipelineDescriptor.label, '*') }}}; + if (labelPtr) desc["label"] = UTF8ToString(labelPtr); + return desc; + }, + + makeRenderPipelineDesc: (descriptor) => { + {{{ gpu.makeCheckDescriptor('descriptor') }}} + function makePrimitiveState(rsPtr) { + if (!rsPtr) return undefined; + {{{ gpu.makeCheck('rsPtr') }}} + + // TODO: This small hack assumes that there's only one type that can be in the chain of + // WGPUPrimitiveState. The correct thing would be to traverse the chain, but unclippedDepth + // is going to move into the core object soon, so we'll just do this for now. See: + // https://github.com/webgpu-native/webgpu-headers/issues/212#issuecomment-1682801259 + var nextInChainPtr = {{{ makeGetValue('rsPtr', C_STRUCTS.WGPUPrimitiveState.nextInChain, '*') }}}; + var sType = nextInChainPtr ? {{{ gpu.makeGetU32('nextInChainPtr', C_STRUCTS.WGPUChainedStruct.sType) }}} : 0; + + return { + "topology": WebGPU.PrimitiveTopology[ + {{{ gpu.makeGetU32('rsPtr', C_STRUCTS.WGPUPrimitiveState.topology) }}}], + "stripIndexFormat": WebGPU.IndexFormat[ + {{{ gpu.makeGetU32('rsPtr', C_STRUCTS.WGPUPrimitiveState.stripIndexFormat) }}}], + "frontFace": WebGPU.FrontFace[ + {{{ gpu.makeGetU32('rsPtr', C_STRUCTS.WGPUPrimitiveState.frontFace) }}}], + "cullMode": WebGPU.CullMode[ + {{{ gpu.makeGetU32('rsPtr', C_STRUCTS.WGPUPrimitiveState.cullMode) }}}], + "unclippedDepth": sType === {{{ gpu.SType.PrimitiveDepthClipControl }}} && {{{ gpu.makeGetBool('nextInChainPtr', C_STRUCTS.WGPUPrimitiveDepthClipControl.unclippedDepth) }}}, + }; + } + + function makeBlendComponent(bdPtr) { + if (!bdPtr) return undefined; + return { + "operation": WebGPU.BlendOperation[ + {{{ gpu.makeGetU32('bdPtr', C_STRUCTS.WGPUBlendComponent.operation) }}}], + "srcFactor": WebGPU.BlendFactor[ + {{{ gpu.makeGetU32('bdPtr', C_STRUCTS.WGPUBlendComponent.srcFactor) }}}], + "dstFactor": WebGPU.BlendFactor[ + {{{ gpu.makeGetU32('bdPtr', C_STRUCTS.WGPUBlendComponent.dstFactor) }}}], + }; + } + + function makeBlendState(bsPtr) { + if (!bsPtr) return undefined; + return { + "alpha": makeBlendComponent(bsPtr + {{{ C_STRUCTS.WGPUBlendState.alpha }}}), + "color": makeBlendComponent(bsPtr + {{{ C_STRUCTS.WGPUBlendState.color }}}), + }; + } + + function makeColorState(csPtr) { + {{{ gpu.makeCheckDescriptor('csPtr') }}} + var formatInt = {{{ gpu.makeGetU32('csPtr', C_STRUCTS.WGPUColorTargetState.format) }}}; + return formatInt === {{{ gpu.TextureFormat.Undefined }}} ? undefined : { + "format": WebGPU.TextureFormat[formatInt], + "blend": makeBlendState({{{ makeGetValue('csPtr', C_STRUCTS.WGPUColorTargetState.blend, '*') }}}), + "writeMask": {{{ gpu.makeGetU32('csPtr', C_STRUCTS.WGPUColorTargetState.writeMask) }}}, + }; + } + + function makeColorStates(count, csArrayPtr) { + var states = []; + for (var i = 0; i < count; ++i) { + states.push(makeColorState(csArrayPtr + {{{ C_STRUCTS.WGPUColorTargetState.__size__ }}} * i)); + } + return states; + } + + function makeStencilStateFace(ssfPtr) { + {{{ gpu.makeCheck('ssfPtr') }}} + return { + "compare": WebGPU.CompareFunction[ + {{{ gpu.makeGetU32('ssfPtr', C_STRUCTS.WGPUStencilFaceState.compare) }}}], + "failOp": WebGPU.StencilOperation[ + {{{ gpu.makeGetU32('ssfPtr', C_STRUCTS.WGPUStencilFaceState.failOp) }}}], + "depthFailOp": WebGPU.StencilOperation[ + {{{ gpu.makeGetU32('ssfPtr', C_STRUCTS.WGPUStencilFaceState.depthFailOp) }}}], + "passOp": WebGPU.StencilOperation[ + {{{ gpu.makeGetU32('ssfPtr', C_STRUCTS.WGPUStencilFaceState.passOp) }}}], + }; + } + + function makeDepthStencilState(dssPtr) { + if (!dssPtr) return undefined; + + {{{ gpu.makeCheck('dssPtr') }}} + return { + "format": WebGPU.TextureFormat[ + {{{ gpu.makeGetU32('dssPtr', C_STRUCTS.WGPUDepthStencilState.format) }}}], + "depthWriteEnabled": {{{ gpu.makeGetBool('dssPtr', C_STRUCTS.WGPUDepthStencilState.depthWriteEnabled) }}}, + "depthCompare": WebGPU.CompareFunction[ + {{{ gpu.makeGetU32('dssPtr', C_STRUCTS.WGPUDepthStencilState.depthCompare) }}}], + "stencilFront": makeStencilStateFace(dssPtr + {{{ C_STRUCTS.WGPUDepthStencilState.stencilFront }}}), + "stencilBack": makeStencilStateFace(dssPtr + {{{ C_STRUCTS.WGPUDepthStencilState.stencilBack }}}), + "stencilReadMask": {{{ gpu.makeGetU32('dssPtr', C_STRUCTS.WGPUDepthStencilState.stencilReadMask) }}}, + "stencilWriteMask": {{{ gpu.makeGetU32('dssPtr', C_STRUCTS.WGPUDepthStencilState.stencilWriteMask) }}}, + "depthBias": {{{ makeGetValue('dssPtr', C_STRUCTS.WGPUDepthStencilState.depthBias, 'i32') }}}, + "depthBiasSlopeScale": {{{ makeGetValue('dssPtr', C_STRUCTS.WGPUDepthStencilState.depthBiasSlopeScale, 'float') }}}, + "depthBiasClamp": {{{ makeGetValue('dssPtr', C_STRUCTS.WGPUDepthStencilState.depthBiasClamp, 'float') }}}, + }; + } + + function makeVertexAttribute(vaPtr) { + {{{ gpu.makeCheck('vaPtr') }}} + return { + "format": WebGPU.VertexFormat[ + {{{ gpu.makeGetU32('vaPtr', C_STRUCTS.WGPUVertexAttribute.format) }}}], + "offset": {{{ gpu.makeGetU64('vaPtr', C_STRUCTS.WGPUVertexAttribute.offset) }}}, + "shaderLocation": {{{ gpu.makeGetU32('vaPtr', C_STRUCTS.WGPUVertexAttribute.shaderLocation) }}}, + }; + } + + function makeVertexAttributes(count, vaArrayPtr) { + var vas = []; + for (var i = 0; i < count; ++i) { + vas.push(makeVertexAttribute(vaArrayPtr + i * {{{ C_STRUCTS.WGPUVertexAttribute.__size__ }}})); + } + return vas; + } + + function makeVertexBuffer(vbPtr) { + if (!vbPtr) return undefined; + var stepModeInt = {{{ gpu.makeGetU32('vbPtr', C_STRUCTS.WGPUVertexBufferLayout.stepMode) }}}; + return stepModeInt === {{{ gpu.VertexStepMode.VertexBufferNotUsed }}} ? null : { + "arrayStride": {{{ gpu.makeGetU64('vbPtr', C_STRUCTS.WGPUVertexBufferLayout.arrayStride) }}}, + "stepMode": WebGPU.VertexStepMode[stepModeInt], + "attributes": makeVertexAttributes( + {{{ gpu.makeGetU32('vbPtr', C_STRUCTS.WGPUVertexBufferLayout.attributeCount) }}}, + {{{ makeGetValue('vbPtr', C_STRUCTS.WGPUVertexBufferLayout.attributes, '*') }}}), + }; + } + + function makeVertexBuffers(count, vbArrayPtr) { + if (!count) return undefined; + + var vbs = []; + for (var i = 0; i < count; ++i) { + vbs.push(makeVertexBuffer(vbArrayPtr + i * {{{ C_STRUCTS.WGPUVertexBufferLayout.__size__ }}})); + } + return vbs; + } + + function makeVertexState(viPtr) { + if (!viPtr) return undefined; + {{{ gpu.makeCheckDescriptor('viPtr') }}} + var desc = { + "module": WebGPU.mgrShaderModule.get( + {{{ makeGetValue('viPtr', C_STRUCTS.WGPUVertexState.module, '*') }}}), + "constants": WebGPU.makePipelineConstants( + {{{ gpu.makeGetU32('viPtr', C_STRUCTS.WGPUVertexState.constantCount) }}}, + {{{ makeGetValue('viPtr', C_STRUCTS.WGPUVertexState.constants, '*') }}}), + "buffers": makeVertexBuffers( + {{{ gpu.makeGetU32('viPtr', C_STRUCTS.WGPUVertexState.bufferCount) }}}, + {{{ makeGetValue('viPtr', C_STRUCTS.WGPUVertexState.buffers, '*') }}}), + }; + var entryPointPtr = {{{ makeGetValue('viPtr', C_STRUCTS.WGPUVertexState.entryPoint, '*') }}}; + if (entryPointPtr) desc["entryPoint"] = UTF8ToString(entryPointPtr); + return desc; + } + + function makeMultisampleState(msPtr) { + if (!msPtr) return undefined; + {{{ gpu.makeCheckDescriptor('msPtr') }}} + return { + "count": {{{ gpu.makeGetU32('msPtr', C_STRUCTS.WGPUMultisampleState.count) }}}, + "mask": {{{ gpu.makeGetU32('msPtr', C_STRUCTS.WGPUMultisampleState.mask) }}}, + "alphaToCoverageEnabled": {{{ gpu.makeGetBool('msPtr', C_STRUCTS.WGPUMultisampleState.alphaToCoverageEnabled) }}}, + }; + } + + function makeFragmentState(fsPtr) { + if (!fsPtr) return undefined; + {{{ gpu.makeCheckDescriptor('fsPtr') }}} + var desc = { + "module": WebGPU.mgrShaderModule.get( + {{{ makeGetValue('fsPtr', C_STRUCTS.WGPUFragmentState.module, '*') }}}), + "constants": WebGPU.makePipelineConstants( + {{{ gpu.makeGetU32('fsPtr', C_STRUCTS.WGPUFragmentState.constantCount) }}}, + {{{ makeGetValue('fsPtr', C_STRUCTS.WGPUFragmentState.constants, '*') }}}), + "targets": makeColorStates( + {{{ gpu.makeGetU32('fsPtr', C_STRUCTS.WGPUFragmentState.targetCount) }}}, + {{{ makeGetValue('fsPtr', C_STRUCTS.WGPUFragmentState.targets, '*') }}}), + }; + var entryPointPtr = {{{ makeGetValue('fsPtr', C_STRUCTS.WGPUFragmentState.entryPoint, '*') }}}; + if (entryPointPtr) desc["entryPoint"] = UTF8ToString(entryPointPtr); + return desc; + } + + var desc = { + "label": undefined, + "layout": WebGPU.makePipelineLayout( + {{{ makeGetValue('descriptor', C_STRUCTS.WGPURenderPipelineDescriptor.layout, '*') }}}), + "vertex": makeVertexState( + descriptor + {{{ C_STRUCTS.WGPURenderPipelineDescriptor.vertex }}}), + "primitive": makePrimitiveState( + descriptor + {{{ C_STRUCTS.WGPURenderPipelineDescriptor.primitive }}}), + "depthStencil": makeDepthStencilState( + {{{ makeGetValue('descriptor', C_STRUCTS.WGPURenderPipelineDescriptor.depthStencil, '*') }}}), + "multisample": makeMultisampleState( + descriptor + {{{ C_STRUCTS.WGPURenderPipelineDescriptor.multisample }}}), + "fragment": makeFragmentState( + {{{ makeGetValue('descriptor', C_STRUCTS.WGPURenderPipelineDescriptor.fragment, '*') }}}), + }; + var labelPtr = {{{ makeGetValue('descriptor', C_STRUCTS.WGPURenderPipelineDescriptor.label, '*') }}}; + if (labelPtr) desc["label"] = UTF8ToString(labelPtr); + return desc; + }, + fillLimitStruct: (limits, supportedLimitsOutPtr) => { var limitsOutPtr = supportedLimitsOutPtr + {{{ C_STRUCTS.WGPUSupportedLimits.limits }}}; @@ -358,7 +471,7 @@ var LibraryWebGPU = { var limitValue = limits[name]; {{{ makeSetValue('limitsOutPtr', 'limitOffset', 'limitValue', 'i64') }}}; } - + setLimitValueU32('maxTextureDimension1D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension1D }}}); setLimitValueU32('maxTextureDimension2D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension2D }}}); setLimitValueU32('maxTextureDimension3D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension3D }}}); @@ -375,10 +488,10 @@ var LibraryWebGPU = { setLimitValueU32('maxUniformBuffersPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxUniformBuffersPerShaderStage }}}); setLimitValueU32('minUniformBufferOffsetAlignment', {{{ C_STRUCTS.WGPULimits.minUniformBufferOffsetAlignment }}}); setLimitValueU32('minStorageBufferOffsetAlignment', {{{ C_STRUCTS.WGPULimits.minStorageBufferOffsetAlignment }}}); - + setLimitValueU64('maxUniformBufferBindingSize', {{{ C_STRUCTS.WGPULimits.maxUniformBufferBindingSize }}}); setLimitValueU64('maxStorageBufferBindingSize', {{{ C_STRUCTS.WGPULimits.maxStorageBufferBindingSize }}}); - + setLimitValueU32('maxVertexBuffers', {{{ C_STRUCTS.WGPULimits.maxVertexBuffers }}}); setLimitValueU32('maxBufferSize', {{{ C_STRUCTS.WGPULimits.maxBufferSize }}}); setLimitValueU32('maxVertexAttributes', {{{ C_STRUCTS.WGPULimits.maxVertexAttributes }}}); @@ -395,356 +508,11 @@ var LibraryWebGPU = { setLimitValueU32('maxComputeWorkgroupsPerDimension', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupsPerDimension }}}); }, - // Map from enum string back to enum number, for callbacks. - Int_BufferMapState: { - 'unmapped': 0, - 'pending': 1, - 'mapped': 2, - }, - Int_CompilationMessageType : { - 'error': 0, - 'warning': 1, - 'info': 2, - }, - Int_DeviceLostReason: { - 'undefined': 1, - 'unknown': 1, - 'destroyed': 2, - }, - Int_PreferredFormat: { - 'rgba8unorm': 0x12, - 'bgra8unorm': 0x17, - }, + // Maps from enum string back to enum number, for callbacks. + {{{ WEBGPU_STRING_TO_INT_TABLES }}} - // Map from enum number to enum string. - // This section is auto-generated. See system/include/webgpu/README.md for details. - WGSLFeatureName: [ - undefined, - 'readonly_and_readwrite_storage_textures', - 'packed_4x8_integer_dot_product', - 'unrestricted_pointer_parameters', - 'pointer_composite_access', - ], - AddressMode: [ - undefined, - 'clamp-to-edge', - 'repeat', - 'mirror-repeat', - ], - BlendFactor: [ - undefined, - 'zero', - 'one', - 'src', - 'one-minus-src', - 'src-alpha', - 'one-minus-src-alpha', - 'dst', - 'one-minus-dst', - 'dst-alpha', - 'one-minus-dst-alpha', - 'src-alpha-saturated', - 'constant', - 'one-minus-constant', - ], - BlendOperation: [ - undefined, - 'add', - 'subtract', - 'reverse-subtract', - 'min', - 'max', - ], - BufferBindingType: [ - undefined, - 'uniform', - 'storage', - 'read-only-storage', - ], - BufferMapState: { - 1: 'unmapped', - 2: 'pending', - 3: 'mapped', - }, - CompareFunction: [ - undefined, - 'never', - 'less', - 'equal', - 'less-equal', - 'greater', - 'not-equal', - 'greater-equal', - 'always', - ], - CompilationInfoRequestStatus: [ - 'success', - 'error', - 'device-lost', - 'unknown', - ], - CullMode: [ - undefined, - 'none', - 'front', - 'back', - ], - ErrorFilter: { - 1: 'validation', - 2: 'out-of-memory', - 3: 'internal', - }, - FeatureName: [ - undefined, - 'depth-clip-control', - 'depth32float-stencil8', - 'timestamp-query', - 'texture-compression-bc', - 'texture-compression-etc2', - 'texture-compression-astc', - 'indirect-first-instance', - 'shader-f16', - 'rg11b10ufloat-renderable', - 'bgra8unorm-storage', - 'float32-filterable', - ], - FilterMode: [ - undefined, - 'nearest', - 'linear', - ], - FrontFace: [ - undefined, - 'ccw', - 'cw', - ], - IndexFormat: [ - undefined, - 'uint16', - 'uint32', - ], - LoadOp: [ - undefined, - 'clear', - 'load', - ], - MipmapFilterMode: [ - undefined, - 'nearest', - 'linear', - ], - PowerPreference: [ - undefined, - 'low-power', - 'high-performance', - ], - PrimitiveTopology: [ - undefined, - 'point-list', - 'line-list', - 'line-strip', - 'triangle-list', - 'triangle-strip', - ], - QueryType: { - 1: 'occlusion', - 2: 'timestamp', - }, - SamplerBindingType: [ - undefined, - 'filtering', - 'non-filtering', - 'comparison', - ], - StencilOperation: [ - undefined, - 'keep', - 'zero', - 'replace', - 'invert', - 'increment-clamp', - 'decrement-clamp', - 'increment-wrap', - 'decrement-wrap', - ], - StorageTextureAccess: [ - undefined, - 'write-only', - 'read-only', - 'read-write', - ], - StoreOp: [ - undefined, - 'store', - 'discard', - ], - TextureAspect: [ - undefined, - 'all', - 'stencil-only', - 'depth-only', - ], - TextureDimension: [ - undefined, - '1d', - '2d', - '3d', - ], - TextureFormat: [ - undefined, - 'r8unorm', - 'r8snorm', - 'r8uint', - 'r8sint', - 'r16uint', - 'r16sint', - 'r16float', - 'rg8unorm', - 'rg8snorm', - 'rg8uint', - 'rg8sint', - 'r32float', - 'r32uint', - 'r32sint', - 'rg16uint', - 'rg16sint', - 'rg16float', - 'rgba8unorm', - 'rgba8unorm-srgb', - 'rgba8snorm', - 'rgba8uint', - 'rgba8sint', - 'bgra8unorm', - 'bgra8unorm-srgb', - 'rgb10a2uint', - 'rgb10a2unorm', - 'rg11b10ufloat', - 'rgb9e5ufloat', - 'rg32float', - 'rg32uint', - 'rg32sint', - 'rgba16uint', - 'rgba16sint', - 'rgba16float', - 'rgba32float', - 'rgba32uint', - 'rgba32sint', - 'stencil8', - 'depth16unorm', - 'depth24plus', - 'depth24plus-stencil8', - 'depth32float', - 'depth32float-stencil8', - 'bc1-rgba-unorm', - 'bc1-rgba-unorm-srgb', - 'bc2-rgba-unorm', - 'bc2-rgba-unorm-srgb', - 'bc3-rgba-unorm', - 'bc3-rgba-unorm-srgb', - 'bc4-r-unorm', - 'bc4-r-snorm', - 'bc5-rg-unorm', - 'bc5-rg-snorm', - 'bc6h-rgb-ufloat', - 'bc6h-rgb-float', - 'bc7-rgba-unorm', - 'bc7-rgba-unorm-srgb', - 'etc2-rgb8unorm', - 'etc2-rgb8unorm-srgb', - 'etc2-rgb8a1unorm', - 'etc2-rgb8a1unorm-srgb', - 'etc2-rgba8unorm', - 'etc2-rgba8unorm-srgb', - 'eac-r11unorm', - 'eac-r11snorm', - 'eac-rg11unorm', - 'eac-rg11snorm', - 'astc-4x4-unorm', - 'astc-4x4-unorm-srgb', - 'astc-5x4-unorm', - 'astc-5x4-unorm-srgb', - 'astc-5x5-unorm', - 'astc-5x5-unorm-srgb', - 'astc-6x5-unorm', - 'astc-6x5-unorm-srgb', - 'astc-6x6-unorm', - 'astc-6x6-unorm-srgb', - 'astc-8x5-unorm', - 'astc-8x5-unorm-srgb', - 'astc-8x6-unorm', - 'astc-8x6-unorm-srgb', - 'astc-8x8-unorm', - 'astc-8x8-unorm-srgb', - 'astc-10x5-unorm', - 'astc-10x5-unorm-srgb', - 'astc-10x6-unorm', - 'astc-10x6-unorm-srgb', - 'astc-10x8-unorm', - 'astc-10x8-unorm-srgb', - 'astc-10x10-unorm', - 'astc-10x10-unorm-srgb', - 'astc-12x10-unorm', - 'astc-12x10-unorm-srgb', - 'astc-12x12-unorm', - 'astc-12x12-unorm-srgb', - ], - TextureSampleType: [ - undefined, - 'float', - 'unfilterable-float', - 'depth', - 'sint', - 'uint', - ], - TextureViewDimension: [ - undefined, - '1d', - '2d', - '2d-array', - 'cube', - 'cube-array', - '3d', - ], - VertexFormat: [ - undefined, - 'uint8x2', - 'uint8x4', - 'sint8x2', - 'sint8x4', - 'unorm8x2', - 'unorm8x4', - 'snorm8x2', - 'snorm8x4', - 'uint16x2', - 'uint16x4', - 'sint16x2', - 'sint16x4', - 'unorm16x2', - 'unorm16x4', - 'snorm16x2', - 'snorm16x4', - 'float16x2', - 'float16x4', - 'float32', - 'float32x2', - 'float32x3', - 'float32x4', - 'uint32', - 'uint32x2', - 'uint32x3', - 'uint32x4', - 'sint32', - 'sint32x2', - 'sint32x3', - 'sint32x4', - 'unorm10-10-10-2', - ], - VertexStepMode: [ - undefined, - 'vertex-buffer-not-used', - 'vertex', - 'instance', - ], + // Maps from enum number to enum string. + {{{ WEBGPU_INT_TO_STRING_TABLES }}} }, // Non-method functions @@ -759,35 +527,35 @@ var LibraryWebGPU = { return 0; }, - // *Reference/*Release + // *AddRef/*Release - {{{ gpu.makeReferenceRelease('Surface') }}} - {{{ gpu.makeReferenceRelease('SwapChain') }}} + {{{ gpu.makeAddRefRelease('Surface') }}} + {{{ gpu.makeAddRefRelease('SwapChain') }}} - {{{ gpu.makeReferenceRelease('Adapter') }}} - {{{ gpu.makeReferenceRelease('Device') }}} - {{{ gpu.makeReferenceRelease('Queue') }}} + {{{ gpu.makeAddRefRelease('Adapter') }}} + {{{ gpu.makeAddRefRelease('Device') }}} + {{{ gpu.makeAddRefRelease('Queue') }}} - {{{ gpu.makeReferenceRelease('CommandBuffer') }}} - {{{ gpu.makeReferenceRelease('CommandEncoder') }}} - {{{ gpu.makeReferenceRelease('RenderPassEncoder') }}} - {{{ gpu.makeReferenceRelease('ComputePassEncoder') }}} + {{{ gpu.makeAddRefRelease('CommandBuffer') }}} + {{{ gpu.makeAddRefRelease('CommandEncoder') }}} + {{{ gpu.makeAddRefRelease('RenderPassEncoder') }}} + {{{ gpu.makeAddRefRelease('ComputePassEncoder') }}} - {{{ gpu.makeReferenceRelease('BindGroup') }}} - {{{ gpu.makeReferenceRelease('Buffer') }}} - {{{ gpu.makeReferenceRelease('Sampler') }}} - {{{ gpu.makeReferenceRelease('Texture') }}} - {{{ gpu.makeReferenceRelease('TextureView') }}} - {{{ gpu.makeReferenceRelease('QuerySet') }}} + {{{ gpu.makeAddRefRelease('BindGroup') }}} + {{{ gpu.makeAddRefRelease('Buffer') }}} + {{{ gpu.makeAddRefRelease('Sampler') }}} + {{{ gpu.makeAddRefRelease('Texture') }}} + {{{ gpu.makeAddRefRelease('TextureView') }}} + {{{ gpu.makeAddRefRelease('QuerySet') }}} - {{{ gpu.makeReferenceRelease('BindGroupLayout') }}} - {{{ gpu.makeReferenceRelease('PipelineLayout') }}} - {{{ gpu.makeReferenceRelease('RenderPipeline') }}} - {{{ gpu.makeReferenceRelease('ComputePipeline') }}} - {{{ gpu.makeReferenceRelease('ShaderModule') }}} + {{{ gpu.makeAddRefRelease('BindGroupLayout') }}} + {{{ gpu.makeAddRefRelease('PipelineLayout') }}} + {{{ gpu.makeAddRefRelease('RenderPipeline') }}} + {{{ gpu.makeAddRefRelease('ComputePipeline') }}} + {{{ gpu.makeAddRefRelease('ShaderModule') }}} - {{{ gpu.makeReferenceRelease('RenderBundleEncoder') }}} - {{{ gpu.makeReferenceRelease('RenderBundle') }}} + {{{ gpu.makeAddRefRelease('RenderBundleEncoder') }}} + {{{ gpu.makeAddRefRelease('RenderBundle') }}} // *Destroy @@ -835,7 +603,7 @@ var LibraryWebGPU = { assert(queueId, 'wgpuDeviceGetQueue: queue was missing or null'); #endif // Returns a new reference to the existing queue. - WebGPU.mgrQueue.reference(queueId); + WebGPU.mgrQueue.addRef(queueId); return queueId; }, @@ -1244,32 +1012,15 @@ var LibraryWebGPU = { return WebGPU.mgrRenderBundleEncoder.create(device.createRenderBundleEncoder(desc)); }, - $generateComputePipelineDesc__internal: true, - $generateComputePipelineDesc: (descriptor) => { - {{{ gpu.makeCheckDescriptor('descriptor') }}} - - var desc = { - "label": undefined, - "layout": WebGPU.makePipelineLayout( - {{{ makeGetValue('descriptor', C_STRUCTS.WGPUComputePipelineDescriptor.layout, '*') }}}), - "compute": WebGPU.makeProgrammableStageDescriptor( - descriptor + {{{ C_STRUCTS.WGPUComputePipelineDescriptor.compute }}}), - }; - var labelPtr = {{{ makeGetValue('descriptor', C_STRUCTS.WGPUComputePipelineDescriptor.label, '*') }}}; - if (labelPtr) desc["label"] = UTF8ToString(labelPtr); - return desc; - }, - - wgpuDeviceCreateComputePipeline__deps: ['$generateComputePipelineDesc'], wgpuDeviceCreateComputePipeline: (deviceId, descriptor) => { - var desc = generateComputePipelineDesc(descriptor); + var desc = WebGPU.makeComputePipelineDesc(descriptor); var device = WebGPU.mgrDevice.get(deviceId); return WebGPU.mgrComputePipeline.create(device.createComputePipeline(desc)); }, - wgpuDeviceCreateComputePipelineAsync__deps: ['$callUserCallback', '$stringToUTF8OnStack', '$generateComputePipelineDesc'], + wgpuDeviceCreateComputePipelineAsync__deps: ['$callUserCallback', '$stringToUTF8OnStack'], wgpuDeviceCreateComputePipelineAsync: (deviceId, descriptor, callback, userdata) => { - var desc = generateComputePipelineDesc(descriptor); + var desc = WebGPU.makeComputePipelineDesc(descriptor); var device = WebGPU.mgrDevice.get(deviceId); {{{ runtimeKeepalivePush() }}} device.createComputePipelineAsync(desc).then((pipeline) => { @@ -1295,221 +1046,15 @@ var LibraryWebGPU = { }); }, - $generateRenderPipelineDesc__internal: true, - $generateRenderPipelineDesc: (descriptor) => { - {{{ gpu.makeCheckDescriptor('descriptor') }}} - function makePrimitiveState(rsPtr) { - if (!rsPtr) return undefined; - {{{ gpu.makeCheck('rsPtr') }}} - - // TODO: This small hack assumes that there's only one type that can be in the chain of - // WGPUPrimitiveState. The correct thing would be to traverse the chain, but unclippedDepth - // is going to move into the core object soon, so we'll just do this for now. See: - // https://github.com/webgpu-native/webgpu-headers/issues/212#issuecomment-1682801259 - var nextInChainPtr = {{{ makeGetValue('rsPtr', C_STRUCTS.WGPUPrimitiveState.nextInChain, '*') }}}; - var sType = nextInChainPtr ? {{{ gpu.makeGetU32('nextInChainPtr', C_STRUCTS.WGPUChainedStruct.sType) }}} : 0; - - return { - "topology": WebGPU.PrimitiveTopology[ - {{{ gpu.makeGetU32('rsPtr', C_STRUCTS.WGPUPrimitiveState.topology) }}}], - "stripIndexFormat": WebGPU.IndexFormat[ - {{{ gpu.makeGetU32('rsPtr', C_STRUCTS.WGPUPrimitiveState.stripIndexFormat) }}}], - "frontFace": WebGPU.FrontFace[ - {{{ gpu.makeGetU32('rsPtr', C_STRUCTS.WGPUPrimitiveState.frontFace) }}}], - "cullMode": WebGPU.CullMode[ - {{{ gpu.makeGetU32('rsPtr', C_STRUCTS.WGPUPrimitiveState.cullMode) }}}], - "unclippedDepth": sType === {{{ gpu.SType.PrimitiveDepthClipControl }}} && {{{ gpu.makeGetBool('nextInChainPtr', C_STRUCTS.WGPUPrimitiveDepthClipControl.unclippedDepth) }}}, - }; - } - - function makeBlendComponent(bdPtr) { - if (!bdPtr) return undefined; - return { - "operation": WebGPU.BlendOperation[ - {{{ gpu.makeGetU32('bdPtr', C_STRUCTS.WGPUBlendComponent.operation) }}}], - "srcFactor": WebGPU.BlendFactor[ - {{{ gpu.makeGetU32('bdPtr', C_STRUCTS.WGPUBlendComponent.srcFactor) }}}], - "dstFactor": WebGPU.BlendFactor[ - {{{ gpu.makeGetU32('bdPtr', C_STRUCTS.WGPUBlendComponent.dstFactor) }}}], - }; - } - - function makeBlendState(bsPtr) { - if (!bsPtr) return undefined; - return { - "alpha": makeBlendComponent(bsPtr + {{{ C_STRUCTS.WGPUBlendState.alpha }}}), - "color": makeBlendComponent(bsPtr + {{{ C_STRUCTS.WGPUBlendState.color }}}), - }; - } - - function makeColorState(csPtr) { - {{{ gpu.makeCheckDescriptor('csPtr') }}} - var formatInt = {{{ gpu.makeGetU32('csPtr', C_STRUCTS.WGPUColorTargetState.format) }}}; - return formatInt === {{{ gpu.TextureFormat.Undefined }}} ? undefined : { - "format": WebGPU.TextureFormat[formatInt], - "blend": makeBlendState({{{ makeGetValue('csPtr', C_STRUCTS.WGPUColorTargetState.blend, '*') }}}), - "writeMask": {{{ gpu.makeGetU32('csPtr', C_STRUCTS.WGPUColorTargetState.writeMask) }}}, - }; - } - - function makeColorStates(count, csArrayPtr) { - var states = []; - for (var i = 0; i < count; ++i) { - states.push(makeColorState(csArrayPtr + {{{ C_STRUCTS.WGPUColorTargetState.__size__ }}} * i)); - } - return states; - } - - function makeStencilStateFace(ssfPtr) { - {{{ gpu.makeCheck('ssfPtr') }}} - return { - "compare": WebGPU.CompareFunction[ - {{{ gpu.makeGetU32('ssfPtr', C_STRUCTS.WGPUStencilFaceState.compare) }}}], - "failOp": WebGPU.StencilOperation[ - {{{ gpu.makeGetU32('ssfPtr', C_STRUCTS.WGPUStencilFaceState.failOp) }}}], - "depthFailOp": WebGPU.StencilOperation[ - {{{ gpu.makeGetU32('ssfPtr', C_STRUCTS.WGPUStencilFaceState.depthFailOp) }}}], - "passOp": WebGPU.StencilOperation[ - {{{ gpu.makeGetU32('ssfPtr', C_STRUCTS.WGPUStencilFaceState.passOp) }}}], - }; - } - - function makeDepthStencilState(dssPtr) { - if (!dssPtr) return undefined; - - {{{ gpu.makeCheck('dssPtr') }}} - return { - "format": WebGPU.TextureFormat[ - {{{ gpu.makeGetU32('dssPtr', C_STRUCTS.WGPUDepthStencilState.format) }}}], - "depthWriteEnabled": {{{ gpu.makeGetBool('dssPtr', C_STRUCTS.WGPUDepthStencilState.depthWriteEnabled) }}}, - "depthCompare": WebGPU.CompareFunction[ - {{{ gpu.makeGetU32('dssPtr', C_STRUCTS.WGPUDepthStencilState.depthCompare) }}}], - "stencilFront": makeStencilStateFace(dssPtr + {{{ C_STRUCTS.WGPUDepthStencilState.stencilFront }}}), - "stencilBack": makeStencilStateFace(dssPtr + {{{ C_STRUCTS.WGPUDepthStencilState.stencilBack }}}), - "stencilReadMask": {{{ gpu.makeGetU32('dssPtr', C_STRUCTS.WGPUDepthStencilState.stencilReadMask) }}}, - "stencilWriteMask": {{{ gpu.makeGetU32('dssPtr', C_STRUCTS.WGPUDepthStencilState.stencilWriteMask) }}}, - "depthBias": {{{ makeGetValue('dssPtr', C_STRUCTS.WGPUDepthStencilState.depthBias, 'i32') }}}, - "depthBiasSlopeScale": {{{ makeGetValue('dssPtr', C_STRUCTS.WGPUDepthStencilState.depthBiasSlopeScale, 'float') }}}, - "depthBiasClamp": {{{ makeGetValue('dssPtr', C_STRUCTS.WGPUDepthStencilState.depthBiasClamp, 'float') }}}, - }; - } - - function makeVertexAttribute(vaPtr) { - {{{ gpu.makeCheck('vaPtr') }}} - return { - "format": WebGPU.VertexFormat[ - {{{ gpu.makeGetU32('vaPtr', C_STRUCTS.WGPUVertexAttribute.format) }}}], - "offset": {{{ gpu.makeGetU64('vaPtr', C_STRUCTS.WGPUVertexAttribute.offset) }}}, - "shaderLocation": {{{ gpu.makeGetU32('vaPtr', C_STRUCTS.WGPUVertexAttribute.shaderLocation) }}}, - }; - } - - function makeVertexAttributes(count, vaArrayPtr) { - var vas = []; - for (var i = 0; i < count; ++i) { - vas.push(makeVertexAttribute(vaArrayPtr + i * {{{ C_STRUCTS.WGPUVertexAttribute.__size__ }}})); - } - return vas; - } - - function makeVertexBuffer(vbPtr) { - if (!vbPtr) return undefined; - var stepModeInt = {{{ gpu.makeGetU32('vbPtr', C_STRUCTS.WGPUVertexBufferLayout.stepMode) }}}; - return stepModeInt === {{{ gpu.VertexStepMode.VertexBufferNotUsed }}} ? null : { - "arrayStride": {{{ gpu.makeGetU64('vbPtr', C_STRUCTS.WGPUVertexBufferLayout.arrayStride) }}}, - "stepMode": WebGPU.VertexStepMode[stepModeInt], - "attributes": makeVertexAttributes( - {{{ gpu.makeGetU32('vbPtr', C_STRUCTS.WGPUVertexBufferLayout.attributeCount) }}}, - {{{ makeGetValue('vbPtr', C_STRUCTS.WGPUVertexBufferLayout.attributes, '*') }}}), - }; - } - - function makeVertexBuffers(count, vbArrayPtr) { - if (!count) return undefined; - - var vbs = []; - for (var i = 0; i < count; ++i) { - vbs.push(makeVertexBuffer(vbArrayPtr + i * {{{ C_STRUCTS.WGPUVertexBufferLayout.__size__ }}})); - } - return vbs; - } - - function makeVertexState(viPtr) { - if (!viPtr) return undefined; - {{{ gpu.makeCheckDescriptor('viPtr') }}} - var desc = { - "module": WebGPU.mgrShaderModule.get( - {{{ makeGetValue('viPtr', C_STRUCTS.WGPUVertexState.module, '*') }}}), - "constants": WebGPU.makePipelineConstants( - {{{ gpu.makeGetU32('viPtr', C_STRUCTS.WGPUVertexState.constantCount) }}}, - {{{ makeGetValue('viPtr', C_STRUCTS.WGPUVertexState.constants, '*') }}}), - "buffers": makeVertexBuffers( - {{{ gpu.makeGetU32('viPtr', C_STRUCTS.WGPUVertexState.bufferCount) }}}, - {{{ makeGetValue('viPtr', C_STRUCTS.WGPUVertexState.buffers, '*') }}}), - }; - var entryPointPtr = {{{ makeGetValue('viPtr', C_STRUCTS.WGPUVertexState.entryPoint, '*') }}}; - if (entryPointPtr) desc["entryPoint"] = UTF8ToString(entryPointPtr); - return desc; - } - - function makeMultisampleState(msPtr) { - if (!msPtr) return undefined; - {{{ gpu.makeCheckDescriptor('msPtr') }}} - return { - "count": {{{ gpu.makeGetU32('msPtr', C_STRUCTS.WGPUMultisampleState.count) }}}, - "mask": {{{ gpu.makeGetU32('msPtr', C_STRUCTS.WGPUMultisampleState.mask) }}}, - "alphaToCoverageEnabled": {{{ gpu.makeGetBool('msPtr', C_STRUCTS.WGPUMultisampleState.alphaToCoverageEnabled) }}}, - }; - } - - function makeFragmentState(fsPtr) { - if (!fsPtr) return undefined; - {{{ gpu.makeCheckDescriptor('fsPtr') }}} - var desc = { - "module": WebGPU.mgrShaderModule.get( - {{{ makeGetValue('fsPtr', C_STRUCTS.WGPUFragmentState.module, '*') }}}), - "constants": WebGPU.makePipelineConstants( - {{{ gpu.makeGetU32('fsPtr', C_STRUCTS.WGPUFragmentState.constantCount) }}}, - {{{ makeGetValue('fsPtr', C_STRUCTS.WGPUFragmentState.constants, '*') }}}), - "targets": makeColorStates( - {{{ gpu.makeGetU32('fsPtr', C_STRUCTS.WGPUFragmentState.targetCount) }}}, - {{{ makeGetValue('fsPtr', C_STRUCTS.WGPUFragmentState.targets, '*') }}}), - }; - var entryPointPtr = {{{ makeGetValue('fsPtr', C_STRUCTS.WGPUFragmentState.entryPoint, '*') }}}; - if (entryPointPtr) desc["entryPoint"] = UTF8ToString(entryPointPtr); - return desc; - } - - var desc = { - "label": undefined, - "layout": WebGPU.makePipelineLayout( - {{{ makeGetValue('descriptor', C_STRUCTS.WGPURenderPipelineDescriptor.layout, '*') }}}), - "vertex": makeVertexState( - descriptor + {{{ C_STRUCTS.WGPURenderPipelineDescriptor.vertex }}}), - "primitive": makePrimitiveState( - descriptor + {{{ C_STRUCTS.WGPURenderPipelineDescriptor.primitive }}}), - "depthStencil": makeDepthStencilState( - {{{ makeGetValue('descriptor', C_STRUCTS.WGPURenderPipelineDescriptor.depthStencil, '*') }}}), - "multisample": makeMultisampleState( - descriptor + {{{ C_STRUCTS.WGPURenderPipelineDescriptor.multisample }}}), - "fragment": makeFragmentState( - {{{ makeGetValue('descriptor', C_STRUCTS.WGPURenderPipelineDescriptor.fragment, '*') }}}), - }; - var labelPtr = {{{ makeGetValue('descriptor', C_STRUCTS.WGPURenderPipelineDescriptor.label, '*') }}}; - if (labelPtr) desc["label"] = UTF8ToString(labelPtr); - return desc; - }, - - wgpuDeviceCreateRenderPipeline__deps: ['$generateRenderPipelineDesc'], wgpuDeviceCreateRenderPipeline: (deviceId, descriptor) => { - var desc = generateRenderPipelineDesc(descriptor); + var desc = WebGPU.makeRenderPipelineDesc(descriptor); var device = WebGPU.mgrDevice.get(deviceId); return WebGPU.mgrRenderPipeline.create(device.createRenderPipeline(desc)); }, - wgpuDeviceCreateRenderPipelineAsync__deps: ['$callUserCallback', '$stringToUTF8OnStack', '$generateRenderPipelineDesc'], + wgpuDeviceCreateRenderPipelineAsync__deps: ['$callUserCallback', '$stringToUTF8OnStack'], wgpuDeviceCreateRenderPipelineAsync: (deviceId, descriptor, callback, userdata) => { - var desc = generateRenderPipelineDesc(descriptor); + var desc = WebGPU.makeRenderPipelineDesc(descriptor); var device = WebGPU.mgrDevice.get(deviceId); {{{ runtimeKeepalivePush() }}} device.createRenderPipelineAsync(desc).then((pipeline) => { @@ -2691,8 +2236,8 @@ var LibraryWebGPU = { desc["defaultQueue"] = defaultQueueDesc; } - var deviceLostCallbackPtr = {{{ makeGetValue('descriptor', C_STRUCTS.WGPUDeviceDescriptor.deviceLostCallback, '*') }}}; - var deviceLostUserdataPtr = {{{ makeGetValue('descriptor', C_STRUCTS.WGPUDeviceDescriptor.deviceLostUserdata, '*') }}}; + var deviceLostCallbackPtr = {{{ makeGetValue('descriptor', C_STRUCTS.WGPUDeviceDescriptor.deviceLostCallbackInfo + C_STRUCTS.WGPUDeviceLostCallbackInfo.callback, '*') }}}; + var deviceLostUserdataPtr = {{{ makeGetValue('descriptor', C_STRUCTS.WGPUDeviceDescriptor.deviceLostCallbackInfo + C_STRUCTS.WGPUDeviceLostCallbackInfo.userdata, '*') }}}; var labelPtr = {{{ makeGetValue('descriptor', C_STRUCTS.WGPUDeviceDescriptor.label, '*') }}}; if (labelPtr) desc["label"] = UTF8ToString(labelPtr); @@ -2786,7 +2331,7 @@ var LibraryWebGPU = { var texture = WebGPU.mgrTexture.create(context.getCurrentTexture()); {{{ makeSetValue('surfaceTexturePtr', C_STRUCTS.WGPUSurfaceTexture.texture, 'texture', '*') }}}; {{{ makeSetValue('surfaceTexturePtr', C_STRUCTS.WGPUSurfaceTexture.suboptimal, '0', 'i32') }}}; - {{{ makeSetValue('surfaceTexturePtr', C_STRUCTS.WGPUSurfaceTexture.status, + {{{ makeSetValue('surfaceTexturePtr', C_STRUCTS.WGPUSurfaceTexture.status, gpu.SurfaceGetCurrentTextureStatus.Success, 'i32') }}}; } catch (ex) { #if ASSERTIONS @@ -2879,4 +2424,4 @@ for (const key of Object.keys(LibraryWebGPU)) { } autoAddDeps(LibraryWebGPU, '$WebGPU'); -addToLibrary(LibraryWebGPU); +mergeInto(LibraryManager.library, LibraryWebGPU); diff --git a/third_party/emdawnwebgpu/webgpu.cpp b/third_party/emdawnwebgpu/webgpu.cpp index e65fbe2d5bb..797e629f878 100644 --- a/third_party/emdawnwebgpu/webgpu.cpp +++ b/third_party/emdawnwebgpu/webgpu.cpp @@ -20,21 +20,21 @@ // Standalone (non-method) functions -WGPUInstance wgpuCreateInstance(const WGPUInstanceDescriptor* descriptor) { - assert(descriptor == nullptr); // descriptor not implemented yet +WGPUInstance wgpuCreateInstance([[maybe_unused]] const WGPUInstanceDescriptor* descriptor) { + assert(descriptor == nullptr); // descriptor not implemented yet return reinterpret_cast(1); } // Instance -void wgpuInstanceReference(WGPUInstance) { /* no-op for now */ } +void wgpuInstanceAddRef(WGPUInstance) { /* no-op for now */ } void wgpuInstanceRelease(WGPUInstance) { /* no-op for now */ } // WGPUSurface -void wgpuSurfaceGetCapabilities(WGPUSurface surface, - WGPUAdapter adapter, - WGPUSurfaceCapabilities* capabilities) { +WGPUStatus wgpuSurfaceGetCapabilities(WGPUSurface surface, + WGPUAdapter adapter, + WGPUSurfaceCapabilities* capabilities) { assert(capabilities->nextInChain == nullptr); // TODO: Return WGPUStatus_Error static constexpr std::array kSurfaceFormatsRGBAFirst = { @@ -59,6 +59,7 @@ void wgpuSurfaceGetCapabilities(WGPUSurface surface, break; default: assert(false); + return WGPUStatus_Error; } { @@ -75,11 +76,13 @@ void wgpuSurfaceGetCapabilities(WGPUSurface surface, capabilities->alphaModeCount = kAlphaModes.size(); capabilities->alphaModes = kAlphaModes.data(); } -}; + + return WGPUStatus_Success; +} // WGPUSurfaceCapabilities -void wgpuSurfaceCapabilitiesFreeMembers(WGPUSurfaceCapabilities value) { +void wgpuSurfaceCapabilitiesFreeMembers(WGPUSurfaceCapabilities) { // wgpuSurfaceCapabilities doesn't currently allocate anything. }