├── .gitignore
├── .clang-format
├── src
├── example_05
│ ├── README.md
│ ├── CMakeLists.txt
│ └── main.cc
├── example_04
│ ├── README.md
│ ├── CMakeLists.txt
│ └── main.cc
├── example_03
│ ├── README.md
│ ├── CMakeLists.txt
│ └── main.cc
├── example_02
│ ├── README.md
│ ├── CMakeLists.txt
│ └── main.cc
├── example_01
│ ├── README.md
│ ├── CMakeLists.txt
│ └── main.cc
├── CMakeLists.txt
└── common
│ ├── CMakeLists.txt
│ ├── glfw.h
│ ├── vec3.h
│ ├── expected.h
│ ├── wgpu.h
│ ├── callback.cc
│ ├── webgpu_helpers.cc
│ ├── callback.h
│ ├── mat4.h
│ ├── webgpu_helpers.h
│ ├── mat4.cc
│ ├── log.h
│ └── log.cc
├── AUTHORS
├── docs
├── index.html
├── 01_instance_adapter_and_device_info.md.html
└── 00_setup.md.html
├── CONTRIBUTING.md
├── .gitmodules
├── README.md
├── third_party
└── CMakeLists.txt
├── CMakeLists.txt
├── CODE_OF_CONDUCT.md
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | out
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/.clang-format:
--------------------------------------------------------------------------------
1 | # http://clang.llvm.org/docs/ClangFormatStyleOptions.html
2 | BasedOnStyle: Chromium
3 |
--------------------------------------------------------------------------------
/src/example_05/README.md:
--------------------------------------------------------------------------------
1 | # Dusk Example 05 -- Instanced Cubes
2 |
3 | Multiple instanced cubes.
4 |
--------------------------------------------------------------------------------
/src/example_04/README.md:
--------------------------------------------------------------------------------
1 | # Dusk Example 04 -- Rotating Cube
2 |
3 | Rotating cube, uploads uniform data per frame.
4 |
--------------------------------------------------------------------------------
/src/example_03/README.md:
--------------------------------------------------------------------------------
1 | # Dusk Example 03 -- Using an Index Buffer
2 |
3 | Similar to [Example 02](../example_02) except an index buffer
4 | with the vertex draw order.
5 |
--------------------------------------------------------------------------------
/src/example_02/README.md:
--------------------------------------------------------------------------------
1 | # Dusk Example 02 -- Coloured Triangle
2 |
3 | Draws a multi-colour triangle to the screen providing the
4 | vertex and colour information in a vertex buffer.
5 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | # This is the list of Dusk authors for copyright purposes.
2 | #
3 | # This does not necessarily list everyone who has contributed code, since in
4 | # some cases, their employer may be the copyright holder. To see the full list
5 | # of contributors, see the revision history in source control.
6 | Google LLC
7 |
--------------------------------------------------------------------------------
/src/example_01/README.md:
--------------------------------------------------------------------------------
1 | # Dusk Example 01 -- Instance, Adapter and Device Information
2 |
3 | An example showing how to create an Instance, an Adapter and a Device and then
4 | printing out all the available information on those objects.
5 |
6 | See [Instance, Adapter and Device Information](https://dj2.github.io/Dusk/01_instance_adapter_and_device_info.md.html)
7 | for more information.
8 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | **Dusk WebGPU Native Documents**
4 | Jan 28, 2025
5 |
6 | Articles discussing various aspects of WebGPU native development with Dawn.
7 |
8 | * [Setup](./00_setup.md.html)
9 | * [Instance, Adapter and Device Info](./01_instance_adapter_and_device_info.md.html)
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2022 The Dusk Authors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | add_subdirectory(common)
16 |
17 | add_subdirectory(example_01)
18 | add_subdirectory(example_02)
19 | add_subdirectory(example_03)
20 | add_subdirectory(example_04)
21 | add_subdirectory(example_05)
22 |
--------------------------------------------------------------------------------
/src/example_01/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2022 The Dusk Authors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | add_executable(dusk_example_01)
16 | dusk_compile_options(dusk_example_01)
17 | target_sources(dusk_example_01 PRIVATE
18 | main.cc
19 | )
20 | target_link_libraries(dusk_example_01
21 | dusk::common
22 | webgpu_dawn
23 | webgpu_cpp
24 | )
25 |
--------------------------------------------------------------------------------
/src/example_02/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2022 The Dusk Authors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | add_executable(dusk_example_02)
16 | dusk_compile_options(dusk_example_02)
17 | target_sources(dusk_example_02 PRIVATE
18 | main.cc
19 | )
20 | target_link_libraries(dusk_example_02
21 | dusk::common
22 | webgpu_dawn
23 | webgpu_cpp
24 | webgpu_glfw
25 | glfw
26 | )
27 |
--------------------------------------------------------------------------------
/src/example_03/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2022 The Dusk Authors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | add_executable(dusk_example_03 ${DUSK_SRCS})
16 | dusk_compile_options(dusk_example_03)
17 | target_sources(dusk_example_03 PRIVATE
18 | main.cc
19 | )
20 | target_link_libraries(dusk_example_03
21 | dusk::common
22 | webgpu_dawn
23 | webgpu_cpp
24 | webgpu_glfw
25 | glfw
26 | )
27 |
--------------------------------------------------------------------------------
/src/example_04/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2022 The Dusk Authors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | add_executable(dusk_example_04 ${DUSK_SRCS})
16 | dusk_compile_options(dusk_example_04)
17 | target_sources(dusk_example_04 PRIVATE
18 | main.cc
19 | )
20 | target_link_libraries(dusk_example_04
21 | dusk::common
22 | webgpu_dawn
23 | webgpu_cpp
24 | webgpu_glfw
25 | glfw
26 | )
27 |
--------------------------------------------------------------------------------
/src/example_05/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2022 The Dusk Authors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | add_executable(dusk_example_05 ${DUSK_SRCS})
16 | dusk_compile_options(dusk_example_05)
17 | target_sources(dusk_example_05 PRIVATE
18 | main.cc
19 | )
20 | target_link_libraries(dusk_example_05
21 | dusk::common
22 | webgpu_dawn
23 | webgpu_cpp
24 | webgpu_glfw
25 | glfw
26 | )
27 |
--------------------------------------------------------------------------------
/src/common/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2025 The Dusk Authors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | add_library(dusk-common)
16 | add_library(dusk::common ALIAS dusk-common)
17 | dusk_compile_options(dusk-common)
18 | target_sources(dusk-common PRIVATE
19 | callback.cc
20 | callback.h
21 | glfw.h
22 | log.cc
23 | log.h
24 | mat4.cc
25 | mat4.h
26 | vec3.h
27 | webgpu_helpers.cc
28 | webgpu_helpers.h
29 | wgpu.h
30 | )
31 |
32 | target_link_libraries(dusk-common PRIVATE
33 | webgpu_cpp
34 | )
35 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement (CLA). You (or your employer) retain the copyright to your
10 | contribution; this simply gives us permission to use and redistribute your
11 | contributions as part of the project. Head over to
12 | to see your current agreements on file or
13 | to sign a new one.
14 |
15 | You generally only need to submit a CLA once, so if you've already submitted one
16 | (even if it was for a different project), you probably don't need to do it
17 | again.
18 |
19 | ## Code Reviews
20 |
21 | All submissions, including submissions by project members, require review. We
22 | use GitHub pull requests for this purpose. Consult
23 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
24 | information on using pull requests.
25 |
26 | ## Community Guidelines
27 |
28 | This project follows
29 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/).
30 |
--------------------------------------------------------------------------------
/src/common/glfw.h:
--------------------------------------------------------------------------------
1 | // Copyright 2025 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #pragma clang diagnostic push
18 | #pragma clang diagnostic ignored "-Wdocumentation"
19 | #pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
20 | #pragma clang diagnostic ignored "-Wdouble-promotion"
21 | #pragma clang diagnostic ignored "-Wpadded"
22 | #pragma clang diagnostic ignored "-Wshadow-field-in-constructor"
23 | #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
24 |
25 | #define GLFW_INCLUDE_NONE
26 | #include // IWYU pragma: export
27 | #include // IWYU pragma: export
28 |
29 | #pragma clang diagnostic pop
30 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "third_party/dawn"]
2 | path = third_party/dawn
3 | url = https://dawn.googlesource.com/dawn
4 | [submodule "third_party/abseil-cpp"]
5 | path = third_party/abseil-cpp
6 | url = https://github.com/abseil/abseil-cpp
7 | [submodule "third_party/spirv-tools"]
8 | path = third_party/spirv-tools
9 | url = https://github.com/KhronosGroup/Spirv-Tools
10 | [submodule "third_party/spirv-headers"]
11 | path = third_party/spirv-headers
12 | url = https://github.com/KhronosGroup/Spirv-Headers
13 | [submodule "third_party/jinja"]
14 | path = third_party/jinja
15 | url = https://github.com/pallets/jinja
16 | [submodule "third_party/markupsafe"]
17 | path = third_party/markupsafe
18 | url = https://github.com/mitsuhiko/markupsafe
19 | [submodule "third_party/vulkan-tools"]
20 | path = third_party/vulkan-tools
21 | url = https://github.com/KhronosGroup/Vulkan-Tools
22 | [submodule "third_party/vulkan-headers"]
23 | path = third_party/vulkan-headers
24 | url = https://github.com/KhronosGroup/Vulkan-Headers
25 | [submodule "third_party/glfw"]
26 | path = third_party/glfw
27 | url = https://github.com/glfw/glfw
28 | [submodule "third_party/vulkan-utility-libraries"]
29 | path = third_party/vulkan-utility-libraries
30 | url = https://github.com/KhronosGroup/Vulkan-Utility-Libraries
31 | [submodule "third_party/webgpu-headers"]
32 | path = third_party/webgpu-headers
33 | url = https://github.com/webgpu-native/webgpu-headers
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dusk
2 |
3 | The Dusk repository contains examples of small programs using the WebGPU
4 | native API.
5 |
6 | This is not an officially supported Google product.
7 |
8 | ## Requirements
9 | * `git`
10 | * `cmake` 3.22 or later
11 | * `ninja`
12 | * `clang` or `gcc`
13 |
14 | ## Building
15 | ```
16 | git submodule init
17 | git submodule update
18 | mkdir -p out/Debug
19 | cd out/Debug
20 | cmake -GNinja ../..
21 | ninja
22 | ```
23 |
24 | ## Setup
25 | Dawn has a lot of dependencies, in order to simplify things we're using git
26 | submodules to handle the heavy lifting of checkout out all the needed
27 | dependencies.
28 |
29 | The examples are using `CMake` for build configuration. The CMake is pretty
30 | standard, c++23 is used for a few of the newer features. When linking the
31 | executables we add the following to the `target_link_libraries`:
32 |
33 | * `webgpu_dawn`
34 | * `webgpu_cpp`
35 | * `webgpu_glfw`
36 | * `glfw`
37 |
38 | The last two are only needed because `GLFW` is being used for the window
39 | management. If something else is providing the window, and code is written to
40 | create the needed surface, the last two libraries can be removed.
41 |
42 | ## Examples
43 |
44 | The following examples are provided:
45 |
46 | * [01 - Adapter and Device Information](src/example_01/README.md)
47 | * [02 - Coloured Triangle](src/example_02/README.md)
48 | * [03 - Using an Index Buffer](src/example_03/README.md)
49 | * [04 - Rotating Cube](src/example_04/README.md)
50 |
51 |
--------------------------------------------------------------------------------
/src/common/vec3.h:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include
16 |
17 | #pragma once
18 |
19 | namespace dusk {
20 |
21 | /// A vector in 3-space
22 | class Vec3 {
23 | public:
24 | /// Creates a new vector with the given values
25 | Vec3(float x, float y, float z) : x_(x), y_(y), z_(z) {}
26 |
27 | /// Returns the length of the vector
28 | float length() const { return sqrtf((x_ * x_) + (y_ * y_) + (z_ * z_)); }
29 |
30 | /// Returns the normalized version of the vector
31 | Vec3 normalize() const {
32 | auto l = length();
33 | if (l == 0.0f) {
34 | return Vec3{0., 0., 0.};
35 | }
36 |
37 | auto inv = 1 / l;
38 | return Vec3{x_ * inv, y_ * inv, z_ * inv};
39 | }
40 |
41 | inline float x() const { return x_; }
42 | inline float y() const { return y_; }
43 | inline float z() const { return z_; }
44 |
45 | private:
46 | float x_ = 0;
47 | float y_ = 0;
48 | float z_ = 0;
49 | };
50 |
51 | } // namespace dusk
52 |
--------------------------------------------------------------------------------
/src/common/expected.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | //! @file expected.h
4 |
5 | #include
6 | #include
7 | #include
8 |
9 | namespace dusk {
10 |
11 | /// Checks if `item` is valid, if so returns the expected value. If not, prints
12 | /// the error and calls `exit`.
13 | template
14 | requires(!std::is_void_v)
15 | T valid_or_exit(std::expected item) {
16 | if (!item) {
17 | std::println(stderr, "{}", item.error());
18 | exit(1);
19 | }
20 | return std::move(item.value());
21 | }
22 |
23 | /// Checks if `item` is valid, if so returns. If not, prints the error and calls
24 | /// `exit`.
25 | template
26 | requires(std::is_void_v)
27 | void valid_or_exit(std::expected item) {
28 | if (!item) {
29 | std::println(stderr, "{}", item.error());
30 | exit(1);
31 | }
32 | }
33 |
34 | /// Checks if `item` is valid, otherwise propagates the result of the `error`
35 | /// call as an `expected`.
36 | #define VALID_OR_PROPAGATE(item) \
37 | do { \
38 | if (!(item)) { \
39 | return std::unexpected((item).error()); \
40 | } \
41 | } while (false)
42 |
43 | /// Checks if `check` is true, otherwise returns an `unexpected` result with
44 | /// `err` as the error value.
45 | #define VALID_OR_RETURN(check, err) \
46 | do { \
47 | if (!(check)) { \
48 | return std::unexpected((err)); \
49 | } \
50 | } while (false)
51 |
52 | } // namespace dusk
53 |
--------------------------------------------------------------------------------
/third_party/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2025 The Dusk Authors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | set(DAWN_THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/)
16 | set(DAWN_SPIRV_HEADERS_DIR ${DAWN_THIRD_PARTY_DIR}/spirv-headers)
17 | set(DAWN_SPIRV_TOOLS_DIR ${DAWN_THIRD_PARTY_DIR}/spirv-tools)
18 | set(DAWN_VULKAN_HEADERS_DIR ${DAWN_THIRD_PARTY_DIR}/vulkan-headers)
19 | set(DAWN_VULKAN_UTILITY_LIBRARIES_DIR ${DAWN_THIRD_PARTY_DIR}/vulkan-utility-libraries)
20 | set(DAWN_JINJA2_DIR ${DAWN_THIRD_PARTY_DIR}/jinja/src/jinja2)
21 | set(DAWN_MARKUPSAFE_DIR ${DAWN_THIRD_PARTY_DIR}/markupsafe/src/markupsafe)
22 | set(DAWN_WEBGPU_HEADERS_DIR ${DAWN_THIRD_PARTY_DIR}/webgpu-headers)
23 |
24 | set(DAWN_BUILD_GEN_DIR ${CMAKE_CURRENT_BINARY_DIR}/gen)
25 | set(DAWN_ENABLE_DESKTOP_GL OFF)
26 | set(DAWN_ENABLE_OPENGLES OFF)
27 | set(DAWN_ENABLE_NULL OFF)
28 | set(DAWN_BUILD_SAMPLES OFF)
29 | set(DAWN_BUILD_PROTOBUF OFF)
30 |
31 | set(TINT_BUILD_DOCS OFF)
32 | set(TINT_BUILD_TESTS OFF)
33 | set(TINT_BUILD_SAMPLES OFF)
34 | set(TINT_BUILD_GLSL_VALIDATOR OFF)
35 | set(TINT_BUILD_GLSL_WRITER OFF)
36 | set(TINT_BUILD_SPV_READER OFF)
37 |
38 | if(NOT APPLE)
39 | # Build Dawn as a shared library.
40 | set(DAWN_BUILD_MONOLITHIC_LIBRARY SHARED)
41 | endif()
42 |
43 | add_definitions(
44 | -Wno-deprecated-builtins # From Abseil
45 | -Wno-nullability-extension # From abseil
46 | -Wno-unknown-warning-option # SPIRV-Tools
47 | )
48 |
49 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/dawn EXCLUDE_FROM_ALL)
50 |
--------------------------------------------------------------------------------
/src/example_01/main.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include
16 |
17 | #include "src/common/callback.h"
18 | #include "src/common/expected.h"
19 | #include "src/common/log.h"
20 | #include "src/common/wgpu.h"
21 |
22 | int main() {
23 | // Get the default instance.
24 | //
25 | // Alternatively, you can use an InstanceDescriptor to request an instance
26 | // with specific features (see InstanceFeatureName), minimum limits
27 | // (InstanceLimits), or another feature based on a chained struct (see "Can be
28 | // chained in InstanceDescriptor" in webgpu_cpp.h header).
29 | auto instance = wgpu::CreateInstance();
30 | dusk::valid_or_exit(dusk::log::emit_instance_language_features(instance));
31 |
32 | // Get Adapter
33 | wgpu::RequestAdapterOptions adapter_opts{
34 | .powerPreference = wgpu::PowerPreference::HighPerformance,
35 | };
36 | wgpu::Adapter adapter{};
37 | instance.RequestAdapter(&adapter_opts, wgpu::CallbackMode::AllowSpontaneous,
38 | dusk::cb::adapter_request, &adapter);
39 |
40 | dusk::valid_or_exit(dusk::log::emit(adapter));
41 |
42 | // Get device
43 | wgpu::DeviceDescriptor device_desc{};
44 | device_desc.label = "default device";
45 | device_desc.SetDeviceLostCallback(wgpu::CallbackMode::AllowProcessEvents,
46 | dusk::cb::device_lost);
47 | device_desc.SetUncapturedErrorCallback(dusk::cb::uncaptured_error);
48 |
49 | wgpu::Device device = adapter.CreateDevice(&device_desc);
50 | dusk::valid_or_exit(dusk::log::emit(device));
51 |
52 | return 0;
53 | }
54 |
--------------------------------------------------------------------------------
/src/common/wgpu.h:
--------------------------------------------------------------------------------
1 | // Copyright 2025 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include
18 |
19 | #pragma clang diagnostic push
20 | #pragma clang diagnostic ignored "-Wdocumentation"
21 | #pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
22 | #pragma clang diagnostic ignored "-Wdouble-promotion"
23 | #pragma clang diagnostic ignored "-Wpadded"
24 | #pragma clang diagnostic ignored "-Wshadow-field-in-constructor"
25 | #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
26 | #include // IWYU pragma: export
27 | #pragma clang diagnostic pop
28 |
29 | /// Tries to run `expr`. If the status is not `Successes` then returns an
30 | /// unexpected result with the error information.
31 | #define WGPU_TRY(expr) \
32 | do { \
33 | wgpu::Status status = (expr); \
34 | if (status != wgpu::Status::Success) { \
35 | return std::unexpected(std::format("Failed: " #expr "\n")); \
36 | } \
37 | } while (false)
38 |
39 | #define WGPU_TRY_EXIT(expr) \
40 | do { \
41 | wgpu::Status status = (expr); \
42 | if (status != wgpu::Status::Success) { \
43 | std::println(stderr, "Failed: " #expr "\n"); \
44 | exit(1); \
45 | } \
46 | } while (false)
47 |
--------------------------------------------------------------------------------
/src/common/callback.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2025 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include "src/common/callback.h"
16 |
17 | #include
18 | #include
19 |
20 | #include "src/common/log.h"
21 |
22 | namespace dusk::cb {
23 |
24 | void adapter_request(wgpu::RequestAdapterStatus status,
25 | wgpu::Adapter adapter,
26 | wgpu::StringView message,
27 | wgpu::Adapter* data) {
28 | if (status != wgpu::RequestAdapterStatus::Success) {
29 | std::println(stderr, "Adapter request failed: {}",
30 | std::string_view(message));
31 | exit(1);
32 | }
33 | *data = adapter;
34 | }
35 |
36 | void device_lost([[maybe_unused]] const wgpu::Device& device,
37 | wgpu::DeviceLostReason reason,
38 | struct wgpu::StringView message) {
39 | if (message == std::string_view(
40 | "A valid external Instance reference no longer exists.")) {
41 | return;
42 | }
43 |
44 | std::print(stderr, "device lost: {}", dusk::log::to_str(reason));
45 | if (message.length > 0) {
46 | std::print(stderr, ": {}", std::string_view(message));
47 | }
48 | std::println(stderr, "");
49 | }
50 |
51 | void uncaptured_error [[noreturn]] ([[maybe_unused]] const wgpu::Device& device,
52 | wgpu::ErrorType type,
53 | struct wgpu::StringView message) {
54 | std::print(stderr, "uncaptured error: {}", dusk::log::to_str(type));
55 | if (message.length > 0) {
56 | std::print(stderr, ": {}", std::string_view(message));
57 | }
58 |
59 | std::println(stderr, "");
60 | assert(false);
61 | };
62 |
63 | void glfw_error [[noreturn]] (int code, const char* message) {
64 | std::println(stderr, "GLFW error: {}: {}", code, message);
65 | assert(false);
66 | }
67 |
68 | } // namespace dusk::cb
69 |
--------------------------------------------------------------------------------
/src/common/webgpu_helpers.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include "src/common/webgpu_helpers.h"
16 |
17 | namespace dusk::webgpu {
18 |
19 | wgpu::Buffer create_buffer(const wgpu::Device& device,
20 | std::string_view label,
21 | uint64_t size_in_bytes,
22 | wgpu::BufferUsage usage) {
23 | wgpu::BufferDescriptor desc{
24 | .label = label,
25 | .usage = usage | wgpu::BufferUsage::CopyDst,
26 | .size = size_in_bytes,
27 | };
28 | return device.CreateBuffer(&desc);
29 | }
30 |
31 | wgpu::Buffer create_buffer(const wgpu::Device& device,
32 | std::string_view label,
33 | const void* data,
34 | uint64_t size_in_bytes,
35 | wgpu::BufferUsage usage) {
36 | auto buffer = create_buffer(device, label, size_in_bytes, usage);
37 | device.GetQueue().WriteBuffer(buffer, 0, data, size_in_bytes);
38 | return buffer;
39 | }
40 |
41 | wgpu::Texture create_texture(const wgpu::Device& device,
42 | std::string_view label,
43 | wgpu::Extent3D extent,
44 | wgpu::TextureFormat format,
45 | wgpu::TextureUsage usage) {
46 | wgpu::TextureDescriptor desc{
47 | .label = label,
48 | .usage = usage,
49 | .size = extent,
50 | .format = format,
51 | };
52 |
53 | return device.CreateTexture(&desc);
54 | }
55 |
56 | wgpu::ShaderModule create_shader_module(const wgpu::Device& device,
57 | std::string_view label,
58 | std::string_view src) {
59 | wgpu::ShaderSourceWGSL wgslDesc;
60 | wgslDesc.code = src;
61 |
62 | wgpu::ShaderModuleDescriptor desc{
63 | .nextInChain = &wgslDesc,
64 | .label = label,
65 | };
66 | return device.CreateShaderModule(&desc);
67 | }
68 |
69 | } // namespace dusk::webgpu
70 |
--------------------------------------------------------------------------------
/src/common/callback.h:
--------------------------------------------------------------------------------
1 | // Copyright 2025 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include "src/common/wgpu.h"
16 |
17 | /// Callbacks which are common over all of the examples.
18 | namespace dusk::cb {
19 |
20 | /// Callback when making a WebGPU Adapter Request
21 | ///
22 | /// @param status reports if the adapter request was successful or not
23 | /// @param adapter the selected adapter
24 | /// @param message if the request failed, a message about the failure
25 | /// @param data the user data attached to the request.
26 |
27 | /// @note, the type of the `data` member matches the type of data attached when
28 | /// calling `instance.requestAdapter`. We happen to be passing a `wgpu::Adapter`
29 | /// so our type is `wgpu::Adapter*` but if you used a custom `struct MyStruct`
30 | /// for example you'd use `MyStruct*`.
31 | void adapter_request(wgpu::RequestAdapterStatus status,
32 | wgpu::Adapter adapter,
33 | wgpu::StringView message,
34 | wgpu::Adapter* data);
35 |
36 | /// Callback when the GPU is lost to WebGPU
37 | ///
38 | /// @param device the device associated to the lost GPU
39 | /// @param reason the reason the device was lost
40 | /// @param message any context information
41 | void device_lost(const wgpu::Device& device,
42 | wgpu::DeviceLostReason reason,
43 | struct wgpu::StringView message);
44 |
45 | /// Callback for any error not captured in an error scope.
46 | ///
47 | /// @param device the device where the error originated
48 | /// @param type the type of error
49 | /// @param message any context information on the error
50 | void uncaptured_error [[noreturn]] (const wgpu::Device& device,
51 | wgpu::ErrorType type,
52 | struct wgpu::StringView message);
53 |
54 | /// Callback for a GLFW error
55 | ///
56 | /// @param code the error code
57 | /// @param message any context information for the error.
58 | void glfw_error [[noreturn]] (int code, const char* message);
59 |
60 | } // namespace dusk::cb
61 |
--------------------------------------------------------------------------------
/src/common/mat4.h:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include
18 |
19 | #include "src/common/vec3.h"
20 |
21 | namespace dusk {
22 |
23 | /// Column major matrix
24 | class Mat4 {
25 | public:
26 | /// Creates a new identity matrix
27 | static Mat4 Identity();
28 |
29 | /// Creates a perspective transformation matrix.
30 | ///
31 | /// @param fov_y_radians the field of view, in radians, in the y-direction
32 | /// @param aspect the aspect ratio of the screen
33 | /// @param near the distance to the near plane
34 | /// @param far the distance to the far plane
35 | /// @returns the perspective matrix
36 | static Mat4 Perspective(float fov_y_radians,
37 | float aspect,
38 | float near,
39 | float far);
40 |
41 | /// Creates a translation matrix
42 | ///
43 | /// @param v the translation information for the x, y and z directions
44 | /// @returns the translation matrix
45 | static Mat4 Translation(const Vec3& v);
46 |
47 | /// Creates a rotation matrix
48 | ///
49 | /// @param radians the number of radians to rotate
50 | /// @param angle the direction of rotation
51 | /// @returns the rotation matrix
52 | static Mat4 Rotation(float radians, const Vec3& angle);
53 |
54 | Mat4() = default;
55 | // clang-format off
56 | Mat4(float a, float b, float c, float d, // first-column
57 | float e, float f, float g, float h, // second-column
58 | float i, float j, float k, float l, // third-column
59 | float m, float n, float o, float p) // fourth-column
60 | : data_{a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p} {}
61 | // clang-format on
62 | Mat4(Mat4&&) = default;
63 | ~Mat4() = default;
64 |
65 | Mat4& operator=(Mat4&&) = default;
66 |
67 | Mat4 operator*(const Mat4& o) const;
68 |
69 | /// Retrieves the matrix value at a given point
70 | ///
71 | /// @param col the column to retrieve
72 | /// @param row the row to retrieve
73 | /// @returns the value
74 | float at(size_t col, size_t row) const { return data_[(col * 4) + row]; }
75 |
76 | /// Retrieves a pointer to the matrix data
77 | ///
78 | /// @returns the matrix data pointer
79 | float const* data() const { return data_; }
80 |
81 | private:
82 | float data_[16] = {0};
83 | };
84 |
85 | } // namespace dusk
86 |
--------------------------------------------------------------------------------
/src/common/webgpu_helpers.h:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include
16 |
17 | #include "src/common/wgpu.h"
18 |
19 | /// Helper methods for interacting with the WebGPU API.
20 | namespace dusk::webgpu {
21 |
22 | /// Creates a new GPU buffer
23 | ///
24 | /// @param device the device to associate the buffer too
25 | /// @param label the label to attach to the buffer
26 | /// @param size_in_bytes the byte size of the buffer
27 | /// @param usage the usage of the buffer
28 | /// @returns a new WebGPU buffer of the selected size
29 | wgpu::Buffer create_buffer(const wgpu::Device& device,
30 | std::string_view label,
31 | uint64_t size_in_bytes,
32 | wgpu::BufferUsage usage);
33 |
34 | /// Creates a new GPU buffer initialized with the given data
35 | ///
36 | /// @param device the device to associate the buffer too
37 | /// @param label the label to attach to the buffer
38 | /// @param data the initial data to write to the buffer
39 | /// @param size_in_bytes the byte size of the buffer
40 | /// @param usage the usage of the buffer
41 | /// @returns a new WebGPU buffer of the selected size
42 | wgpu::Buffer create_buffer(const wgpu::Device& device,
43 | std::string_view label,
44 | const void* data,
45 | uint64_t size_in_bytes,
46 | wgpu::BufferUsage usage);
47 |
48 | /// Creates a new GPU texture
49 | ///
50 | /// @param device the device to associate the buffer too
51 | /// @param label the label to attach to the texture
52 | /// @param extent the extent of the texture
53 | /// @param format the texture format
54 | /// @param usage the texture usage
55 | /// @returns a new WebGPU texture
56 | wgpu::Texture create_texture(const wgpu::Device& device,
57 | std::string_view label,
58 | wgpu::Extent3D extent,
59 | wgpu::TextureFormat format,
60 | wgpu::TextureUsage usage);
61 |
62 | /// Creates a shader module from the given shader
63 | ///
64 | /// @param device the device to create the module on
65 | /// @param label the label to attach to the shader
66 | /// @param src the shader source
67 | /// @returns a new WebGPU ShaderModule
68 | wgpu::ShaderModule create_shader_module(const wgpu::Device& device,
69 | std::string_view label,
70 | std::string_view src);
71 |
72 | } // namespace dusk::webgpu
73 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2022 The Dusk Authors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | cmake_minimum_required(VERSION 3.22)
16 |
17 | project(dusk
18 | VERSION 0.0.1
19 | LANGUAGES CXX
20 | )
21 |
22 | enable_testing()
23 |
24 | set(CMAKE_CXX_STANDARD 23)
25 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
26 | set(CMAKE_CXX_EXTENSIONS OFF)
27 | set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
28 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
29 | set(CMAKE_POSITION_INDEPENDENT_CODE ON)
30 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
31 | # std::to_chars requires macOS 13.3
32 | set(CMAKE_OSX_DEPLOYMENT_TARGET 13.3)
33 |
34 | if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
35 | message(STATUS "No build type selected, default to Debug")
36 | set(CMAKE_BUILD_TYPE "Debug")
37 | endif()
38 |
39 | message(STATUS "")
40 | message(STATUS "Build type...........: ${CMAKE_BUILD_TYPE}")
41 | message(STATUS "C++ compiler.........: ${CMAKE_CXX_COMPILER}")
42 | message(STATUS "CMake generator......: ${CMAKE_GENERATOR}")
43 | message(STATUS "CMake version........: ${CMAKE_VERSION}")
44 | message(STATUS "")
45 |
46 | if (NOT CLANG_FORMAT)
47 | find_program(CLANG_FORMAT "clang-format")
48 | endif()
49 | if (CLANG_FORMAT)
50 | message(STATUS "Clang-format.........: ${CLANG_FORMAT}")
51 | file(GLOB_RECURSE
52 | ALL_CXX_SOURCE_FILES
53 | ${PROJECT_SOURCE_DIR}/src/*.h
54 | ${PROJECT_SOURCE_DIR}/src/*.cc
55 | )
56 |
57 | add_custom_target(
58 | format
59 | COMMAND ${CLANG_FORMAT} -i ${ALL_CXX_SOURCE_FILES}
60 | )
61 | endif()
62 |
63 | message(STATUS "")
64 |
65 | function(dusk_compile_options TARGET)
66 | target_include_directories(${TARGET} PRIVATE "${PROJECT_SOURCE_DIR}")
67 |
68 | target_compile_options(${TARGET} PRIVATE
69 | -O3
70 | -fno-rtti
71 | -fno-exceptions
72 | -fvisibility=hidden
73 | -Wall
74 | -Werror
75 | -Wextra
76 | -Wpedantic
77 | -pedantic-errors
78 |
79 | -Wno-unknown-pragmas
80 | )
81 |
82 | if (("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") OR
83 | ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang"))
84 | target_compile_options(${TARGET} PRIVATE
85 | -Weverything
86 |
87 | -Wno-switch-default
88 | -Wno-switch-enum
89 | -Wno-c++98-compat
90 | -Wno-c++98-compat-pedantic
91 | -Wno-documentation
92 | -Wno-documentation-unknown-command
93 | -Wno-poison-system-directories
94 | -Wno-unsafe-buffer-usage
95 | )
96 | elseif(CMAKE_COMPILER_IS_GNUCXX)
97 | target_compile_options(${TARGET} PRIVATE
98 | -Wno-missing-field-initializers
99 | )
100 | endif()
101 | endfunction()
102 |
103 | add_subdirectory(third_party)
104 | add_subdirectory(src)
105 |
--------------------------------------------------------------------------------
/docs/01_instance_adapter_and_device_info.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | **Adapter and Device Info**
4 | Jan 31, 2025
5 |
6 | This example shows how to create the initial `Instance`, `Adapter` and `Device`
7 | objects and then query them for any available information.
8 |
9 | * [Source](https://github.com/dj2/Dusk/tree/main/src/example_01)
10 |
11 | # Instance
12 | ~~~ cpp
13 | auto instance = wgpu::CreateInstance();
14 | ~~~
15 | [Listing [instance-creation]: Creating an `Instance`]
16 |
17 | # Adapter
18 |
19 | ## Adapter Request
20 | ~~~ cpp
21 | // Get Adapter
22 | wgpu::RequestAdapterOptions adapter_opts{
23 | .powerPreference = wgpu::PowerPreference::HighPerformance,
24 | };
25 | wgpu::Adapter adapter{};
26 | instance.RequestAdapter(&adapter_opts, wgpu::CallbackMode::AllowSpontaneous,
27 | dusk::cb::adapter_request, &adapter);
28 | ~~~
29 | [Listing [adapter-request]: Requesting an adapter]
30 |
31 | ## Adapter Request Callback
32 | ~~~ cpp
33 | void adapter_request(wgpu::RequestAdapterStatus status,
34 | wgpu::Adapter adapter,
35 | wgpu::StringView message,
36 | wgpu::Adapter* data) {
37 | if (status != wgpu::RequestAdapterStatus::Success) {
38 | std::println(stderr, "Adapter request failed: {}",
39 | std::string_view(message));
40 | exit(1);
41 | }
42 | *data = adapter;
43 | }
44 | ~~~
45 | [Listing [adapter-request-callback]: Adapter request callback]
46 |
47 | ## Logging Adapter Information
48 | ~~~ cpp
49 | dusk::log::emit(adapter);
50 | ~~~
51 | [Listing [adapter-log]: Logging adapter information]
52 |
53 | # Device
54 |
55 | ## Device Creation
56 | ~~~ cpp
57 | // Get device
58 | wgpu::DeviceDescriptor device_desc{};
59 | device_desc.label = "default device";
60 | device_desc.SetDeviceLostCallback(wgpu::CallbackMode::AllowProcessEvents,
61 | dusk::cb::device_lost);
62 | device_desc.SetUncapturedErrorCallback(dusk::cb::uncaptured_error);
63 |
64 | wgpu::Device device = adapter.CreateDevice(&device_desc);
65 | ~~~
66 | [Listing [device-creation]: Device creation]
67 |
68 | ## Device Lost Callback
69 | ~~~ cpp
70 | void device_lost([[maybe_unused]] const wgpu::Device& device,
71 | wgpu::DeviceLostReason reason,
72 | struct wgpu::StringView message) {
73 | std::print(stderr, "device lost: {}", dusk::log::to_str(reason));
74 | if (message.length > 0) {
75 | std::print(stderr, ": {}", std::string_view(message));
76 | }
77 | std::println(stderr, "");
78 | }
79 | ~~~
80 | [Listing [device-lost-callback]: Device lost callback]
81 |
82 | ## Device Uncaptured Error Callback
83 | ~~~ cpp
84 | void uncaptured_error [[noreturn]] ([[maybe_unused]] const wgpu::Device& device,
85 | wgpu::ErrorType type,
86 | struct wgpu::StringView message) {
87 | std::print(stderr, "uncaptured error: {}", dusk::log::to_str(type));
88 | if (message.length > 0) {
89 | std::print(stderr, ": {}", std::string_view(message));
90 | }
91 |
92 | std::println(stderr, "");
93 | assert(false);
94 | };
95 | ~~~
96 | [Listing [device-uncaptured-error-callback]: Device uncaptured error callback]
97 |
98 | ## Logging Device Information
99 | ~~~ cpp
100 | dusk::log::emit(device);
101 | ~~~
102 | [Listing [device-log]: Logging device information]
103 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of
9 | experience, education, socio-economic status, nationality, personal appearance,
10 | race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or reject
41 | comments, commits, code, wiki edits, issues, and other contributions that are
42 | not aligned to this Code of Conduct, or to ban temporarily or permanently any
43 | contributor for other behaviors that they deem inappropriate, threatening,
44 | offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | This Code of Conduct also applies outside the project spaces when the Project
56 | Steward has a reasonable belief that an individual's behavior may have a
57 | negative impact on the project or its community.
58 |
59 | ## Conflict Resolution
60 |
61 | We do not believe that all conflict is bad; healthy debate and disagreement
62 | often yield positive results. However, it is never okay to be disrespectful or
63 | to engage in behavior that violates the project’s code of conduct.
64 |
65 | If you see someone violating the code of conduct, you are encouraged to address
66 | the behavior directly with those involved. Many issues can be resolved quickly
67 | and easily, and this gives people more control over the outcome of their
68 | dispute. If you are unable to resolve the matter for any reason, or if the
69 | behavior is threatening or harassing, report it. We are dedicated to providing
70 | an environment where participants feel welcome and safe.
71 |
72 | Reports should be directed to dan sinclair , the
73 | Project Steward(s) for Dusk. It is the Project Steward’s duty to
74 | receive and address reported violations of the code of conduct. They will then
75 | work with a committee consisting of representatives from the Open Source
76 | Programs Office and the Google Open Source Strategy team. If for any reason you
77 | are uncomfortable reaching out the Project Steward, please email
78 | opensource@google.com.
79 |
80 | We will investigate every complaint, but you may not receive a direct response.
81 | We will use our discretion in determining when and how to follow up on reported
82 | incidents, which may range from not taking action to permanent expulsion from
83 | the project and project-sponsored spaces. We will notify the accused of the
84 | report and provide them an opportunity to discuss it before any action is taken.
85 | The identity of the reporter will be omitted from the details of the report
86 | supplied to the accused. In potentially harmful situations, such as ongoing
87 | harassment or threats to anyone's safety, we may take action without notice.
88 |
89 | ## Attribution
90 |
91 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
92 | available at
93 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
94 |
--------------------------------------------------------------------------------
/src/common/mat4.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include "src/common/mat4.h"
16 |
17 | #include
18 |
19 | namespace dusk {
20 |
21 | // static
22 | Mat4 Mat4::Identity() {
23 | // clang-format off
24 | return Mat4{
25 | 1, 0, 0, 0,
26 | 0, 1, 0, 0,
27 | 0, 0, 1, 0,
28 | 0, 0, 0, 1};
29 | // clang-format on
30 | }
31 |
32 | // static
33 | Mat4 Mat4::Perspective(float fov_y_radians,
34 | float aspect,
35 | float near,
36 | float far) {
37 | float tan_half_fov_y = 1.f / std::tan(fov_y_radians / 2.f);
38 | float dist = 1.f / (near - far);
39 |
40 | // clang-format off
41 | return Mat4{
42 | tan_half_fov_y / aspect, 0, 0, 0,
43 | 0, tan_half_fov_y, 0, 0,
44 | 0, 0, far * dist, -1,
45 | 0, 0, (far * near) * dist, 0};
46 | // clang-format on
47 | }
48 |
49 | // static
50 | Mat4 Mat4::Translation(const Vec3& v) {
51 | auto m = Mat4::Identity();
52 | m.data_[12] = v.x();
53 | m.data_[13] = v.y();
54 | m.data_[14] = v.z();
55 | return m;
56 | }
57 |
58 | // From https://ksuweb.kennesaw.edu/~plaval/math4490/rotgen.pdf
59 | // Rotation about an arbitrary angle.
60 | //
61 | // static
62 | Mat4 Mat4::Rotation(float radians, const Vec3& angle) {
63 | auto r = angle.normalize();
64 | auto x = r.x();
65 | auto y = r.y();
66 | auto z = r.z();
67 | auto C = cosf(radians);
68 | auto S = sinf(radians);
69 | auto t = 1.f - C;
70 |
71 | auto txy = t * x * y;
72 | auto txz = t * z * z;
73 | auto tyz = t * y * z;
74 | auto Sz = S * z;
75 | auto Sy = S * y;
76 | auto Sx = S * x;
77 |
78 | // clang-format off
79 | return Mat4{
80 | (t * x * x) + C, txy + Sz, txz - Sy, 0,
81 | txy - Sz, (t * y * y) + C, tyz + Sx, 0,
82 | txz + Sy, tyz - Sx, (t * z * z) + C, 0,
83 | 0, 0, 0, 1
84 | };
85 | // clang-format on
86 | }
87 |
88 | Mat4 Mat4::operator*(const Mat4& o) const {
89 | float r00 = at(0, 0) * o.at(0, 0) + at(1, 0) * o.at(0, 1) +
90 | at(2, 0) * o.at(0, 2) + at(3, 0) * o.at(0, 3);
91 | float r01 = at(0, 1) * o.at(0, 0) + at(1, 1) * o.at(0, 1) +
92 | at(2, 1) * o.at(0, 2) + at(3, 1) * o.at(0, 3);
93 | float r02 = at(0, 2) * o.at(0, 0) + at(1, 2) * o.at(0, 1) +
94 | at(2, 2) * o.at(0, 2) + at(3, 2) * o.at(0, 3);
95 | float r03 = at(0, 3) * o.at(0, 0) + at(1, 3) * o.at(0, 1) +
96 | at(2, 3) * o.at(0, 2) + at(3, 3) * o.at(0, 3);
97 |
98 | float r10 = at(0, 0) * o.at(1, 0) + at(1, 0) * o.at(1, 1) +
99 | at(2, 0) * o.at(1, 2) + at(3, 0) * o.at(1, 3);
100 | float r11 = at(0, 1) * o.at(1, 0) + at(1, 1) * o.at(1, 1) +
101 | at(2, 1) * o.at(1, 2) + at(3, 1) * o.at(1, 3);
102 | float r12 = at(0, 2) * o.at(1, 0) + at(1, 2) * o.at(1, 1) +
103 | at(2, 2) * o.at(1, 2) + at(3, 2) * o.at(1, 3);
104 | float r13 = at(0, 3) * o.at(1, 0) + at(1, 3) * o.at(1, 1) +
105 | at(2, 3) * o.at(1, 2) + at(3, 3) * o.at(1, 3);
106 |
107 | float r20 = at(0, 0) * o.at(2, 0) + at(1, 0) * o.at(2, 1) +
108 | at(2, 0) * o.at(2, 2) + at(3, 0) * o.at(2, 3);
109 | float r21 = at(0, 1) * o.at(2, 0) + at(1, 1) * o.at(2, 1) +
110 | at(2, 1) * o.at(2, 2) + at(3, 1) * o.at(2, 3);
111 | float r22 = at(0, 2) * o.at(2, 0) + at(1, 2) * o.at(2, 1) +
112 | at(2, 2) * o.at(2, 2) + at(3, 2) * o.at(2, 3);
113 | float r23 = at(0, 3) * o.at(2, 0) + at(1, 3) * o.at(2, 1) +
114 | at(2, 3) * o.at(2, 2) + at(3, 3) * o.at(2, 3);
115 |
116 | float r30 = at(0, 0) * o.at(3, 0) + at(1, 0) * o.at(3, 1) +
117 | at(2, 0) * o.at(3, 2) + at(3, 0) * o.at(3, 3);
118 | float r31 = at(0, 1) * o.at(3, 0) + at(1, 1) * o.at(3, 1) +
119 | at(2, 1) * o.at(3, 2) + at(3, 1) * o.at(3, 3);
120 | float r32 = at(0, 2) * o.at(3, 0) + at(1, 2) * o.at(3, 1) +
121 | at(2, 2) * o.at(3, 2) + at(3, 2) * o.at(3, 3);
122 | float r33 = at(0, 3) * o.at(3, 0) + at(1, 3) * o.at(3, 1) +
123 | at(2, 3) * o.at(3, 2) + at(3, 3) * o.at(3, 3);
124 |
125 | // clang-format off
126 | return Mat4{
127 | r00, r01, r02, r03,
128 | r10, r11, r12, r13,
129 | r20, r21, r22, r23,
130 | r30, r31, r32, r33};
131 | // clang-format on
132 | }
133 |
134 | } // namespace dusk
135 |
--------------------------------------------------------------------------------
/src/common/log.h:
--------------------------------------------------------------------------------
1 | // Copyright 2025 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include
18 | #include
19 |
20 | #include "src/common/wgpu.h"
21 |
22 | /// Methods used to log various bits of data from WebGPU.
23 | namespace dusk::log {
24 |
25 | /// Creates a textual version of the feature name
26 | ///
27 | /// @param f the feature name to convert
28 | /// @returns the string name
29 | std::string to_str(wgpu::FeatureName f);
30 |
31 | /// Creates a textual version of the WGSL language feature name
32 | ///
33 | /// @Parma f the language feature name to convert
34 | /// @returns the string name
35 | std::string to_str(wgpu::WGSLLanguageFeatureName f);
36 |
37 | /// Creates a textual version of the adapter type
38 | ///
39 | /// @param type the adapter type to convert
40 | /// @returns the string name
41 | std::string to_str(wgpu::AdapterType type);
42 |
43 | /// Creates a textual version of the backend type
44 | ///
45 | /// @param type the backend type to convert
46 | /// @returns the string name
47 | std::string to_str(wgpu::BackendType type);
48 |
49 | /// Creates a textual version of the device lost reason
50 | ///
51 | /// @param f the device lost reason to convert
52 | /// @returns the string name
53 | std::string to_str(wgpu::DeviceLostReason reason);
54 |
55 | /// Creates a textual version of the error type
56 | ///
57 | /// @param f the error type to convert
58 | /// @returns the string name
59 | std::string to_str(wgpu::ErrorType type);
60 |
61 | /// Creates a textual version of the heap properties
62 | ///
63 | /// @param prop the property to emit
64 | /// @returns the string name
65 | std::string to_str(wgpu::HeapProperty prop);
66 |
67 | /// Creates a textual version of the power preference
68 | ///
69 | /// @param prop the power preference to emit
70 | /// @returns the string name
71 | std::string to_str(wgpu::PowerPreference pref);
72 |
73 | /// Creates a textual version of the adapter information
74 | ///
75 | /// @param info the adapter info to convert
76 | /// @returns the string representation
77 | std::string to_str(const wgpu::AdapterInfo& info);
78 |
79 | /// Creates a textual version of the subgroup matrix component type
80 | ///
81 | /// @param type the type to convert
82 | /// @returns the string representation
83 | std::string to_str(wgpu::SubgroupMatrixComponentType type);
84 |
85 | /// Creates a textual representation of the D3D adapter properties
86 | ///
87 | /// @param props the properties
88 | /// @returns the string representation
89 | std::string to_str(wgpu::AdapterPropertiesD3D* props);
90 |
91 | /// Creates a textual representation of the VK adapter properties
92 | ///
93 | /// @param props the properties
94 | /// @returns the string representation
95 | std::string to_str(wgpu::AdapterPropertiesVk* props);
96 |
97 | /// Creates a textual representation of the subgroup matrix configuration
98 | /// adapter properties
99 | ///
100 | /// @param props the properties
101 | /// @returns the string representation
102 | std::string to_str(wgpu::AdapterPropertiesSubgroupMatrixConfigs* props);
103 |
104 | /// Creates a textual representation of the memmory heaps adapter properties
105 | ///
106 | /// @param props the properties
107 | /// @returns the string representation
108 | std::string to_str(wgpu::AdapterPropertiesMemoryHeaps* props);
109 |
110 | /// Creates a textual representation of the dawn power preference adapter
111 | /// properties
112 | ///
113 | /// @param props the props
114 | /// @returns the string representation
115 | std::string to_str(wgpu::DawnAdapterPropertiesPowerPreference* props);
116 |
117 | /// Creates a textual version of the limits
118 | ///
119 | /// @param limits the limits to convert
120 | /// @param indent the amount to indent each limit string
121 | /// @returns the string representation
122 | std::string limits(const wgpu::Limits& limits, std::string_view indent);
123 |
124 | /// Emits the language features known to the instance
125 | ///
126 | /// @param instance the instance to retrieve the features from
127 | std::expected emit_instance_language_features(
128 | wgpu::Instance& instance);
129 |
130 | /// Emits the adapter info to `stderr`
131 | ///
132 | /// @param adapter the adapter to emit from
133 | std::expected emit_adapter_info(wgpu::Adapter& adapter);
134 |
135 | /// Emits the adapter features to `stderr`
136 | ///
137 | /// @param adapter the adapter to emit from
138 | void emit_adapter_features(wgpu::Adapter& adapter);
139 |
140 | /// Emits the adapter limits to `stderr`
141 | ///
142 | /// @param adapter the adapter to emit from
143 | std::expected emit_adapter_limits(wgpu::Adapter& adapter);
144 |
145 | /// Emits the adapter to `stderr`
146 | ///
147 | /// @param adapter the adapter to emit
148 | std::expected emit(wgpu::Adapter& adapter);
149 |
150 | /// Emits the device features to `stderr`
151 | ///
152 | /// @param device the device to emit from
153 | void emit_device_features(wgpu::Device& device);
154 |
155 | /// Emits the device limits to `stderr`
156 | ///
157 | /// @param device the device to emit from
158 | std::expected emit_device_limits(wgpu::Device& device);
159 |
160 | /// Emits the device to `stderr`
161 | ///
162 | /// @param device the device to emit
163 | std::expected emit(wgpu::Device& device);
164 |
165 | } // namespace dusk::log
166 |
--------------------------------------------------------------------------------
/src/example_02/main.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include
16 | #include
17 |
18 | #include "src/common/callback.h"
19 | #include "src/common/glfw.h"
20 | #include "src/common/webgpu_helpers.h"
21 | #include "src/common/wgpu.h"
22 |
23 | namespace {
24 |
25 | constexpr uint32_t kWidth = 1024;
26 | constexpr uint32_t kHeight = 768;
27 |
28 | constexpr std::array vertex_data{
29 | 0.f, .5f, 0.f, 1.f, 1.f, 0.f, 0.f, 1.f, -.5f, -.5f, 0.f, 1.f,
30 | 0.f, 1.f, 0.f, 1.f, .5f, -.5f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f,
31 | };
32 |
33 | constexpr const char* kShader = R"(
34 | struct VertexInput {
35 | @location(0) pos: vec4f,
36 | @location(1) colour: vec4f,
37 | }
38 |
39 | struct VertexOutput {
40 | @builtin(position) pos: vec4f,
41 | @location(0) colour: vec4f,
42 | }
43 |
44 | @vertex
45 | fn vs_main(in: VertexInput) -> VertexOutput {
46 | return VertexOutput(in.pos, in.colour);
47 | }
48 |
49 | @fragment
50 | fn fs_main(in: VertexOutput) -> @location(0) vec4f {
51 | return in.colour;
52 | }
53 | )";
54 |
55 | } // namespace
56 |
57 | int main() {
58 | glfwSetErrorCallback(dusk::cb::glfw_error);
59 |
60 | if (!glfwInit()) {
61 | std::println(stderr, "Failed to initialize GLFW.");
62 | return 1;
63 | }
64 |
65 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
66 | glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
67 |
68 | auto window = glfwCreateWindow(kWidth, kHeight, "dusk", nullptr, nullptr);
69 | if (!window) {
70 | std::println(stderr, "Unable to create GLFW window");
71 | return 1;
72 | }
73 |
74 | auto instance = wgpu::CreateInstance();
75 |
76 | // Get surface
77 | auto surface = wgpu::glfw::CreateSurfaceForWindow(instance, window);
78 |
79 | // Get Adapter
80 |
81 | wgpu::Adapter adapter{};
82 | {
83 | wgpu::RequestAdapterOptions adapter_opts{
84 | .powerPreference = wgpu::PowerPreference::HighPerformance,
85 | .compatibleSurface = surface,
86 | };
87 | instance.RequestAdapter(&adapter_opts, wgpu::CallbackMode::AllowSpontaneous,
88 | dusk::cb::adapter_request, &adapter);
89 | }
90 |
91 | // Get device
92 | wgpu::DeviceDescriptor deviceDesc{};
93 | deviceDesc.label = "Primary Device";
94 | deviceDesc.SetDeviceLostCallback(wgpu::CallbackMode::AllowProcessEvents,
95 | dusk::cb::device_lost);
96 | deviceDesc.SetUncapturedErrorCallback(dusk::cb::uncaptured_error);
97 | auto device = adapter.CreateDevice(&deviceDesc);
98 |
99 | // Set up surface for drawing and presenting
100 | wgpu::SurfaceCapabilities capabilities;
101 | surface.GetCapabilities(adapter, &capabilities);
102 | auto surfaceFormat = capabilities.formats[0];
103 | wgpu::SurfaceConfiguration config = {
104 | .device = device,
105 | .format = surfaceFormat,
106 | .width = kWidth,
107 | .height = kHeight,
108 | .presentMode = wgpu::PresentMode::Fifo,
109 | };
110 | surface.Configure(&config);
111 |
112 | // Create buffers
113 | auto vertexBuffer = dusk::webgpu::create_buffer(
114 | device, "Vertex Buffer", vertex_data.data(),
115 | vertex_data.size() * sizeof(float), wgpu::BufferUsage::Vertex);
116 |
117 | // Shaders
118 | auto shader =
119 | dusk::webgpu::create_shader_module(device, "Main Shader Module", kShader);
120 |
121 | // Pipeline creation
122 | std::array vertAttributes{
123 | wgpu::VertexAttribute{
124 | .format = wgpu::VertexFormat::Float32x4,
125 | .offset = 0,
126 | .shaderLocation = 0,
127 | },
128 | wgpu::VertexAttribute{
129 | .format = wgpu::VertexFormat::Float32x4,
130 | .offset = 4 * sizeof(float),
131 | .shaderLocation = 1,
132 | }};
133 |
134 | wgpu::VertexBufferLayout vertBufferLayout{
135 | .stepMode = wgpu::VertexStepMode::Vertex,
136 | .arrayStride = 8 * sizeof(float),
137 | .attributeCount = vertAttributes.size(),
138 | .attributes = vertAttributes.data(),
139 | };
140 |
141 | wgpu::ColorTargetState target{
142 | .format = surfaceFormat,
143 | };
144 |
145 | wgpu::FragmentState fragState{
146 | .module = shader,
147 | .entryPoint = "fs_main",
148 | .constants = nullptr,
149 | .targetCount = 1,
150 | .targets = &target,
151 | };
152 |
153 | wgpu::RenderPipelineDescriptor pipelineDesc{
154 | .label = "Main Render Pipeline",
155 | .layout = nullptr, // Automatic layout
156 | .vertex =
157 | {
158 | .module = shader,
159 | .entryPoint = "vs_main",
160 | .constants = nullptr,
161 | .bufferCount = 1,
162 | .buffers = &vertBufferLayout,
163 | },
164 | .fragment = &fragState,
165 | };
166 | auto pipeline = device.CreateRenderPipeline(&pipelineDesc);
167 |
168 | while (!glfwWindowShouldClose(window)) {
169 | glfwPollEvents();
170 | device.Tick();
171 |
172 | auto encoder = device.CreateCommandEncoder();
173 | encoder.SetLabel("Main command encoder");
174 |
175 | {
176 | wgpu::SurfaceTexture surfaceTexture;
177 | surface.GetCurrentTexture(&surfaceTexture);
178 |
179 | auto backbufferView = surfaceTexture.texture.CreateView();
180 | backbufferView.SetLabel("Back Buffer Texture View");
181 |
182 | wgpu::RenderPassColorAttachment attachment{
183 | .view = backbufferView,
184 | .loadOp = wgpu::LoadOp::Clear,
185 | .storeOp = wgpu::StoreOp::Store,
186 | .clearValue = {0., 0., 0., 1.},
187 | };
188 |
189 | wgpu::RenderPassDescriptor renderPass{
190 | .label = "Main Render Pass",
191 | .colorAttachmentCount = 1,
192 | .colorAttachments = &attachment,
193 | };
194 |
195 | auto pass = encoder.BeginRenderPass(&renderPass);
196 | pass.SetPipeline(pipeline);
197 | pass.SetVertexBuffer(0, vertexBuffer);
198 | pass.Draw(3);
199 | pass.End();
200 | }
201 | auto commands = encoder.Finish();
202 |
203 | device.GetQueue().Submit(1, &commands);
204 | surface.Present();
205 | }
206 |
207 | vertexBuffer.Destroy();
208 | surface.Unconfigure();
209 | surface = nullptr;
210 | device.Destroy();
211 |
212 | glfwDestroyWindow(window);
213 | glfwTerminate();
214 | return 0;
215 | }
216 |
--------------------------------------------------------------------------------
/src/example_03/main.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include
16 | #include
17 |
18 | #include "src/common/callback.h"
19 | #include "src/common/glfw.h"
20 | #include "src/common/webgpu_helpers.h"
21 | #include "src/common/wgpu.h"
22 |
23 | namespace {
24 |
25 | constexpr uint32_t kWidth = 1024;
26 | constexpr uint32_t kHeight = 768;
27 |
28 | constexpr std::array index_data{0, 1, 2};
29 | constexpr std::array vertex_data{
30 | 0.f, .5f, 0.f, 1.f, 1.f, 0.f, 0.f, 1.f, -.5f, -.5f, 0.f, 1.f,
31 | 0.f, 1.f, 0.f, 1.f, .5f, -.5f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f,
32 | };
33 |
34 | constexpr const char* kShader = R"(
35 | struct VertexInput {
36 | @location(0) pos: vec4f,
37 | @location(1) colour: vec4f,
38 | }
39 |
40 | struct VertexOutput {
41 | @builtin(position) pos: vec4f,
42 | @location(0) colour: vec4f,
43 | }
44 |
45 | @vertex
46 | fn vs_main(in: VertexInput) -> VertexOutput {
47 | return VertexOutput(in.pos, in.colour);
48 | }
49 |
50 | @fragment
51 | fn fs_main(in: VertexOutput) -> @location(0) vec4f {
52 | return in.colour;
53 | }
54 | )";
55 |
56 | } // namespace
57 |
58 | int main() {
59 | glfwSetErrorCallback(dusk::cb::glfw_error);
60 |
61 | if (!glfwInit()) {
62 | std::println(stderr, "Failed to initialize GLFW.");
63 | return 1;
64 | }
65 |
66 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
67 | glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
68 |
69 | auto window = glfwCreateWindow(kWidth, kHeight, "dusk", nullptr, nullptr);
70 | if (!window) {
71 | std::println(stderr, "Failed to create GLFW window");
72 | return 1;
73 | }
74 |
75 | auto instance = wgpu::CreateInstance();
76 |
77 | // Get surface
78 | auto surface = wgpu::glfw::CreateSurfaceForWindow(instance, window);
79 |
80 | // Get Adapter
81 | wgpu::Adapter adapter{};
82 | {
83 | wgpu::RequestAdapterOptions adapter_opts{
84 | .powerPreference = wgpu::PowerPreference::HighPerformance,
85 | .compatibleSurface = surface,
86 | };
87 |
88 | instance.RequestAdapter(&adapter_opts, wgpu::CallbackMode::AllowSpontaneous,
89 | dusk::cb::adapter_request, &adapter);
90 | }
91 |
92 | // Get device
93 | wgpu::DeviceDescriptor deviceDesc{};
94 | deviceDesc.label = "Primary Device";
95 | deviceDesc.SetDeviceLostCallback(wgpu::CallbackMode::AllowProcessEvents,
96 | dusk::cb::device_lost);
97 | deviceDesc.SetUncapturedErrorCallback(dusk::cb::uncaptured_error);
98 | auto device = adapter.CreateDevice(&deviceDesc);
99 |
100 | // Set up surface for drawing and presenting
101 | wgpu::SurfaceCapabilities capabilities;
102 | surface.GetCapabilities(adapter, &capabilities);
103 | auto surfaceFormat = capabilities.formats[0];
104 | wgpu::SurfaceConfiguration config = {
105 | .device = device,
106 | .format = surfaceFormat,
107 | .width = kWidth,
108 | .height = kHeight,
109 | .presentMode = wgpu::PresentMode::Fifo,
110 | };
111 | surface.Configure(&config);
112 |
113 | // Create buffers
114 | auto indexBuffer = dusk::webgpu::create_buffer(
115 | device, "Index Buffer", index_data.data(),
116 | index_data.size() * sizeof(uint32_t), wgpu::BufferUsage::Index);
117 | auto vertexBuffer = dusk::webgpu::create_buffer(
118 | device, "Vertex Buffer", vertex_data.data(),
119 | vertex_data.size() * sizeof(float), wgpu::BufferUsage::Vertex);
120 |
121 | // Shaders
122 | auto shader =
123 | dusk::webgpu::create_shader_module(device, "Main Shader Module", kShader);
124 |
125 | // Pipeline creation
126 | std::array vertAttributes{
127 | wgpu::VertexAttribute{
128 | .format = wgpu::VertexFormat::Float32x4,
129 | .offset = 0,
130 | .shaderLocation = 0,
131 | },
132 | wgpu::VertexAttribute{
133 | .format = wgpu::VertexFormat::Float32x4,
134 | .offset = 4 * sizeof(float),
135 | .shaderLocation = 1,
136 | }};
137 |
138 | wgpu::VertexBufferLayout vertBufferLayout{
139 | .stepMode = wgpu::VertexStepMode::Vertex,
140 | .arrayStride = 8 * sizeof(float),
141 | .attributeCount = vertAttributes.size(),
142 | .attributes = vertAttributes.data(),
143 | };
144 |
145 | wgpu::ColorTargetState target{
146 | .format = surfaceFormat,
147 | };
148 |
149 | wgpu::FragmentState fragState{
150 | .module = shader,
151 | .entryPoint = "fs_main",
152 | .constants = nullptr,
153 | .targetCount = 1,
154 | .targets = &target,
155 | };
156 |
157 | wgpu::RenderPipelineDescriptor pipelineDesc{
158 | .label = "Main Render Pipeline",
159 | .layout = nullptr, // Automatic layout
160 | .vertex =
161 | {
162 | .module = shader,
163 | .entryPoint = "vs_main",
164 | .constants = nullptr,
165 | .bufferCount = 1,
166 | .buffers = &vertBufferLayout,
167 | },
168 | .fragment = &fragState,
169 | };
170 |
171 | auto pipeline = device.CreateRenderPipeline(&pipelineDesc);
172 |
173 | while (!glfwWindowShouldClose(window)) {
174 | glfwPollEvents();
175 | device.Tick();
176 |
177 | auto encoder = device.CreateCommandEncoder();
178 | encoder.SetLabel("Main Command Encoder");
179 |
180 | {
181 | wgpu::SurfaceTexture surfaceTexture;
182 | surface.GetCurrentTexture(&surfaceTexture);
183 |
184 | auto backbufferView = surfaceTexture.texture.CreateView();
185 | backbufferView.SetLabel("Back Buffer Texture View");
186 |
187 | wgpu::RenderPassColorAttachment attachment{
188 | .view = backbufferView,
189 | .loadOp = wgpu::LoadOp::Clear,
190 | .storeOp = wgpu::StoreOp::Store,
191 | .clearValue = {0., 0., 0., 1.},
192 | };
193 |
194 | wgpu::RenderPassDescriptor renderPass{
195 | .label = "Main Render Pass",
196 | .colorAttachmentCount = 1,
197 | .colorAttachments = &attachment,
198 | };
199 |
200 | auto pass = encoder.BeginRenderPass(&renderPass);
201 | pass.SetPipeline(pipeline);
202 | pass.SetVertexBuffer(0, vertexBuffer);
203 | pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32);
204 | pass.DrawIndexed(3);
205 | pass.End();
206 | }
207 | auto commands = encoder.Finish();
208 |
209 | device.GetQueue().Submit(1, &commands);
210 | surface.Present();
211 | }
212 |
213 | vertexBuffer.Destroy();
214 | surface.Unconfigure();
215 | surface = nullptr;
216 | device.Destroy();
217 |
218 | glfwDestroyWindow(window);
219 | glfwTerminate();
220 | return 0;
221 | }
222 |
--------------------------------------------------------------------------------
/docs/00_setup.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | **Setup and Configuration**
4 | Jan 28, 2025
5 |
6 | This article provides some information on setting up Dawn for use with CMake.
7 | The requirements are:
8 |
9 |
10 | * [c++23](https://en.cppreference.com/w/cpp/23)
11 | * [CMake](https://cmake.org/)
12 | * [Clang](https://clang.llvm.org/)
13 | * [Git](https://git-scm.com/)
14 | * [Ninja](https://ninja-build.org/)
15 | * [GLFW](https://www.glfw.org/)
16 |
17 | All code licensed under the [Apache License, v2.0](https://www.apache.org/licenses/LICENSE-2.0).
18 |
19 | # Setup
20 |
21 | We'll use a folder setup of:
22 |
23 | *******************************************************************************
24 | * dusk
25 | * |
26 | * +-- out
27 | * +-- src
28 | * | |
29 | * | +-- example_yy
30 | * | | |
31 | * | | +-- CMakeLists.txt
32 | * | +-- CMakeLists.txt
33 | * |
34 | * +-- third_party
35 | * | |
36 | * | +-- CMakeLists.txt
37 | * |
38 | * +-- CMakeLists.txt
39 | *******************************************************************************
40 |
41 | All of our dependencies, and the dependencies needed by `Dawn`, will live in the
42 | `third_party` folder. Our source will live in `src` and `out` will be used for
43 | and build artifacts.
44 |
45 | We will be using `submodules` to manage Dawn and needed dependencies. This means
46 | you'll need to run `git init` in the `dawn` folder before continuing to setup
47 | `git`.
48 |
49 | ## Submodules
50 |
51 | ~~~ shell
52 | git submodule init
53 | git submodule add https://dawn.googlesource.com/dawn third_party/dawn
54 | git submodule add https://github.com/abseil/abseil-cpp third_party/abseil-cpp
55 | git submodule add https://github.com/KhronosGroup/Spirv-Tools third_party/spirv-tools
56 | git submodule add https://github.com/KhronosGroup/Spirv-Headers third_party/spirv-headers
57 | git submodule add https://github.com/pallets/jinja third_party/jinja
58 | git submodule add https://github.com/mitsuhiko/markupsafe third_party/markupsafe
59 | git submodule add https://github.com/KhronosGroup/Vulkan-Tools third_party/vulkan-tools
60 | git submodule add https://github.com/KhronosGroup/Vulkan-Headers third_party/vulkan-headers
61 | git submodule add https://github.com/KhronosGroup/Vulkan-Utility-Libraries third_party/vulkan-utility-libraries
62 | git submodule add https://github.com/glfw/glfw third_party/glfw
63 | ~~~
64 | [Listing [git-submodules]: Git submodule list]
65 |
66 | ## CMake
67 |
68 | There are a few different `CMakeLists.txt` files in the repository. The two
69 | primary ones are the top level `CMakeLists.txt` and
70 | `third_party/CMakeLists.txt`. These control the initial project setup and the
71 | Dawn configuration options.
72 |
73 | ### `CMakeLists.txt`
74 |
75 | Setting up CMake follows a simple pattern of configuring some CMake options,
76 | setting up `clang-format` if available, creating a function to set build options
77 | for each `TARGET` and then adding the sub-directories.
78 |
79 | For the build options, we enable c++ 23 mode, turn of c++ modules scanning as
80 | we won't be using them. We turn on the `CMAKE_EXPORT_COMPILE_COMMANDS` entry
81 | so that `compile_commands.json` will be created and usable by various tools.
82 | Finally, we log the build information to make it obvious what was found by
83 | CMake and where.
84 |
85 | ~~~ Makefile
86 | cmake_minimum_required(VERSION 3.12)
87 |
88 | project(dusk
89 | VERSION 0.0.1
90 | LANGUAGES CXX
91 | )
92 |
93 | enable_testing()
94 |
95 | set(CMAKE_CXX_STANDARD 23)
96 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
97 | set(CMAKE_CXX_EXTENSIONS OFF)
98 | set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
99 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
100 | set(CMAKE_POSITION_INDEPENDENT_CODE ON)
101 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
102 |
103 | if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
104 | message(STATUS "No build type selected, default to Debug")
105 | set(CMAKE_BUILD_TYPE "Debug")
106 | endif()
107 |
108 | message(STATUS "")
109 | message(STATUS "Build type...........: ${CMAKE_BUILD_TYPE}")
110 | message(STATUS "CMake generator......: ${CMAKE_GENERATOR} ${CMAKE_VERSION}")
111 | message(STATUS "CXX compiler.........: ${CMAKE_CXX_COMPILER}")
112 | message(STATUS "")
113 | ~~~
114 | [Listing [cmake-config]: CMake configuration]
115 |
116 | `clang-format` we attempt to find, either already part of the build, or
117 | using the CMake `find_program` option. If found, we configure it out run over
118 | all of our `src` files of type `.cc` and `.h`. A `format` target is also added
119 | so we can do `ninja format` to run the formatter over the source.
120 |
121 | ~~~ Makefile
122 | if (NOT CLANG_FORMAT)
123 | find_program(CLANG_FORMAT "clang-format")
124 | endif()
125 | if (CLANG_FORMAT)
126 | message(STATUS "Clang-format.........: ${CLANG_FORMAT}")
127 | file(GLOB_RECURSE
128 | ALL_CXX_SOURCE_FILES
129 | ${PROJECT_SOURCE_DIR}/src/*.h
130 | ${PROJECT_SOURCE_DIR}/src/*.cc
131 | )
132 |
133 | add_custom_target(
134 | format
135 | COMMAND ${CLANG_FORMAT} -i ${ALL_CXX_SOURCE_FILES}
136 | )
137 | endif()
138 |
139 | message(STATUS "")
140 | ~~~
141 | [Listing [cmake-format]: clang-format setup]
142 |
143 | Each of our `TARGET` executables will use the `dusk_compile_options` to setup
144 | the basic build configuration. This does turn on `Werror` and `Weverything`
145 | which people may find too restrictive. My preference is to find the warnings
146 | as soon as possible and add suppressions or fixes for them.
147 |
148 | ~~~ Makefile
149 | function(dusk_compile_options TARGET)
150 | target_include_directories(${TARGET} PRIVATE "${PROJECT_SOURCE_DIR}")
151 |
152 | target_compile_options(${TARGET} PRIVATE
153 | -O3
154 | -fno-rtti
155 | -fno-exceptions
156 | -fvisibility=hidden
157 | -Wall
158 | -Werror
159 | -Wextra
160 | -Wpedantic
161 | -pedantic-errors
162 |
163 | -Wno-unknown-pragmas
164 | )
165 |
166 | if (("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") OR
167 | ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang"))
168 | target_compile_options(${TARGET} PRIVATE
169 | -Weverything
170 |
171 | -Wno-c++98-compat
172 | -Wno-c++98-compat-pedantic
173 | -Wno-documentation
174 | -Wno-documentation-unknown-command
175 | -Wno-poison-system-directories
176 | )
177 | elseif(CMAKE_COMPILER_IS_GNUCXX)
178 | target_compile_options(${TARGET} PRIVATE
179 | -Wno-missing-field-initializers
180 | )
181 | endif()
182 | endfunction()
183 | ~~~
184 | [Listing [compile-opts]: Compile options function]
185 |
186 | Finally, the `third_party` and `src` folders are added to the build.
187 |
188 | ~~~ Makefile
189 | add_subdirectory(third_party)
190 | add_subdirectory(src)
191 | ~~~
192 | [Listing [cmake-subdirs]: Add sub-directories to CMake]
193 |
194 | ### `third_party/CMakeLists.txt`
195 |
196 | The `third_party/CMakeLists.txt` is concerned with configuring the Dawn build.
197 | All of the dependencies we added will be add to the build by Dawn, we just have
198 | to tell Dawn where it's dependencies live.
199 |
200 | Each of the dependencies has a `_DIR` entry tell Dawn how to find the
201 | dependency. There is also a `DAWN_THIRD_PARTY_DIR` but the subfolders are
202 | expected to have a specific layout which we aren't adhering too. So, while we
203 | set `DAWN_THIRD_PARTY_DIR` we really only use it for our configuration of the
204 | dependencies.
205 |
206 | ~~~ Makefile
207 | set(DAWN_THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/)
208 | set(DAWN_SPIRV_HEADERS_DIR ${DAWN_THIRD_PARTY_DIR}/spirv-headers)
209 | set(DAWN_SPIRV_TOOLS_DIR ${DAWN_THIRD_PARTY_DIR}/spirv-tools)
210 | set(DAWN_VULKAN_HEADERS_DIR ${DAWN_THIRD_PARTY_DIR}/vulkan-headers)
211 | set(DAWN_VULKAN_UTILITY_LIBRARIES_DIR ${DAWN_THIRD_PARTY_DIR}/vulkan-utility-libraries)
212 | set(DAWN_JINJA2_DIR ${DAWN_THIRD_PARTY_DIR}/jinja/src/jinja2)
213 | set(DAWN_MARKUPSAFE_DIR ${DAWN_THIRD_PARTY_DIR}/markupsafe/src/markupsafe)
214 | ~~~
215 | [Listing [dawn-config]: Configuring Dawn dependencies]
216 |
217 | Next we configure the Dawn build as desired. There are a few backend options
218 | I'm not planning on using, so we turn those off (mostly Compat and testing
219 | backends). We can then turn off various samples, tests and validation which we
220 | won't be using. You can leave these on, it's just more things to build.
221 |
222 | ~~~ Makefile
223 | set(DAWN_BUILD_GEN_DIR ${CMAKE_CURRENT_BINARY_DIR}/gen)
224 | set(DAWN_ENABLE_DESKTOP_GL OFF)
225 | set(DAWN_ENABLE_OPENGLES OFF)
226 | set(DAWN_ENABLE_NULL OFF)
227 | set(DAWN_BUILD_SAMPLES OFF)
228 | set(TINT_BUILD_DOCS OFF)
229 | set(TINT_BUILD_TESTS OFF)
230 | set(TINT_BUILD_SAMPLES OFF)
231 | set(TINT_BUILD_GLSL_VALIDATOR OFF)
232 | set(TINT_BUILD_GLSL_WRITER OFF)
233 | set(TINT_BUILD_SPV_READER OFF)
234 |
235 | if(NOT APPLE)
236 | set(BUILD_SHARED_LIBS ON)
237 | endif()
238 |
239 | add_definitions(
240 | -Wno-deprecated-builtins # From Abseil
241 | -Wno-nullability-extension # From abseil
242 | -Wno-unknown-warning-option # SPIRV-Tools
243 | )
244 | ~~~
245 | [Listing [dawn-build-flags]: Setting Dawn build flags]
246 |
247 | Finally, add the Dawn folder to the CMake build.
248 |
249 | ~~~ Makefile
250 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/dawn EXCLUDE_FROM_ALL)
251 | ~~~
252 | [Listing [dawn-dir]: Add Dawn folder]
253 |
254 | ### `src/CMakeLists.txt`
255 |
256 | The `src` CMake file just adds the sub-directories for each of the examples.
257 | Essentially just a proxy file.
258 |
259 | ### `example/CMakeList.txt`
260 |
261 | Each example has a build file similar to:
262 |
263 | ~~~ Makefile
264 | add_executable(dusk_example_01)
265 | dusk_compile_options(dusk_example_01)
266 | target_sources(dusk_example_01
267 | dump_utils.cc
268 | dump_utils.h
269 | main.cc
270 | wgpu.h
271 | )
272 |
273 | target_link_libraries(dusk_example_01
274 | webgpu_dawn
275 | webgpu_cpp
276 | )
277 | ~~~
278 |
279 | This sets the source files, creates the executable with `add_executable` sets
280 | our compile options and then links against the needed targets. Typically they'll
281 | link against `webgpu_dawn` and `webgpu_cpp`. If you're using `glfw` we'll also
282 | end up adding a `glfw` and `webgpu_glfw` target to link against as well.
283 |
284 |
285 |
286 |
--------------------------------------------------------------------------------
/src/example_04/main.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | #include "src/common/callback.h"
21 | #include "src/common/glfw.h"
22 | #include "src/common/mat4.h"
23 | #include "src/common/webgpu_helpers.h"
24 | #include "src/common/wgpu.h"
25 |
26 | namespace {
27 |
28 | constexpr uint32_t kWidth = 1024;
29 | constexpr uint32_t kHeight = 768;
30 |
31 | // clang-format off
32 | constexpr const std::array cube_data{
33 | // vec4 position, vec2 uv,
34 | 1, -1, 1, 1, 1, 1,
35 | -1, -1, 1, 1, 0, 1,
36 | -1, -1, -1, 1, 0, 0,
37 | 1, -1, -1, 1, 1, 0,
38 | 1, -1, 1, 1, 1, 1,
39 | -1, -1, -1, 1, 0, 0,
40 |
41 | 1, 1, 1, 1, 1, 1,
42 | 1, -1, 1, 1, 0, 1,
43 | 1, -1, -1, 1, 0, 0,
44 | 1, 1, -1, 1, 1, 0,
45 | 1, 1, 1, 1, 1, 1,
46 | 1, -1, -1, 1, 0, 0,
47 |
48 | -1, 1, 1, 1, 1, 1,
49 | 1, 1, 1, 1, 0, 1,
50 | 1, 1, -1, 1, 0, 0,
51 | -1, 1, -1, 1, 1, 0,
52 | -1, 1, 1, 1, 1, 1,
53 | 1, 1, -1, 1, 0, 0,
54 |
55 | -1, -1, 1, 1, 1, 1,
56 | -1, 1, 1, 1, 0, 1,
57 | -1, 1, -1, 1, 0, 0,
58 | -1, -1, -1, 1, 1, 0,
59 | -1, -1, 1, 1, 1, 1,
60 | -1, 1, -1, 1, 0, 0,
61 |
62 | 1, 1, 1, 1, 1, 1,
63 | -1, 1, 1, 1, 0, 1,
64 | -1, -1, 1, 1, 0, 0,
65 | -1, -1, 1, 1, 0, 0,
66 | 1, -1, 1, 1, 1, 0,
67 | 1, 1, 1, 1, 1, 1,
68 |
69 | 1, -1, -1, 1, 1, 1,
70 | -1, -1, -1, 1, 0, 1,
71 | -1, 1, -1, 1, 0, 0,
72 | 1, 1, -1, 1, 1, 0,
73 | 1, -1, -1, 1, 1, 1,
74 | -1, 1, -1, 1, 0, 0,
75 | };
76 | // clang-format on
77 |
78 | constexpr uint32_t kVertexCount = 36;
79 | constexpr uint32_t kPositionByteOffset = 0;
80 | constexpr uint32_t kUVByteOffset = 4 * sizeof(float);
81 | constexpr uint32_t kCubeDataStride = 6;
82 |
83 | constexpr const char* kShader = R"(
84 | struct Uniforms {
85 | mvp : mat4x4f,
86 | }
87 | @binding(0) @group(0) var uniforms : Uniforms;
88 |
89 | struct VertexInput {
90 | @location(0) pos: vec4f,
91 | @location(1) uv: vec2f,
92 | }
93 |
94 | struct VertexOutput {
95 | @builtin(position) vertex_pos: vec4f,
96 | @location(0) uv: vec2f,
97 | @location(1) frag_colour: vec4f,
98 | }
99 |
100 | @vertex
101 | fn vs_main(in: VertexInput) -> VertexOutput {
102 | let vert_pos = uniforms.mvp * in.pos;
103 | let frag_colour = 0.5 * (in.pos + vec4(1));
104 | return VertexOutput(vert_pos, in.uv, frag_colour);
105 | }
106 |
107 | @fragment
108 | fn fs_main(in: VertexOutput) -> @location(0) vec4f {
109 | return in.frag_colour;
110 | }
111 | )";
112 |
113 | } // namespace
114 |
115 | int main() {
116 | glfwSetErrorCallback(dusk::cb::glfw_error);
117 |
118 | if (!glfwInit()) {
119 | std::println(stderr, "Failed to initialize GLFW.");
120 | return 1;
121 | }
122 |
123 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
124 | glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
125 |
126 | auto window = glfwCreateWindow(kWidth, kHeight, "dusk", nullptr, nullptr);
127 | if (!window) {
128 | std::println(stderr, "Unable to create GLFW window");
129 | return 1;
130 | }
131 |
132 | auto instance = wgpu::CreateInstance();
133 |
134 | // Get surface
135 | auto surface = wgpu::glfw::CreateSurfaceForWindow(instance, window);
136 |
137 | // Get Adapter
138 | wgpu::Adapter adapter{};
139 | {
140 | wgpu::RequestAdapterOptions adapter_opts{
141 | .powerPreference = wgpu::PowerPreference::HighPerformance,
142 | .compatibleSurface = surface,
143 | };
144 | instance.RequestAdapter(&adapter_opts, wgpu::CallbackMode::AllowSpontaneous,
145 | dusk::cb::adapter_request, &adapter);
146 | }
147 |
148 | // Get device
149 | wgpu::DeviceDescriptor deviceDesc{};
150 | deviceDesc.label = "Primary Device";
151 | deviceDesc.SetDeviceLostCallback(wgpu::CallbackMode::AllowProcessEvents,
152 | dusk::cb::device_lost);
153 | deviceDesc.SetUncapturedErrorCallback(dusk::cb::uncaptured_error);
154 | auto device = adapter.CreateDevice(&deviceDesc);
155 |
156 | // Setup surface for drawing and presenting
157 | wgpu::SurfaceCapabilities capabilities;
158 | surface.GetCapabilities(adapter, &capabilities);
159 | auto surfaceFormat = capabilities.formats[0];
160 | wgpu::SurfaceConfiguration config = {
161 | .device = device,
162 | .format = surfaceFormat,
163 | .width = kWidth,
164 | .height = kHeight,
165 | .presentMode = wgpu::PresentMode::Fifo,
166 | };
167 | surface.Configure(&config);
168 |
169 | // Create buffers
170 | auto vertexBuffer = dusk::webgpu::create_buffer(
171 | device, "Cube Data Buffer", cube_data.data(),
172 | cube_data.size() * sizeof(float), wgpu::BufferUsage::Vertex);
173 |
174 | // Shaders
175 | auto shader =
176 | dusk::webgpu::create_shader_module(device, "Main Shader Module", kShader);
177 |
178 | // Pipeline creation
179 | std::array vertAttributes{
180 | wgpu::VertexAttribute{
181 | .format = wgpu::VertexFormat::Float32x4,
182 | .offset = kPositionByteOffset,
183 | .shaderLocation = 0,
184 | },
185 | wgpu::VertexAttribute{
186 | .format = wgpu::VertexFormat::Float32x2,
187 | .offset = kUVByteOffset,
188 | .shaderLocation = 1,
189 | }};
190 |
191 | wgpu::VertexBufferLayout vertBufferLayout{
192 | .stepMode = wgpu::VertexStepMode::Vertex,
193 | .arrayStride = kCubeDataStride * sizeof(float),
194 | .attributeCount = vertAttributes.size(),
195 | .attributes = vertAttributes.data(),
196 | };
197 |
198 | wgpu::ColorTargetState target{
199 | .format = surfaceFormat,
200 | };
201 |
202 | wgpu::FragmentState fragState{
203 | .module = shader,
204 | .entryPoint = "fs_main",
205 | .constants = nullptr,
206 | .targetCount = 1,
207 | .targets = &target,
208 | };
209 |
210 | wgpu::DepthStencilState depthState = {
211 | .format = wgpu::TextureFormat::Depth24Plus,
212 | .depthWriteEnabled = true,
213 | .depthCompare = wgpu::CompareFunction::Less,
214 | };
215 |
216 | wgpu::RenderPipelineDescriptor pipelineDesc{
217 | .label = "Main Render Pipeline",
218 | .layout = nullptr,
219 | .vertex =
220 | {
221 | .module = shader,
222 | .entryPoint = "vs_main",
223 | .constants = nullptr,
224 | .bufferCount = 1,
225 | .buffers = &vertBufferLayout,
226 | },
227 | .primitive =
228 | {
229 | // Cube is solid, so the back faces can never be seen
230 | .cullMode = wgpu::CullMode::Back,
231 | },
232 | // Enable depth-testing so correct front ordering is maintained
233 | .depthStencil = &depthState,
234 | .fragment = &fragState,
235 | };
236 | auto pipeline = device.CreateRenderPipeline(&pipelineDesc);
237 |
238 | // Create depth texture
239 | auto depthTexture = dusk::webgpu::create_texture(
240 | device, "Depth texture",
241 | {
242 | .width = kWidth,
243 | .height = kHeight,
244 | },
245 | wgpu::TextureFormat::Depth24Plus, wgpu::TextureUsage::RenderAttachment);
246 |
247 | // Setup Uniforms
248 | constexpr uint64_t uniformBufferSize = 4 * 16; // mat4x4
249 | auto uniformBuffer = dusk::webgpu::create_buffer(
250 | device, "Uniform buffer", uniformBufferSize, wgpu::BufferUsage::Uniform);
251 |
252 | std::array bindEntries{
253 | wgpu::BindGroupEntry{
254 | .binding = 0,
255 | .buffer = uniformBuffer,
256 | .size = uniformBufferSize,
257 | },
258 | };
259 |
260 | wgpu::BindGroupDescriptor bindGroupDesc{
261 | .label = "Uniform bind group",
262 | .layout = pipeline.GetBindGroupLayout(0),
263 | .entryCount = bindEntries.size(),
264 | .entries = bindEntries.data(),
265 | };
266 | auto uniformBindGroup = device.CreateBindGroup(&bindGroupDesc);
267 |
268 | auto aspect = float(kWidth) / float(kHeight);
269 | auto fov_y_radians = float((2.f * std::numbers::pi_v) / 5.f);
270 | auto projectionMatrix =
271 | dusk::Mat4::Perspective(fov_y_radians, aspect, 1.f, 100.f);
272 | auto viewMatrix = dusk::Mat4::Translation(dusk::Vec3(0, 0, -4));
273 |
274 | auto projViewMatrix = projectionMatrix * viewMatrix;
275 |
276 | auto frame = 1.f;
277 | auto transformation_matrix = [&projViewMatrix, &frame]() -> dusk::Mat4 {
278 | frame += 1.f;
279 |
280 | auto rotMatrix = dusk::Mat4::Rotation(
281 | 1, dusk::Vec3(sinf(frame / 256.f), cosf(frame / 256.f), 0));
282 | return projViewMatrix * rotMatrix;
283 | };
284 |
285 | wgpu::RenderPassColorAttachment attachment{
286 | .view = nullptr,
287 | .loadOp = wgpu::LoadOp::Clear,
288 | .storeOp = wgpu::StoreOp::Store,
289 | .clearValue = {.5, .5, .5, 1.},
290 | };
291 |
292 | wgpu::RenderPassDepthStencilAttachment depthStencilAttach{
293 | .view = depthTexture.CreateView(),
294 | .depthLoadOp = wgpu::LoadOp::Clear,
295 | .depthStoreOp = wgpu::StoreOp::Store,
296 | .depthClearValue = 1.0,
297 | };
298 |
299 | wgpu::RenderPassDescriptor renderPass{
300 | .label = "Main Render Pass",
301 | .colorAttachmentCount = 1,
302 | .colorAttachments = &attachment,
303 | .depthStencilAttachment = &depthStencilAttach,
304 | };
305 |
306 | while (!glfwWindowShouldClose(window)) {
307 | device.Tick();
308 | glfwPollEvents();
309 |
310 | auto transform = transformation_matrix();
311 | device.GetQueue().WriteBuffer(uniformBuffer, 0, transform.data(),
312 | uniformBufferSize);
313 |
314 | auto encoder = device.CreateCommandEncoder();
315 | encoder.SetLabel("Main Command Encoder");
316 |
317 | {
318 | wgpu::SurfaceTexture surfaceTexture;
319 | surface.GetCurrentTexture(&surfaceTexture);
320 |
321 | auto backbufferView = surfaceTexture.texture.CreateView();
322 | backbufferView.SetLabel("Back Buffer Texture View");
323 |
324 | attachment.view = backbufferView;
325 |
326 | auto pass = encoder.BeginRenderPass(&renderPass);
327 | pass.SetPipeline(pipeline);
328 | pass.SetBindGroup(0, uniformBindGroup);
329 | pass.SetVertexBuffer(0, vertexBuffer);
330 | pass.Draw(kVertexCount);
331 | pass.End();
332 | }
333 | auto commands = encoder.Finish();
334 |
335 | device.GetQueue().Submit(1, &commands);
336 | surface.Present();
337 | }
338 |
339 | vertexBuffer.Destroy();
340 | surface.Unconfigure();
341 | surface = nullptr;
342 | device.Destroy();
343 |
344 | glfwDestroyWindow(window);
345 | glfwTerminate();
346 | return 0;
347 | }
348 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/src/example_05/main.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 |
22 | #include "src/common/callback.h"
23 | #include "src/common/glfw.h"
24 | #include "src/common/mat4.h"
25 | #include "src/common/webgpu_helpers.h"
26 | #include "src/common/wgpu.h"
27 |
28 | namespace {
29 |
30 | constexpr uint32_t kWidth = 1024;
31 | constexpr uint32_t kHeight = 768;
32 |
33 | // clang-format off
34 | constexpr const std::array cube_data {
35 | // vec4 position, vec2 uv,
36 | 1, -1, 1, 1, 1, 1,
37 | -1, -1, 1, 1, 0, 1,
38 | -1, -1, -1, 1, 0, 0,
39 | 1, -1, -1, 1, 1, 0,
40 | 1, -1, 1, 1, 1, 1,
41 | -1, -1, -1, 1, 0, 0,
42 |
43 | 1, 1, 1, 1, 1, 1,
44 | 1, -1, 1, 1, 0, 1,
45 | 1, -1, -1, 1, 0, 0,
46 | 1, 1, -1, 1, 1, 0,
47 | 1, 1, 1, 1, 1, 1,
48 | 1, -1, -1, 1, 0, 0,
49 |
50 | -1, 1, 1, 1, 1, 1,
51 | 1, 1, 1, 1, 0, 1,
52 | 1, 1, -1, 1, 0, 0,
53 | -1, 1, -1, 1, 1, 0,
54 | -1, 1, 1, 1, 1, 1,
55 | 1, 1, -1, 1, 0, 0,
56 |
57 | -1, -1, 1, 1, 1, 1,
58 | -1, 1, 1, 1, 0, 1,
59 | -1, 1, -1, 1, 0, 0,
60 | -1, -1, -1, 1, 1, 0,
61 | -1, -1, 1, 1, 1, 1,
62 | -1, 1, -1, 1, 0, 0,
63 |
64 | 1, 1, 1, 1, 1, 1,
65 | -1, 1, 1, 1, 0, 1,
66 | -1, -1, 1, 1, 0, 0,
67 | -1, -1, 1, 1, 0, 0,
68 | 1, -1, 1, 1, 1, 0,
69 | 1, 1, 1, 1, 1, 1,
70 |
71 | 1, -1, -1, 1, 1, 1,
72 | -1, -1, -1, 1, 0, 1,
73 | -1, 1, -1, 1, 0, 0,
74 | 1, 1, -1, 1, 1, 0,
75 | 1, -1, -1, 1, 1, 1,
76 | -1, 1, -1, 1, 0, 0,
77 | };
78 | // clang-format on
79 |
80 | constexpr uint32_t kVertexCount = 36;
81 | constexpr uint32_t kPositionByteOffset = 0;
82 | constexpr uint32_t kUVByteOffset = 4 * sizeof(float);
83 | constexpr uint32_t kCubeDataStride = 6;
84 |
85 | constexpr uint32_t kNumInstances = 1024 * 1024;
86 |
87 | constexpr const char* kShader = R"(
88 | struct Uniforms {
89 | pv : mat4x4f,
90 | num_instances: u32,
91 | frame: f32,
92 | }
93 | @binding(0) @group(0) var uniforms : Uniforms;
94 |
95 | struct VertexInput {
96 | @builtin(instance_index) instance_idx : u32,
97 | @location(0) pos: vec4f,
98 | @location(1) uv: vec2f,
99 | }
100 |
101 | struct VertexOutput {
102 | @builtin(position) vertex_pos: vec4f,
103 | @location(0) uv: vec2f,
104 | @location(1) frag_colour: vec4f,
105 | }
106 |
107 | const step = 2.f;
108 | const amplitude = (3.f / 2.f);
109 |
110 | @vertex
111 | fn vs_main(in: VertexInput) -> VertexOutput {
112 | // Assume the grid is always square
113 | let per_side = u32(sqrt(f32(uniforms.num_instances)));
114 | let half_side = (f32(per_side) / 2.f) + .5;
115 |
116 | let frame_step = uniforms.frame / 64;
117 |
118 | // Find our position in the grid based on which instance we're emitting and the
119 | // number of cubes per side.
120 | let x = in.instance_idx / per_side;
121 | let y = in.instance_idx % per_side;
122 |
123 | let x_pos = step * (f32(x) - half_side);
124 | let y_pos = (sin((f32(x) / 1.75) + frame_step) +
125 | cos((f32(y) / 1.75) + frame_step)) * amplitude;
126 | let z_pos = step * (f32(y) - half_side);
127 |
128 | // The WGSL matrix constructor is column major. So each group of 4 numbers is a column,
129 | // so, to set the translation information into the 4th column, we have to put it into
130 | // what looks like the 4th row.
131 | let model = mat4x4f(1, 0, 0, 0,
132 | 0, 1, 0, 0,
133 | 0, 0, 1, 0,
134 | x_pos, y_pos, z_pos, 1);
135 |
136 |
137 | let vert_pos = uniforms.pv * model * in.pos;
138 | let frag_colour = 0.5 * (in.pos + vec4(1));
139 | return VertexOutput(vert_pos, in.uv, frag_colour);
140 | }
141 |
142 | @fragment
143 | fn fs_main(in : VertexOutput) -> @location(0) vec4f {
144 | return in.frag_colour;
145 | }
146 | )";
147 |
148 | struct Uniforms {
149 | dusk::Mat4 projView;
150 | uint32_t numInstances;
151 | float frame;
152 | std::array padding_{};
153 | };
154 |
155 | } // namespace
156 |
157 | int main() {
158 | glfwSetErrorCallback(dusk::cb::glfw_error);
159 |
160 | if (!glfwInit()) {
161 | std::println(stderr, "Failed to initialize GLFW.");
162 | return 1;
163 | }
164 |
165 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
166 | glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
167 |
168 | auto window = glfwCreateWindow(kWidth, kHeight, "dusk", nullptr, nullptr);
169 | if (!window) {
170 | std::println(stderr, "Unable to create GLFW window");
171 | return 1;
172 | }
173 |
174 | auto instance = wgpu::CreateInstance();
175 |
176 | // Get surface
177 | auto surface = wgpu::glfw::CreateSurfaceForWindow(instance, window);
178 |
179 | // Get Adapter
180 | wgpu::Adapter adapter{};
181 | {
182 | wgpu::RequestAdapterOptions adapter_opts{
183 | .powerPreference = wgpu::PowerPreference::HighPerformance,
184 | .compatibleSurface = surface,
185 | };
186 | instance.RequestAdapter(&adapter_opts, wgpu::CallbackMode::AllowSpontaneous,
187 | dusk::cb::adapter_request, &adapter);
188 | }
189 |
190 | // Get device
191 | wgpu::DeviceDescriptor deviceDesc{};
192 | deviceDesc.label = "Primary Device";
193 | deviceDesc.SetDeviceLostCallback(wgpu::CallbackMode::AllowProcessEvents,
194 | dusk::cb::device_lost);
195 | deviceDesc.SetUncapturedErrorCallback(dusk::cb::uncaptured_error);
196 | auto device = adapter.CreateDevice(&deviceDesc);
197 |
198 | // Setup surface for drawing and presenting
199 | wgpu::SurfaceCapabilities capabilities;
200 | surface.GetCapabilities(adapter, &capabilities);
201 | auto surfaceFormat = capabilities.formats[0];
202 | wgpu::SurfaceConfiguration config = {
203 | .device = device,
204 | .format = surfaceFormat,
205 | .usage = wgpu::TextureUsage::RenderAttachment,
206 | .width = kWidth,
207 | .height = kHeight,
208 | .presentMode = wgpu::PresentMode::Fifo,
209 | };
210 | surface.Configure(&config);
211 |
212 | // Create buffers
213 | auto vertexBuffer = dusk::webgpu::create_buffer(
214 | device, "Cube Data Buffer", cube_data.data(),
215 | cube_data.size() * sizeof(float), wgpu::BufferUsage::Vertex);
216 |
217 | // Shaders
218 | auto shader = dusk::webgpu::create_shader_module(device, "Main Shader Module",
219 | std::string(kShader));
220 |
221 | // Pipeline creation
222 | std::array vertAttributes{
223 | wgpu::VertexAttribute{
224 | .format = wgpu::VertexFormat::Float32x4,
225 | .offset = kPositionByteOffset,
226 | .shaderLocation = 0,
227 | },
228 | wgpu::VertexAttribute{
229 | .format = wgpu::VertexFormat::Float32x2,
230 | .offset = kUVByteOffset,
231 | .shaderLocation = 1,
232 | },
233 | };
234 |
235 | wgpu::VertexBufferLayout vertBufferLayout{
236 | .stepMode = wgpu::VertexStepMode::Vertex,
237 | .arrayStride = kCubeDataStride * sizeof(float),
238 | .attributeCount = vertAttributes.size(),
239 | .attributes = vertAttributes.data(),
240 | };
241 |
242 | wgpu::ColorTargetState target{
243 | .format = surfaceFormat,
244 | };
245 |
246 | wgpu::FragmentState fragState{
247 | .module = shader,
248 | .entryPoint = "fs_main",
249 | .constants = nullptr,
250 | .targetCount = 1,
251 | .targets = &target,
252 | };
253 |
254 | wgpu::DepthStencilState depthState = {
255 | .format = wgpu::TextureFormat::Depth24Plus,
256 | .depthWriteEnabled = true,
257 | .depthCompare = wgpu::CompareFunction::Less,
258 | };
259 |
260 | wgpu::RenderPipelineDescriptor pipelineDesc{
261 | .label = "Main Render Pipeline",
262 | .layout = nullptr,
263 | .vertex =
264 | {
265 | .module = shader,
266 | .entryPoint = "vs_main",
267 | .constants = nullptr,
268 | .bufferCount = 1,
269 | .buffers = &vertBufferLayout,
270 | },
271 | .primitive =
272 | {
273 | // Cube is solid, so the back faces can never be seen
274 | .cullMode = wgpu::CullMode::Back,
275 | },
276 | // Enable depth-testing so correct front ordering is maintained
277 | .depthStencil = &depthState,
278 | .fragment = &fragState,
279 | };
280 | auto pipeline = device.CreateRenderPipeline(&pipelineDesc);
281 |
282 | // Create depth texture
283 | auto depthTexture = dusk::webgpu::create_texture(
284 | device, "Depth texture",
285 | {
286 | .width = kWidth,
287 | .height = kHeight,
288 | },
289 | wgpu::TextureFormat::Depth24Plus, wgpu::TextureUsage::RenderAttachment);
290 |
291 | // Setup Uniforms
292 | constexpr uint64_t uniformBufferSize = sizeof(Uniforms);
293 | auto uniformBuffer = dusk::webgpu::create_buffer(
294 | device, "Uniform buffer", uniformBufferSize, wgpu::BufferUsage::Uniform);
295 |
296 | Uniforms uniforms{
297 | .projView = dusk::Mat4::Identity(),
298 | .numInstances = kNumInstances,
299 | .frame = 0.f,
300 | };
301 |
302 | std::array bindEntries{
303 | wgpu::BindGroupEntry{
304 | .binding = 0,
305 | .buffer = uniformBuffer,
306 | .size = uniformBufferSize,
307 | },
308 | };
309 |
310 | wgpu::BindGroupDescriptor bindGroupDesc{
311 | .label = "Uniform bind group",
312 | .layout = pipeline.GetBindGroupLayout(0),
313 | .entryCount = bindEntries.size(),
314 | .entries = bindEntries.data(),
315 | };
316 | auto uniformBindGroup = device.CreateBindGroup(&bindGroupDesc);
317 |
318 | auto aspect = float(kWidth) / float(kHeight);
319 | auto fov_y_radians = float((45.f * (std::numbers::pi_v) / 180.f));
320 | auto projectionMatrix =
321 | dusk::Mat4::Perspective(fov_y_radians, aspect, 1.f, 1000.f);
322 |
323 | auto startPos = dusk::Mat4::Translation(dusk::Vec3(0, -75, -650));
324 |
325 | auto update_view_matrix = [&] {
326 | uniforms.frame += 1.f;
327 | auto viewMatrix = startPos * dusk::Mat4::Rotation(uniforms.frame / 2048.f,
328 | dusk::Vec3(0., 1., 0.));
329 |
330 | uniforms.projView = projectionMatrix * viewMatrix;
331 | };
332 |
333 | wgpu::RenderPassColorAttachment attachment{
334 | .view = nullptr,
335 | .loadOp = wgpu::LoadOp::Clear,
336 | .storeOp = wgpu::StoreOp::Store,
337 | .clearValue = {.5, .5, .5, 1.},
338 | };
339 |
340 | wgpu::RenderPassDepthStencilAttachment depthStencilAttach{
341 | .view = depthTexture.CreateView(),
342 | .depthLoadOp = wgpu::LoadOp::Clear,
343 | .depthStoreOp = wgpu::StoreOp::Store,
344 | .depthClearValue = 1.0,
345 | };
346 |
347 | wgpu::RenderPassDescriptor renderPass{
348 | .label = "Main Render Pass",
349 | .colorAttachmentCount = 1,
350 | .colorAttachments = &attachment,
351 | .depthStencilAttachment = &depthStencilAttach,
352 | };
353 |
354 | while (!glfwWindowShouldClose(window)) {
355 | glfwPollEvents();
356 | device.Tick();
357 |
358 | update_view_matrix();
359 | device.GetQueue().WriteBuffer(uniformBuffer, 0, &uniforms,
360 | uniformBufferSize);
361 |
362 | auto encoder = device.CreateCommandEncoder();
363 | encoder.SetLabel("Main Command Encoder");
364 |
365 | {
366 | wgpu::SurfaceTexture surfaceTexture;
367 | surface.GetCurrentTexture(&surfaceTexture);
368 |
369 | auto backbufferView = surfaceTexture.texture.CreateView();
370 | backbufferView.SetLabel("Back Buffer Texture View");
371 |
372 | attachment.view = backbufferView;
373 |
374 | auto pass = encoder.BeginRenderPass(&renderPass);
375 | pass.SetPipeline(pipeline);
376 | pass.SetBindGroup(0, uniformBindGroup);
377 | pass.SetVertexBuffer(0, vertexBuffer);
378 | pass.Draw(kVertexCount, kNumInstances);
379 | pass.End();
380 | }
381 | auto commands = encoder.Finish();
382 |
383 | device.GetQueue().Submit(1, &commands);
384 | surface.Present();
385 | }
386 |
387 | vertexBuffer.Destroy();
388 | surface.Unconfigure();
389 | surface = nullptr;
390 | device.Destroy();
391 |
392 | glfwDestroyWindow(window);
393 | glfwTerminate();
394 | return 0;
395 | }
396 |
--------------------------------------------------------------------------------
/src/common/log.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2025 The Dusk Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include "src/common/log.h"
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 |
22 | #pragma clang diagnostic push
23 | #pragma clang diagnostic ignored "-Wcovered-switch-default"
24 | #include
25 | #pragma clang diagnostic pop
26 |
27 | #include "src/common/expected.h"
28 | #include "src/common/wgpu.h"
29 |
30 | namespace dusk::log {
31 | namespace {
32 |
33 | std::string format_number(uint64_t num) {
34 | auto s = std::to_string(num);
35 | std::stringstream ret;
36 |
37 | auto remainder = s.length() % 3;
38 | ret << s.substr(0, remainder);
39 | for (size_t i = remainder; i < s.length(); i += 3) {
40 | if (i > 0) {
41 | ret << ",";
42 | }
43 | ret << s.substr(i, 3);
44 | }
45 | return ret.str();
46 | }
47 |
48 | } // namespace
49 |
50 | std::string to_str(wgpu::FeatureName f) {
51 | std::stringstream s;
52 | s << f;
53 | return s.str();
54 | }
55 |
56 | std::string to_str(wgpu::WGSLLanguageFeatureName name) {
57 | std::stringstream s;
58 | s << name;
59 | return s.str();
60 | }
61 |
62 | std::string to_str(wgpu::AdapterType type) {
63 | std::stringstream s;
64 | s << type;
65 | return s.str();
66 | }
67 |
68 | std::string to_str(wgpu::BackendType type) {
69 | std::stringstream s;
70 | s << type;
71 | return s.str();
72 | }
73 |
74 | std::string to_str(wgpu::DeviceLostReason reason) {
75 | std::stringstream s;
76 | s << reason;
77 | return s.str();
78 | }
79 |
80 | std::string to_str(wgpu::ErrorType type) {
81 | std::stringstream s;
82 | s << type;
83 | return s.str();
84 | }
85 |
86 | std::string to_str(wgpu::HeapProperty props) {
87 | std::stringstream s;
88 | s << props;
89 | return s.str();
90 | }
91 |
92 | std::string to_str(wgpu::PowerPreference pref) {
93 | std::stringstream s;
94 | s << pref;
95 | return s.str();
96 | }
97 |
98 | std::string to_str(wgpu::SubgroupMatrixComponentType type) {
99 | std::stringstream s;
100 | s << type;
101 | return s.str();
102 | }
103 |
104 | std::string to_str(wgpu::AdapterPropertiesD3D* props) {
105 | std::stringstream out;
106 | std::println(out, " AdapterPropertiesD3D");
107 | std::println(out, " ====================");
108 | std::println(out, " ShaderModel: {}", props->shaderModel);
109 | return out.str();
110 | }
111 |
112 | std::string to_str(wgpu::AdapterPropertiesVk* props) {
113 | std::stringstream out;
114 | std::println(out, " AdapterPropertiesVK");
115 | std::println(out, " ===================");
116 | std::println(out, " Driver version: {}", props->driverVersion);
117 | return out.str();
118 | }
119 |
120 | std::string to_str(wgpu::AdapterPropertiesSubgroupMatrixConfigs* props) {
121 | std::stringstream out;
122 |
123 | std::println(out, " AdapterPropertiesSubgroupMatrixConfig");
124 | std::println(out, " =====================================");
125 | for (size_t i = 0; i < props->configCount; ++i) {
126 | const wgpu::SubgroupMatrixConfig& cfg = props->configs[i];
127 |
128 | if (i > 0) {
129 | std::println("");
130 | }
131 |
132 | std::println(out, " ComponentType: {}", to_str(cfg.componentType));
133 | std::println(out, " ResultComponentType: {}",
134 | to_str(cfg.resultComponentType));
135 | std::println(out, " M: {}, N: {}, K: {}", cfg.M, cfg.N, cfg.K);
136 | }
137 | return out.str();
138 | }
139 |
140 | std::string to_str(wgpu::AdapterPropertiesMemoryHeaps* props) {
141 | std::stringstream out;
142 | std::println(out, " AdapterPropertiesMemroyHeaps");
143 | std::println(out, " ============================");
144 |
145 | std::println(out, " Heap count: {}", props->heapCount);
146 | if (props->heapInfo) {
147 | std::println(out, " Heap size: {}", props->heapInfo->size);
148 | std::println(out, " Heap properties: {}",
149 | to_str(props->heapInfo->properties));
150 | }
151 | return out.str();
152 | }
153 |
154 | std::string to_str(wgpu::DawnAdapterPropertiesPowerPreference* props) {
155 | std::stringstream out;
156 | std::println(out, " DawnAdapterPropertiesPowerPreference");
157 | std::println(out, " ====================================");
158 | std::println(out, " Power preference: {}", to_str(props->powerPreference));
159 | return out.str();
160 | }
161 |
162 | std::string to_str(const wgpu::AdapterInfo& info) {
163 | std::stringstream out;
164 | std::println(out, "Adapter Info");
165 | std::println(out, " Vendor: {}", std::string_view(info.vendor));
166 | std::println(out, " Architecture: {}", std::string_view(info.architecture));
167 | std::println(out, " Device: {}", std::string_view(info.device));
168 | std::println(out, " Description: {}", std::string_view(info.description));
169 | std::println(out, " Adapter Type: {}", to_str(info.adapterType));
170 | std::println(out, " Backend Type: {}", to_str(info.backendType));
171 | std::println(out, " Vendor ID: 0x{:x}", info.vendorID);
172 | std::println(out, " Device ID: 0x{:x}", info.deviceID);
173 | std::println(out, " Subgroup size: min: {}, max {}", info.subgroupMinSize,
174 | info.subgroupMaxSize);
175 |
176 | wgpu::ChainedStructOut* next = info.nextInChain;
177 | while (next != nullptr) {
178 | std::println(out, "");
179 |
180 | switch (next->sType) {
181 | case wgpu::SType::AdapterPropertiesD3D:
182 | std::print(out, "{}",
183 | to_str(static_cast(next)));
184 | break;
185 |
186 | case wgpu::SType::AdapterPropertiesVk:
187 | std::print(out, "{}",
188 | to_str(static_cast(next)));
189 | break;
190 |
191 | case wgpu::SType::AdapterPropertiesSubgroupMatrixConfigs:
192 | std::print(
193 | out, "{}",
194 | to_str(static_cast(
195 | next)));
196 | break;
197 |
198 | case wgpu::SType::AdapterPropertiesMemoryHeaps:
199 | std::print(
200 | out, "{}",
201 | to_str(static_cast(next)));
202 | break;
203 |
204 | case wgpu::SType::DawnAdapterPropertiesPowerPreference:
205 | std::print(
206 | out, "{}",
207 | to_str(static_cast(
208 | next)));
209 | break;
210 |
211 | default:
212 | std::println(stderr, "Unknown stype: {}",
213 | static_cast(next->sType));
214 | break;
215 | }
216 | next = next->nextInChain;
217 | }
218 |
219 | return out.str();
220 | }
221 |
222 | std::string limits(const wgpu::Limits& limits, std::string_view indent) {
223 | std::stringstream out;
224 | std::println(out, "{}maxTextureDimension1D: {}", indent,
225 | format_number(limits.maxTextureDimension1D));
226 | std::println(out, "{}maxTextureDimension2D: {}", indent,
227 | format_number(limits.maxTextureDimension2D));
228 | std::println(out, "{}maxTextureDimension3D: {}", indent,
229 | format_number(limits.maxTextureDimension3D));
230 | std::println(out, "{}maxTextureArrayLayers: {}", indent,
231 | format_number(limits.maxTextureArrayLayers));
232 | std::println(out, "{}maxBindGroups: {}", indent,
233 | format_number(limits.maxBindGroups));
234 | std::println(out, "{}maxDynamicUniformBuffersPerPipelineLayout: {}", indent,
235 | format_number(limits.maxDynamicUniformBuffersPerPipelineLayout));
236 | std::println(out, "{}maxDynamicStorageBuffersPerPipelineLayout: {}", indent,
237 | format_number(limits.maxDynamicStorageBuffersPerPipelineLayout));
238 | std::println(out, "{}maxSampledTexturesPerShaderStage: {}", indent,
239 | format_number(limits.maxSampledTexturesPerShaderStage));
240 | std::println(out, "{}maxSamplersPerShaderStage: {}", indent,
241 | format_number(limits.maxSamplersPerShaderStage));
242 | std::println(out, "{}maxStorageBuffersPerShaderStage: {}", indent,
243 | format_number(limits.maxStorageBuffersPerShaderStage));
244 | std::println(out, "{}maxStorageTexturesPerShaderStage: {}", indent,
245 | format_number(limits.maxStorageTexturesPerShaderStage));
246 | std::println(out, "{}maxUniformBuffersPerShaderStage: {}", indent,
247 | format_number(limits.maxUniformBuffersPerShaderStage));
248 | std::println(out, "{}maxUniformBufferBindingSize: {}", indent,
249 | format_number(limits.maxUniformBufferBindingSize));
250 | std::println(out, "{}maxStorageBufferBindingSize: {}", indent,
251 | format_number(limits.maxStorageBufferBindingSize));
252 | std::println(out, "{}minUniformBufferOffsetAlignment: {}", indent,
253 | format_number(limits.minUniformBufferOffsetAlignment));
254 | std::println(out, "{}minStorageBufferOffsetAlignment: {}", indent,
255 | format_number(limits.minStorageBufferOffsetAlignment));
256 | std::println(out, "{}maxVertexBuffers: {}", indent,
257 | format_number(limits.maxVertexBuffers));
258 | std::println(out, "{}maxVertexAttributes: {}", indent,
259 | format_number(limits.maxVertexAttributes));
260 | std::println(out, "{}maxVertexBufferArrayStride: {}", indent,
261 | format_number(limits.maxVertexBufferArrayStride));
262 | std::println(out, "{}maxInterStageShaderComponents: {}", indent,
263 | format_number(limits.maxInterStageShaderVariables));
264 | std::println(out, "{}maxInterStageShaderVariables: {}", indent,
265 | format_number(limits.maxInterStageShaderVariables));
266 | std::println(out, "{}maxColorAttachments: {}", indent,
267 | format_number(limits.maxColorAttachments));
268 | std::println(out, "{}maxComputeWorkgroupStorageSize: {}", indent,
269 | format_number(limits.maxComputeWorkgroupStorageSize));
270 | std::println(out, "{}maxComputeInvocationsPerWorkgroup: {}", indent,
271 | format_number(limits.maxComputeInvocationsPerWorkgroup));
272 | std::println(out, "{}maxComputeWorkgroupSizeX: {}", indent,
273 | format_number(limits.maxComputeWorkgroupSizeX));
274 | std::println(out, "{}maxComputeWorkgroupSizeY: {}", indent,
275 | format_number(limits.maxComputeWorkgroupSizeY));
276 | std::println(out, "{}maxComputeWorkgroupSizeZ: {}", indent,
277 | format_number(limits.maxComputeWorkgroupSizeZ));
278 | std::println(out, "{}maxComputeWorkgroupsPerDimension: {}", indent,
279 | format_number(limits.maxComputeWorkgroupsPerDimension));
280 | std::println(out, "{}maxImmediateSize: {}", indent,
281 | format_number(limits.maxImmediateSize));
282 |
283 | return out.str();
284 | }
285 |
286 | std::expected emit_instance_language_features(
287 | wgpu::Instance& instance) {
288 | wgpu::SupportedWGSLLanguageFeatures supported_features;
289 | instance.GetWGSLLanguageFeatures(&supported_features);
290 |
291 | std::vector names;
292 | names.reserve(supported_features.featureCount);
293 | for (size_t i = 0; i < supported_features.featureCount; ++i) {
294 | names.push_back(to_str(supported_features.features[i]));
295 | }
296 | std::sort(names.begin(), names.end());
297 |
298 | std::println(stderr, "Instance Language Features");
299 | for (auto& name : names) {
300 | std::println(stderr, " {}", name);
301 | }
302 | std::println(stderr, "");
303 | return {};
304 | }
305 |
306 | std::expected emit_adapter_info(wgpu::Adapter& adapter) {
307 | wgpu::DawnAdapterPropertiesPowerPreference power_props{};
308 | wgpu::AdapterPropertiesVk vk_props{};
309 | wgpu::AdapterPropertiesD3D d3d_props{};
310 | wgpu::AdapterPropertiesMemoryHeaps memory_props{};
311 | wgpu::AdapterPropertiesSubgroupMatrixConfigs subgroup_matrix_props{};
312 |
313 | wgpu::AdapterInfo info;
314 | info.nextInChain = &power_props;
315 |
316 | wgpu::ChainedStructOut* cur = info.nextInChain;
317 |
318 | auto hook = [&](wgpu::ChainedStructOut* s) {
319 | cur->nextInChain = s;
320 | cur = cur->nextInChain;
321 | };
322 |
323 | if (adapter.HasFeature(wgpu::FeatureName::AdapterPropertiesD3D)) {
324 | hook(&d3d_props);
325 | }
326 | if (adapter.HasFeature(wgpu::FeatureName::AdapterPropertiesVk)) {
327 | hook(&vk_props);
328 | }
329 | if (adapter.HasFeature(wgpu::FeatureName::AdapterPropertiesMemoryHeaps)) {
330 | hook(&memory_props);
331 | }
332 | if (adapter.HasFeature(
333 | wgpu::FeatureName::ChromiumExperimentalSubgroupMatrix)) {
334 | hook(&subgroup_matrix_props);
335 | }
336 |
337 | WGPU_TRY(adapter.GetInfo(&info));
338 | std::println(stderr, "{}", to_str(info));
339 | return {};
340 | }
341 |
342 | void emit_adapter_features(wgpu::Adapter& adapter) {
343 | wgpu::SupportedFeatures f;
344 | adapter.GetFeatures(&f);
345 |
346 | std::vector feature_names;
347 | feature_names.reserve(f.featureCount);
348 | for (size_t i = 0; i < f.featureCount; ++i) {
349 | feature_names.push_back(to_str(f.features[i]));
350 | }
351 | std::sort(feature_names.begin(), feature_names.end());
352 |
353 | std::println(stderr, "Adapter Features:");
354 | for (auto& name : feature_names) {
355 | std::println(stderr, " {}", name);
356 | }
357 | }
358 |
359 | std::expected emit_adapter_limits(wgpu::Adapter& adapter) {
360 | wgpu::Limits adapter_limits;
361 | WGPU_TRY(adapter.GetLimits(&adapter_limits));
362 |
363 | std::println(stderr, "");
364 | std::println(stderr, "Adapter Limits:");
365 | std::println(stderr, "{}", limits(adapter_limits, " "));
366 |
367 | {
368 | wgpu::ChainedStructOut* next = adapter_limits.nextInChain;
369 | while (next != nullptr) {
370 | if (next->sType == wgpu::SType::DawnTexelCopyBufferRowAlignmentLimits) {
371 | auto* l =
372 | static_cast(next);
373 | std::println(stderr, " minTexelCopyBufferRowAlignment = {}",
374 | l->minTexelCopyBufferRowAlignment);
375 | } else {
376 | std::print(stderr, "Unknown stype: {}",
377 | static_cast(next->sType));
378 | }
379 | next = next->nextInChain;
380 | }
381 | }
382 | return {};
383 | }
384 |
385 | std::expected emit(wgpu::Adapter& adapter) {
386 | VALID_OR_PROPAGATE(emit_adapter_info(adapter));
387 | emit_adapter_features(adapter);
388 | VALID_OR_PROPAGATE(emit_adapter_limits(adapter));
389 | return {};
390 | }
391 |
392 | void emit_device_features(wgpu::Device& device) {
393 | wgpu::SupportedFeatures f;
394 | device.GetFeatures(&f);
395 |
396 | std::vector names;
397 | names.reserve(f.featureCount);
398 | for (size_t i = 0; i < f.featureCount; ++i) {
399 | names.push_back(to_str(f.features[i]));
400 | }
401 |
402 | std::println(stderr, "Device Extensions:");
403 | for (auto& name : names) {
404 | std::println(stderr, " {}", name);
405 | }
406 | }
407 |
408 | std::expected emit_device_limits(wgpu::Device& device) {
409 | wgpu::Limits deviceLimits;
410 | WGPU_TRY(device.GetLimits(&deviceLimits));
411 | std::println(stderr, "");
412 | std::println(stderr, "Device Limits:");
413 | std::println(stderr, "{}", limits(deviceLimits, " "));
414 | return {};
415 | }
416 |
417 | std::expected emit(wgpu::Device& device) {
418 | emit_device_features(device);
419 | VALID_OR_PROPAGATE(emit_device_limits(device));
420 | return {};
421 | }
422 |
423 | } // namespace dusk::log
424 |
--------------------------------------------------------------------------------