├── .clang-format ├── .gitignore ├── .gitmodules ├── AUTHORS ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs ├── 00_setup.md.html ├── 01_instance_adapter_and_device_info.md.html ├── index.html └── markdeep.min.js ├── src ├── CMakeLists.txt ├── common │ ├── CMakeLists.txt │ ├── callback.cc │ ├── callback.h │ ├── expected.h │ ├── glfw.h │ ├── log.cc │ ├── log.h │ ├── mat4.cc │ ├── mat4.h │ ├── vec3.h │ ├── webgpu_helpers.cc │ ├── webgpu_helpers.h │ └── wgpu.h ├── example_01 │ ├── CMakeLists.txt │ ├── README.md │ └── main.cc ├── example_02 │ ├── CMakeLists.txt │ ├── README.md │ └── main.cc ├── example_03 │ ├── CMakeLists.txt │ ├── README.md │ └── main.cc ├── example_04 │ ├── CMakeLists.txt │ ├── README.md │ └── main.cc └── example_05 │ ├── CMakeLists.txt │ ├── README.md │ └── main.cc └── third_party └── CMakeLists.txt /.clang-format: -------------------------------------------------------------------------------- 1 | # http://clang.llvm.org/docs/ClangFormatStyleOptions.html 2 | BasedOnStyle: Chromium 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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.12) 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 | 32 | if ("${CMAKE_BUILD_TYPE}" STREQUAL "") 33 | message(STATUS "No build type selected, default to Debug") 34 | set(CMAKE_BUILD_TYPE "Debug") 35 | endif() 36 | 37 | message(STATUS "") 38 | message(STATUS "Build type...........: ${CMAKE_BUILD_TYPE}") 39 | message(STATUS "C++ compiler.........: ${CMAKE_CXX_COMPILER}") 40 | message(STATUS "CMake generator......: ${CMAKE_GENERATOR}") 41 | message(STATUS "CMake version........: ${CMAKE_VERSION}") 42 | message(STATUS "") 43 | 44 | if (NOT CLANG_FORMAT) 45 | find_program(CLANG_FORMAT "clang-format") 46 | endif() 47 | if (CLANG_FORMAT) 48 | message(STATUS "Clang-format.........: ${CLANG_FORMAT}") 49 | file(GLOB_RECURSE 50 | ALL_CXX_SOURCE_FILES 51 | ${PROJECT_SOURCE_DIR}/src/*.h 52 | ${PROJECT_SOURCE_DIR}/src/*.cc 53 | ) 54 | 55 | add_custom_target( 56 | format 57 | COMMAND ${CLANG_FORMAT} -i ${ALL_CXX_SOURCE_FILES} 58 | ) 59 | endif() 60 | 61 | message(STATUS "") 62 | 63 | function(dusk_compile_options TARGET) 64 | target_include_directories(${TARGET} PRIVATE "${PROJECT_SOURCE_DIR}") 65 | 66 | target_compile_options(${TARGET} PRIVATE 67 | -O3 68 | -fno-rtti 69 | -fno-exceptions 70 | -fvisibility=hidden 71 | -Wall 72 | -Werror 73 | -Wextra 74 | -Wpedantic 75 | -pedantic-errors 76 | 77 | -Wno-unknown-pragmas 78 | ) 79 | 80 | if (("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") OR 81 | ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")) 82 | target_compile_options(${TARGET} PRIVATE 83 | -Weverything 84 | 85 | -Wno-switch-default 86 | -Wno-switch-enum 87 | -Wno-c++98-compat 88 | -Wno-c++98-compat-pedantic 89 | -Wno-documentation 90 | -Wno-documentation-unknown-command 91 | -Wno-poison-system-directories 92 | -Wno-unsafe-buffer-usage 93 | ) 94 | elseif(CMAKE_COMPILER_IS_GNUCXX) 95 | target_compile_options(${TARGET} PRIVATE 96 | -Wno-missing-field-initializers 97 | ) 98 | endif() 99 | endfunction() 100 | 101 | add_subdirectory(third_party) 102 | add_subdirectory(src) 103 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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` 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 21 | #pragma clang diagnostic push 22 | #pragma clang diagnostic ignored "-Wcovered-switch-default" 23 | #include 24 | #pragma clang diagnostic pop 25 | 26 | #include "src/common/expected.h" 27 | #include "src/common/wgpu.h" 28 | 29 | namespace dusk::log { 30 | namespace { 31 | 32 | std::string format_number(uint64_t num) { 33 | auto s = std::to_string(num); 34 | std::stringstream ret; 35 | 36 | auto remainder = s.length() % 3; 37 | ret << s.substr(0, remainder); 38 | for (size_t i = remainder; i < s.length(); i += 3) { 39 | if (i > 0) { 40 | ret << ","; 41 | } 42 | ret << s.substr(i, 3); 43 | } 44 | return ret.str(); 45 | } 46 | 47 | } // namespace 48 | 49 | std::string to_str(wgpu::FeatureName f) { 50 | std::stringstream s; 51 | s << f; 52 | return s.str(); 53 | } 54 | 55 | std::string to_str(wgpu::WGSLLanguageFeatureName name) { 56 | std::stringstream s; 57 | s << name; 58 | return s.str(); 59 | } 60 | 61 | std::string to_str(wgpu::AdapterType type) { 62 | std::stringstream s; 63 | s << type; 64 | return s.str(); 65 | } 66 | 67 | std::string to_str(wgpu::BackendType type) { 68 | std::stringstream s; 69 | s << type; 70 | return s.str(); 71 | } 72 | 73 | std::string to_str(wgpu::DeviceLostReason reason) { 74 | std::stringstream s; 75 | s << reason; 76 | return s.str(); 77 | } 78 | 79 | std::string to_str(wgpu::ErrorType type) { 80 | std::stringstream s; 81 | s << type; 82 | return s.str(); 83 | } 84 | 85 | std::string to_str(wgpu::HeapProperty props) { 86 | std::stringstream s; 87 | s << props; 88 | return s.str(); 89 | } 90 | 91 | std::string to_str(wgpu::PowerPreference pref) { 92 | std::stringstream s; 93 | s << pref; 94 | return s.str(); 95 | } 96 | 97 | std::string to_str(wgpu::SubgroupMatrixComponentType type) { 98 | std::stringstream s; 99 | s << type; 100 | return s.str(); 101 | } 102 | 103 | std::string to_str(wgpu::AdapterPropertiesD3D* props) { 104 | std::stringstream out; 105 | std::println(out, " AdapterPropertiesD3D"); 106 | std::println(out, " ===================="); 107 | std::println(out, " ShaderModel: {}", props->shaderModel); 108 | return out.str(); 109 | } 110 | 111 | std::string to_str(wgpu::AdapterPropertiesVk* props) { 112 | std::stringstream out; 113 | std::println(out, " AdapterPropertiesVK"); 114 | std::println(out, " ==================="); 115 | std::println(out, " Driver version: {}", props->driverVersion); 116 | return out.str(); 117 | } 118 | 119 | std::string to_str(wgpu::AdapterPropertiesSubgroups* props) { 120 | std::stringstream out; 121 | std::println(out, " AdapterPropertiesSubgroups"); 122 | std::println(out, " =========================="); 123 | std::println(out, " SubgroupMinSize: {}", props->subgroupMinSize); 124 | std::println(out, " SubgroupMaxSize: {}", props->subgroupMaxSize); 125 | return out.str(); 126 | } 127 | 128 | std::string to_str(wgpu::AdapterPropertiesSubgroupMatrixConfigs* props) { 129 | std::stringstream out; 130 | 131 | std::println(out, " AdapterPropertiesSubgroupMatrixConfig"); 132 | std::println(out, " ====================================="); 133 | for (size_t i = 0; i < props->configCount; ++i) { 134 | const wgpu::SubgroupMatrixConfig& cfg = props->configs[i]; 135 | 136 | if (i > 0) { 137 | std::println(""); 138 | } 139 | 140 | std::println(out, " ComponentType: {}", to_str(cfg.componentType)); 141 | std::println(out, " ResultComponentType: {}", 142 | to_str(cfg.resultComponentType)); 143 | std::println(out, " M: {}, N: {}, K: {}", cfg.M, cfg.N, cfg.K); 144 | } 145 | return out.str(); 146 | } 147 | 148 | std::string to_str(wgpu::AdapterPropertiesMemoryHeaps* props) { 149 | std::stringstream out; 150 | std::println(out, " AdapterPropertiesMemroyHeaps"); 151 | std::println(out, " ============================"); 152 | 153 | std::println(out, " Heap count: {}", props->heapCount); 154 | if (props->heapInfo) { 155 | std::println(out, " Heap size: {}", props->heapInfo->size); 156 | std::println(out, " Heap properties: {}", 157 | to_str(props->heapInfo->properties)); 158 | } 159 | return out.str(); 160 | } 161 | 162 | std::string to_str(wgpu::DawnAdapterPropertiesPowerPreference* props) { 163 | std::stringstream out; 164 | std::println(out, " DawnAdapterPropertiesPowerPreference"); 165 | std::println(out, " ===================================="); 166 | std::println(out, " Power preference: {}", to_str(props->powerPreference)); 167 | return out.str(); 168 | } 169 | 170 | std::string to_str(const wgpu::AdapterInfo& info) { 171 | std::stringstream out; 172 | std::println(out, "Adapter Info"); 173 | std::println(out, " Vendor: {}", std::string_view(info.vendor)); 174 | std::println(out, " Architecture: {}", std::string_view(info.architecture)); 175 | std::println(out, " Device: {}", std::string_view(info.device)); 176 | std::println(out, " Description: {}", std::string_view(info.description)); 177 | std::println(out, " Adapter Type: {}", to_str(info.adapterType)); 178 | std::println(out, " Backend Type: {}", to_str(info.backendType)); 179 | std::println(out, " Vendor ID: 0x{:x}", info.vendorID); 180 | std::println(out, " Device ID: 0x{:x}", info.deviceID); 181 | std::println(out, " Subgroup size: min: {}, max {}", info.subgroupMinSize, 182 | info.subgroupMaxSize); 183 | 184 | wgpu::ChainedStructOut* next = info.nextInChain; 185 | while (next != nullptr) { 186 | std::println(out, ""); 187 | 188 | switch (next->sType) { 189 | case wgpu::SType::AdapterPropertiesD3D: 190 | std::print(out, "{}", 191 | to_str(static_cast(next))); 192 | break; 193 | 194 | case wgpu::SType::AdapterPropertiesVk: 195 | std::print(out, "{}", 196 | to_str(static_cast(next))); 197 | break; 198 | 199 | case wgpu::SType::AdapterPropertiesSubgroups: 200 | std::print( 201 | out, "{}", 202 | to_str(static_cast(next))); 203 | break; 204 | 205 | case wgpu::SType::AdapterPropertiesSubgroupMatrixConfigs: 206 | std::print( 207 | out, "{}", 208 | to_str(static_cast( 209 | next))); 210 | break; 211 | 212 | case wgpu::SType::AdapterPropertiesMemoryHeaps: 213 | std::print( 214 | out, "{}", 215 | to_str(static_cast(next))); 216 | break; 217 | 218 | case wgpu::SType::DawnAdapterPropertiesPowerPreference: 219 | std::print( 220 | out, "{}", 221 | to_str(static_cast( 222 | next))); 223 | break; 224 | 225 | default: 226 | std::println(stderr, "Unknown stype: {}", 227 | static_cast(next->sType)); 228 | break; 229 | } 230 | next = next->nextInChain; 231 | } 232 | 233 | return out.str(); 234 | } 235 | 236 | std::string limits(const wgpu::Limits& limits, std::string_view indent) { 237 | std::stringstream out; 238 | std::println(out, "{}maxTextureDimension1D: {}", indent, 239 | format_number(limits.maxTextureDimension1D)); 240 | std::println(out, "{}maxTextureDimension2D: {}", indent, 241 | format_number(limits.maxTextureDimension2D)); 242 | std::println(out, "{}maxTextureDimension3D: {}", indent, 243 | format_number(limits.maxTextureDimension3D)); 244 | std::println(out, "{}maxTextureArrayLayers: {}", indent, 245 | format_number(limits.maxTextureArrayLayers)); 246 | std::println(out, "{}maxBindGroups: {}", indent, 247 | format_number(limits.maxBindGroups)); 248 | std::println(out, "{}maxDynamicUniformBuffersPerPipelineLayout: {}", indent, 249 | format_number(limits.maxDynamicUniformBuffersPerPipelineLayout)); 250 | std::println(out, "{}maxDynamicStorageBuffersPerPipelineLayout: {}", indent, 251 | format_number(limits.maxDynamicStorageBuffersPerPipelineLayout)); 252 | std::println(out, "{}maxSampledTexturesPerShaderStage: {}", indent, 253 | format_number(limits.maxSampledTexturesPerShaderStage)); 254 | std::println(out, "{}maxSamplersPerShaderStage: {}", indent, 255 | format_number(limits.maxSamplersPerShaderStage)); 256 | std::println(out, "{}maxStorageBuffersPerShaderStage: {}", indent, 257 | format_number(limits.maxStorageBuffersPerShaderStage)); 258 | std::println(out, "{}maxStorageTexturesPerShaderStage: {}", indent, 259 | format_number(limits.maxStorageTexturesPerShaderStage)); 260 | std::println(out, "{}maxUniformBuffersPerShaderStage: {}", indent, 261 | format_number(limits.maxUniformBuffersPerShaderStage)); 262 | std::println(out, "{}maxUniformBufferBindingSize: {}", indent, 263 | format_number(limits.maxUniformBufferBindingSize)); 264 | std::println(out, "{}maxStorageBufferBindingSize: {}", indent, 265 | format_number(limits.maxStorageBufferBindingSize)); 266 | std::println(out, "{}minUniformBufferOffsetAlignment: {}", indent, 267 | format_number(limits.minUniformBufferOffsetAlignment)); 268 | std::println(out, "{}minStorageBufferOffsetAlignment: {}", indent, 269 | format_number(limits.minStorageBufferOffsetAlignment)); 270 | std::println(out, "{}maxVertexBuffers: {}", indent, 271 | format_number(limits.maxVertexBuffers)); 272 | std::println(out, "{}maxVertexAttributes: {}", indent, 273 | format_number(limits.maxVertexAttributes)); 274 | std::println(out, "{}maxVertexBufferArrayStride: {}", indent, 275 | format_number(limits.maxVertexBufferArrayStride)); 276 | std::println(out, "{}maxInterStageShaderComponents: {}", indent, 277 | format_number(limits.maxInterStageShaderVariables)); 278 | std::println(out, "{}maxInterStageShaderVariables: {}", indent, 279 | format_number(limits.maxInterStageShaderVariables)); 280 | std::println(out, "{}maxColorAttachments: {}", indent, 281 | format_number(limits.maxColorAttachments)); 282 | std::println(out, "{}maxComputeWorkgroupStorageSize: {}", indent, 283 | format_number(limits.maxComputeWorkgroupStorageSize)); 284 | std::println(out, "{}maxComputeInvocationsPerWorkgroup: {}", indent, 285 | format_number(limits.maxComputeInvocationsPerWorkgroup)); 286 | std::println(out, "{}maxComputeWorkgroupSizeX: {}", indent, 287 | format_number(limits.maxComputeWorkgroupSizeX)); 288 | std::println(out, "{}maxComputeWorkgroupSizeY: {}", indent, 289 | format_number(limits.maxComputeWorkgroupSizeY)); 290 | std::println(out, "{}maxComputeWorkgroupSizeZ: {}", indent, 291 | format_number(limits.maxComputeWorkgroupSizeZ)); 292 | std::println(out, "{}maxComputeWorkgroupsPerDimension: {}", indent, 293 | format_number(limits.maxComputeWorkgroupsPerDimension)); 294 | 295 | return out.str(); 296 | } 297 | 298 | void emit(wgpu::InstanceCapabilities& caps) { 299 | std::println(stderr, "Instance Capabilities:"); 300 | std::println(stderr, " timedWaitAnyEnable: {}", 301 | caps.timedWaitAnyEnable ? "true" : "false'"); 302 | std::println(stderr, " timedWaitAnyMaxCount: {}", caps.timedWaitAnyMaxCount); 303 | std::println(stderr, ""); 304 | } 305 | 306 | std::expected emit_instance_language_features( 307 | wgpu::Instance& instance) { 308 | wgpu::SupportedWGSLLanguageFeatures supported_features; 309 | WGPU_TRY(instance.GetWGSLLanguageFeatures(&supported_features)); 310 | 311 | std::vector names; 312 | names.reserve(supported_features.featureCount); 313 | for (size_t i = 0; i < supported_features.featureCount; ++i) { 314 | names.push_back(to_str(supported_features.features[i])); 315 | } 316 | std::sort(names.begin(), names.end()); 317 | 318 | std::println(stderr, "Instance Language Features"); 319 | for (auto& name : names) { 320 | std::println(stderr, " {}", name); 321 | } 322 | std::println(stderr, ""); 323 | return {}; 324 | } 325 | 326 | std::expected emit_adapter_info(wgpu::Adapter& adapter) { 327 | wgpu::DawnAdapterPropertiesPowerPreference power_props{}; 328 | wgpu::AdapterPropertiesVk vk_props{}; 329 | wgpu::AdapterPropertiesD3D d3d_props{}; 330 | wgpu::AdapterPropertiesMemoryHeaps memory_props{}; 331 | wgpu::AdapterPropertiesSubgroups subgroup_props{}; 332 | wgpu::AdapterPropertiesSubgroupMatrixConfigs subgroup_matrix_props{}; 333 | 334 | wgpu::AdapterInfo info; 335 | info.nextInChain = &power_props; 336 | 337 | wgpu::ChainedStructOut* cur = info.nextInChain; 338 | 339 | auto hook = [&](wgpu::ChainedStructOut* s) { 340 | cur->nextInChain = s; 341 | cur = cur->nextInChain; 342 | }; 343 | 344 | if (adapter.HasFeature(wgpu::FeatureName::AdapterPropertiesD3D)) { 345 | hook(&d3d_props); 346 | } 347 | if (adapter.HasFeature(wgpu::FeatureName::AdapterPropertiesVk)) { 348 | hook(&vk_props); 349 | } 350 | if (adapter.HasFeature(wgpu::FeatureName::AdapterPropertiesMemoryHeaps)) { 351 | hook(&memory_props); 352 | } 353 | if (adapter.HasFeature(wgpu::FeatureName::Subgroups)) { 354 | hook(&subgroup_props); 355 | } 356 | if (adapter.HasFeature( 357 | wgpu::FeatureName::ChromiumExperimentalSubgroupMatrix)) { 358 | hook(&subgroup_matrix_props); 359 | } 360 | 361 | WGPU_TRY(adapter.GetInfo(&info)); 362 | std::println(stderr, "{}", to_str(info)); 363 | return {}; 364 | } 365 | 366 | void emit_adapter_features(wgpu::Adapter& adapter) { 367 | wgpu::SupportedFeatures f; 368 | adapter.GetFeatures(&f); 369 | 370 | std::vector feature_names; 371 | feature_names.reserve(f.featureCount); 372 | for (size_t i = 0; i < f.featureCount; ++i) { 373 | feature_names.push_back(to_str(f.features[i])); 374 | } 375 | std::sort(feature_names.begin(), feature_names.end()); 376 | 377 | std::println(stderr, "Adapter Features:"); 378 | for (auto& name : feature_names) { 379 | std::println(stderr, " {}", name); 380 | } 381 | } 382 | 383 | std::expected emit_adapter_limits(wgpu::Adapter& adapter) { 384 | wgpu::Limits adapter_limits; 385 | WGPU_TRY(adapter.GetLimits(&adapter_limits)); 386 | 387 | std::println(stderr, ""); 388 | std::println(stderr, "Adapter Limits:"); 389 | std::println(stderr, "{}", limits(adapter_limits, " ")); 390 | 391 | { 392 | wgpu::ChainedStructOut* next = adapter_limits.nextInChain; 393 | while (next != nullptr) { 394 | if (next->sType == wgpu::SType::DawnExperimentalImmediateDataLimits) { 395 | auto* l = static_cast(next); 396 | std::println(stderr, " maxImmediateDataRangeByteSize = {}", 397 | l->maxImmediateDataRangeByteSize); 398 | } else if (next->sType == 399 | wgpu::SType::DawnTexelCopyBufferRowAlignmentLimits) { 400 | auto* l = 401 | static_cast(next); 402 | std::println(stderr, " minTexelCopyBufferRowAlignment = {}", 403 | l->minTexelCopyBufferRowAlignment); 404 | } else { 405 | std::print(stderr, "Unknown stype: {}", 406 | static_cast(next->sType)); 407 | } 408 | next = next->nextInChain; 409 | } 410 | } 411 | return {}; 412 | } 413 | 414 | std::expected emit(wgpu::Adapter& adapter) { 415 | VALID_OR_PROPAGATE(emit_adapter_info(adapter)); 416 | emit_adapter_features(adapter); 417 | VALID_OR_PROPAGATE(emit_adapter_limits(adapter)); 418 | return {}; 419 | } 420 | 421 | void emit_device_features(wgpu::Device& device) { 422 | wgpu::SupportedFeatures f; 423 | device.GetFeatures(&f); 424 | 425 | std::vector names; 426 | names.reserve(f.featureCount); 427 | for (size_t i = 0; i < f.featureCount; ++i) { 428 | names.push_back(to_str(f.features[i])); 429 | } 430 | 431 | std::println(stderr, "Device Extensions:"); 432 | for (auto& name : names) { 433 | std::println(stderr, " {}", name); 434 | } 435 | } 436 | 437 | std::expected emit_device_limits(wgpu::Device& device) { 438 | wgpu::Limits deviceLimits; 439 | WGPU_TRY(device.GetLimits(&deviceLimits)); 440 | std::println(stderr, ""); 441 | std::println(stderr, "Device Limits:"); 442 | std::println(stderr, "{}", limits(deviceLimits, " ")); 443 | return {}; 444 | } 445 | 446 | std::expected emit(wgpu::Device& device) { 447 | emit_device_features(device); 448 | VALID_OR_PROPAGATE(emit_device_limits(device)); 449 | return {}; 450 | } 451 | 452 | } // namespace dusk::log 453 | -------------------------------------------------------------------------------- /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 Subgroups adapter properties 98 | /// 99 | /// @param props the properties 100 | /// @returns the string representation 101 | std::string to_str(wgpu::AdapterPropertiesSubgroups* props); 102 | 103 | /// Creates a textual representation of the subgroup matrix configuration 104 | /// adapter properties 105 | /// 106 | /// @param props the properties 107 | /// @returns the string representation 108 | std::string to_str(wgpu::AdapterPropertiesSubgroupMatrixConfigs* props); 109 | 110 | /// Creates a textual representation of the memmory heaps adapter properties 111 | /// 112 | /// @param props the properties 113 | /// @returns the string representation 114 | std::string to_str(wgpu::AdapterPropertiesMemoryHeaps* props); 115 | 116 | /// Creates a textual representation of the dawn power preference adapter 117 | /// properties 118 | /// 119 | /// @param props the props 120 | /// @returns the string representation 121 | std::string to_str(wgpu::DawnAdapterPropertiesPowerPreference* props); 122 | 123 | /// Creates a textual version of the limits 124 | /// 125 | /// @param limits the limits to convert 126 | /// @param indent the amount to indent each limit string 127 | /// @returns the string representation 128 | std::string limits(const wgpu::Limits& limits, std::string_view indent); 129 | 130 | /// Emits the instance capability information 131 | /// 132 | /// @param caps the instance capabilities 133 | void emit(wgpu::InstanceCapabilities& caps); 134 | 135 | /// Emits the language features known to the instance 136 | /// 137 | /// @param instance the instance to retrieve the features from 138 | std::expected emit_instance_language_features( 139 | wgpu::Instance& instance); 140 | 141 | /// Emits the adapter info to `stderr` 142 | /// 143 | /// @param adapter the adapter to emit from 144 | std::expected emit_adapter_info(wgpu::Adapter& adapter); 145 | 146 | /// Emits the adapter features to `stderr` 147 | /// 148 | /// @param adapter the adapter to emit from 149 | void emit_adapter_features(wgpu::Adapter& adapter); 150 | 151 | /// Emits the adapter limits to `stderr` 152 | /// 153 | /// @param adapter the adapter to emit from 154 | std::expected emit_adapter_limits(wgpu::Adapter& adapter); 155 | 156 | /// Emits the adapter to `stderr` 157 | /// 158 | /// @param adapter the adapter to emit 159 | std::expected emit(wgpu::Adapter& adapter); 160 | 161 | /// Emits the device features to `stderr` 162 | /// 163 | /// @param device the device to emit from 164 | void emit_device_features(wgpu::Device& device); 165 | 166 | /// Emits the device limits to `stderr` 167 | /// 168 | /// @param device the device to emit from 169 | std::expected emit_device_limits(wgpu::Device& device); 170 | 171 | /// Emits the device to `stderr` 172 | /// 173 | /// @param device the device to emit 174 | std::expected emit(wgpu::Device& device); 175 | 176 | } // namespace dusk::log 177 | -------------------------------------------------------------------------------- /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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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_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 | -------------------------------------------------------------------------------- /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 | wgpu::InstanceCapabilities caps{}; 24 | WGPU_TRY_EXIT(wgpu::GetInstanceCapabilities(&caps)); 25 | dusk::log::emit(caps); 26 | 27 | auto instance = wgpu::CreateInstance(); 28 | dusk::valid_or_exit(dusk::log::emit_instance_language_features(instance)); 29 | 30 | // Get Adapter 31 | wgpu::RequestAdapterOptions adapter_opts{ 32 | .powerPreference = wgpu::PowerPreference::HighPerformance, 33 | }; 34 | wgpu::Adapter adapter{}; 35 | instance.RequestAdapter(&adapter_opts, wgpu::CallbackMode::AllowSpontaneous, 36 | dusk::cb::adapter_request, &adapter); 37 | 38 | dusk::valid_or_exit(dusk::log::emit(adapter)); 39 | 40 | // Get device 41 | wgpu::DeviceDescriptor device_desc{}; 42 | device_desc.label = "default device"; 43 | device_desc.SetDeviceLostCallback(wgpu::CallbackMode::AllowProcessEvents, 44 | dusk::cb::device_lost); 45 | device_desc.SetUncapturedErrorCallback(dusk::cb::uncaptured_error); 46 | 47 | wgpu::Device device = adapter.CreateDevice(&device_desc); 48 | dusk::valid_or_exit(dusk::log::emit(device)); 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /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_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 | -------------------------------------------------------------------------------- /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/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_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_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 | -------------------------------------------------------------------------------- /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_04/README.md: -------------------------------------------------------------------------------- 1 | # Dusk Example 04 -- Rotating Cube 2 | 3 | Rotating cube, uploads uniform data per frame. 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/example_05/README.md: -------------------------------------------------------------------------------- 1 | # Dusk Example 05 -- Instanced Cubes 2 | 3 | Multiple instanced cubes. 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 23 | set(DAWN_BUILD_GEN_DIR ${CMAKE_CURRENT_BINARY_DIR}/gen) 24 | set(DAWN_ENABLE_DESKTOP_GL OFF) 25 | set(DAWN_ENABLE_OPENGLES OFF) 26 | set(DAWN_ENABLE_NULL OFF) 27 | set(DAWN_BUILD_SAMPLES OFF) 28 | 29 | set(TINT_BUILD_DOCS OFF) 30 | set(TINT_BUILD_TESTS OFF) 31 | set(TINT_BUILD_SAMPLES OFF) 32 | set(TINT_BUILD_GLSL_VALIDATOR OFF) 33 | set(TINT_BUILD_GLSL_WRITER OFF) 34 | set(TINT_BUILD_SPV_READER OFF) 35 | 36 | if(NOT APPLE) 37 | set(BUILD_SHARED_LIBS ON) 38 | endif() 39 | 40 | add_definitions( 41 | -Wno-deprecated-builtins # From Abseil 42 | -Wno-nullability-extension # From abseil 43 | -Wno-unknown-warning-option # SPIRV-Tools 44 | ) 45 | 46 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/dawn EXCLUDE_FROM_ALL) 47 | --------------------------------------------------------------------------------