Adding some basic initial files for CMake support. This is almost certainly broken and in need of more dependency updates but I was able to coerce it to build with CMake + make on Linux. It's a start. Change-Id: I515e7949834e9657fa81e4735deb203eec330555
diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..632e5ee --- /dev/null +++ b/AUTHORS
@@ -0,0 +1,6 @@ +# This is the list of Dawn samples authors for copyright purposes. +# +# This does not necessarily list everyone who has contributed code, since in +# some cases, their employer may be the copyright holder. To see the full list +# of contributors, see the revision history in source control. +Google LLC
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..69066a7 --- /dev/null +++ b/CMakeLists.txt
@@ -0,0 +1,156 @@ +# Copyright 2022 The Dawn & Tint Authors +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +cmake_minimum_required(VERSION 3.10.2) + +# When upgrading to CMake 3.11 we can remove SAMPLES_PLACEHOLDER_FILE because source-less add_library +# becomes available. +# When upgrading to CMake 3.12 we should remove the CACHE "" FORCE stuff to +# override options in third_party dependencies. We can also add the HOMEPAGE_URL +# entry to the project `HOMEPAGE_URL "https://dawn.googlesource.com/samples"` + +project( + DawnSamples + DESCRIPTION "Samples for Dawn, a WebGPU implementation" + LANGUAGES C CXX +) +enable_testing() + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_DEBUG_POSTFIX "") + +if ("${CMAKE_BUILD_TYPE}" STREQUAL "") + message(STATUS "No build type selected, default to Debug") + set(CMAKE_BUILD_TYPE "Debug") +endif() + +set(SAMPLES_SRC_DIR "${DawnSamples_SOURCE_DIR}/src") + +set(SAMPLES_PLACEHOLDER_FILE "${SAMPLES_SRC_DIR}/Placeholder.cpp") + +################################################################################ +# Configuration options +################################################################################ + +# option_if_not_defined(name description default) +# Behaves like: +# option(name description default) +# If a variable is not already defined with the given name, otherwise the +# function does nothing. +# Simplifies customization by projects that use Dawn as a dependency. +function (option_if_not_defined name description default) + if(NOT DEFINED ${name}) + option(${name} ${description} ${default}) + endif() +endfunction() + +# set_if_not_defined(name value description) +# Behaves like: +# set(${name} ${value} CACHE STRING ${description}) +# If a variable is not already defined with the given name, otherwise the +# function does nothing. +# Simplifies customization by projects that use Dawn as a dependency. +function (set_if_not_defined name value description) + if(NOT DEFINED ${name}) + set(${name} ${value} CACHE STRING ${description}) + endif() +endfunction() + +# Default values for the backend-enabling options +set(USE_WAYLAND OFF) +set(USE_X11 OFF) + +if(UNIX) + set(USE_X11 ON) +endif() + +# GLFW is not supported in UWP +set(SAMPLES_SUPPORTS_GLFW_FOR_WINDOWING OFF) + +if ((WIN32 AND NOT WINDOWS_STORE) OR (UNIX AND NOT ANDROID)) + set(SAMPLES_SUPPORTS_GLFW_FOR_WINDOWING ON) +endif() + +# Current examples are depend on GLFW +if (SAMPLES_SUPPORTS_GLFW_FOR_WINDOWING) + set(BUILD_SAMPLES ON) +endif() + +option_if_not_defined(SAMPLES_USE_GLFW "Enable compilation of the GLFW windowing utils" ${SAMPLES_SUPPORTS_GLFW_FOR_WINDOWING}) + +if (NOT SAMPLES_USE_GLFW) + message(SEND_ERROR "Dawn samples require GLFW") +endif() + +set_if_not_defined(SAMPLES_THIRD_PARTY_DIR "${DawnSamples_SOURCE_DIR}/third_party" "Directory in which to find third-party dependencies.") + +set_if_not_defined(SAMPLES_DIR "${SAMPLES_THIRD_PARTY_DIR}/dawn" "Directory in which to find Dawn") +set_if_not_defined(SAMPLES_GLFW_DIR "${SAMPLES_THIRD_PARTY_DIR}/glfw" "Directory in which to find GLFW") +set_if_not_defined(SAMPLES_DAWN_DIR "${SAMPLES_THIRD_PARTY_DIR}/dawn" "Directory in which to find Dawm") + +option_if_not_defined(DAWN_USE_WAYLAND "Enable support for Wayland surface" ${USE_WAYLAND}) +option_if_not_defined(DAWN_USE_X11 "Enable support for X11 surface" ${USE_X11}) +option_if_not_defined(DAWN_FETCH_DEPENDENCIES "Use fetch_dawn_dependencies.py as an alternative to using depot_tools" ON) + +message(STATUS "Samples build GLFW support: ${SAMPLES_USE_GLFW}") + +message(STATUS "Using python3") +find_package(PythonInterp 3 REQUIRED) + +################################################################################ +# common_compile_options - sets compiler and linker options common for dawn and +# tint on the given target +################################################################################ +function(common_compile_options TARGET) + if (COMPILER_IS_LIKE_GNU) + target_compile_options(${TARGET} PRIVATE + -fno-exceptions + -fno-rtti + + -Wno-deprecated-builtins + -Wno-unknown-warning-option + ) + + endif(COMPILER_IS_LIKE_GNU) + + if(MSVC) + target_compile_options(${TARGET} PUBLIC /utf-8) + endif() +endfunction() + +################################################################################ +# Run on all subdirectories +################################################################################ + +add_subdirectory(third_party) + +add_subdirectory(src/samples) +
diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 0000000..3eb93c2 --- /dev/null +++ b/CMakeSettings.json
@@ -0,0 +1,100 @@ +{ + "configurations": [ + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": ["msvc_x64_x64"], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "variables": [] + }, + { + "name": "x64-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": ["msvc_x64_x64"], + "variables": [] + }, + { + "name": "x86-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": ["msvc_x86"], + "variables": [] + }, + { + "name": "x86-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": ["msvc_x86"], + "variables": [] + }, + { + "name": "x64-Clang-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": ["clang_cl_x64_x64"], + "variables": [] + }, + { + "name": "x64-Clang-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": ["clang_cl_x64_x64"], + "variables": [] + }, + { + "name": "x86-Clang-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": ["clang_cl_x86"], + "variables": [] + }, + { + "name": "x86-Clang-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": ["clang_cl_x86"], + "variables": [] + } + ] +}
diff --git a/DEPS b/DEPS new file mode 100644 index 0000000..ede80fc --- /dev/null +++ b/DEPS
@@ -0,0 +1,19 @@ +use_relative_paths = True + +vars = { + 'chromium_git': 'https://chromium.googlesource.com', + 'dawn_git': 'https://dawn.googlesource.com', + + 'dawn_revision': '18ac67fc72dfff47efec97c2a8bcf654a13a9e37', + 'glfw_revision': '62e175ef9fae75335575964c845a302447c012c7', +} + +deps = { + 'third_party/dawn': { + 'url': '{dawn_git}/dawn.git@{dawn_revision}', + }, + + 'third_party/glfw': { + 'url': '{chromium_git}/external/github.com/glfw/glfw@{glfw_revision}', + }, +}
diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5b716b6 --- /dev/null +++ b/LICENSE
@@ -0,0 +1,26 @@ +// Copyright 2017-2023 The Dawn & Tint Authors +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/OWNERS b/OWNERS new file mode 100644 index 0000000..65efb86 --- /dev/null +++ b/OWNERS
@@ -0,0 +1,6 @@ +bajones@chromium.org +cwallez@chromium.org +enga@chromium.org +kainino@chromium.org +jiawei.shao@intel.com +lokokung@google.com
diff --git a/README.md b/README.md new file mode 100644 index 0000000..82c6504 --- /dev/null +++ b/README.md
@@ -0,0 +1,26 @@ +<div align="center"> + <img + title="Dawn's logo" + alt="Dawn's logo: a sun rising behind a stylized mountain inspired by the WebGPU logo." + src="docs/imgs/dawn_logo_notext.png" + width="50%"> + + [](https://matrix.to/#/#webgpu-dawn:matrix.org) +</div> + +# Dawn Samples + +### WORK IN PROGRESS + +Samples for the Dawn library + +Dawn is an open-source and cross-platform implementation of the [WebGPU](https://webgpu.dev) standard. +More precisely it implements [`webgpu.h`](https://github.com/webgpu-native/webgpu-headers/blob/main/webgpu.h) that is a one-to-one mapping with the WebGPU IDL. + +## License + +BSD 3-Clause License, please see [LICENSE](/LICENSE). + +## Disclaimer + +This is not an officially supported Google product.
diff --git a/docs/building.md b/docs/building.md new file mode 100644 index 0000000..d864512 --- /dev/null +++ b/docs/building.md
@@ -0,0 +1,48 @@ +# Building Dawn Samples + +## System requirements + + * Git + * CMake (3.10.2 or later) (if desired) + * Python, for fetching dependencies + +- Linux + - The `pkg-config` command: + ```sh + # Install pkg-config on Ubuntu + sudo apt-get install pkg-config + ``` + +- Mac + - [Xcode](https://developer.apple.com/xcode/) 12.2+. + - The macOS 11.0 SDK. Run `xcode-select` to check whether you have it. + ```sh + ls `xcode-select -p`/Platforms/MacOSX.platform/Developer/SDKs + ``` + +## Get the code and its dependencies + +Use `tools/fetch_dawn_dependencies.py` to clone the dependencies' repositories: + +```sh +# Clone the repo as "dawn" +git clone https://dawn.googlesource.com/samples dawn-samples && cd dawn-samples + +# Fetch dependencies (lose equivalent of gclient sync) +python tools/fetch_dawn_dependencies.py +``` + +Use `python tools/fetch_dawn_dependencies.py -h` to know more about the available options. Contrary +to `depot_tools`, this scripts does not figure out option-dependent requirements automatically. + +TODO: Dawn samples do not yet support using the Chromium build system. + +## Build Dawn Samples + +### Compiling using CMake + make +```sh +mkdir -p out/Debug +cd out/Debug +cmake ../.. +make # -j N for N-way parallel build +```
diff --git a/docs/imgs/README.md b/docs/imgs/README.md new file mode 100644 index 0000000..60f42f4 --- /dev/null +++ b/docs/imgs/README.md
@@ -0,0 +1 @@ +Dawn's logo and derivatives found in this folder are under the [Creative Commons Attribution 4.0 International (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/) license.
diff --git a/docs/imgs/dawn_logo_notext.png b/docs/imgs/dawn_logo_notext.png new file mode 100644 index 0000000..f9732e5 --- /dev/null +++ b/docs/imgs/dawn_logo_notext.png Binary files differ
diff --git a/src/Placeholder.cpp b/src/Placeholder.cpp new file mode 100644 index 0000000..645bfc0 --- /dev/null +++ b/src/Placeholder.cpp
@@ -0,0 +1,31 @@ +// Copyright 2020 The Dawn & Tint Authors +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// CMake requires that targets contain at least on file. This file is used when we want to create +// empty targets. + +int someSymbolToMakeXCodeHappy = 0;
diff --git a/src/samples/Animometer.cpp b/src/samples/Animometer.cpp new file mode 100644 index 0000000..6a992e9 --- /dev/null +++ b/src/samples/Animometer.cpp
@@ -0,0 +1,208 @@ +// Copyright 2017 The Dawn & Tint Authors +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <cstdio> +#include <cstdlib> +#include <vector> + +#include "dawn/samples/SampleUtils.h" + +#include "dawn/utils/ComboRenderPipelineDescriptor.h" +#include "dawn/utils/SystemUtils.h" +#include "dawn/utils/Timer.h" +#include "dawn/utils/WGPUHelpers.h" + +wgpu::Device device; +wgpu::Queue queue; +wgpu::SwapChain swapchain; +wgpu::RenderPipeline pipeline; +wgpu::BindGroup bindGroup; +wgpu::Buffer ubo; + +float RandomFloat(float min, float max) { + // NOLINTNEXTLINE(runtime/threadsafe_fn) + float zeroOne = rand() / static_cast<float>(RAND_MAX); + return zeroOne * (max - min) + min; +} + +constexpr size_t kNumTriangles = 10000; + +// Aligned as minUniformBufferOffsetAlignment +struct alignas(256) ShaderData { + float scale; + float time; + float offsetX; + float offsetY; + float scalar; + float scalarOffset; +}; + +static std::vector<ShaderData> shaderData; + +void init() { + device = CreateCppDawnDevice(); + + queue = device.GetQueue(); + swapchain = GetSwapChain(); + + wgpu::ShaderModule vsModule = dawn::utils::CreateShaderModule(device, R"( + struct Constants { + scale : f32, + time : f32, + offsetX : f32, + offsetY : f32, + scalar : f32, + scalarOffset : f32, + }; + @group(0) @binding(0) var<uniform> c : Constants; + + struct VertexOut { + @location(0) v_color : vec4f, + @builtin(position) Position : vec4f, + }; + + @vertex fn main(@builtin(vertex_index) VertexIndex : u32) -> VertexOut { + var positions : array<vec4f, 3> = array( + vec4f( 0.0, 0.1, 0.0, 1.0), + vec4f(-0.1, -0.1, 0.0, 1.0), + vec4f( 0.1, -0.1, 0.0, 1.0) + ); + + var colors : array<vec4f, 3> = array( + vec4f(1.0, 0.0, 0.0, 1.0), + vec4f(0.0, 1.0, 0.0, 1.0), + vec4f(0.0, 0.0, 1.0, 1.0) + ); + + var position : vec4f = positions[VertexIndex]; + var color : vec4f = colors[VertexIndex]; + + // TODO(dawn:572): Revisit once modf has been reworked in WGSL. + var fade : f32 = c.scalarOffset + c.time * c.scalar / 10.0; + fade = fade - floor(fade); + if (fade < 0.5) { + fade = fade * 2.0; + } else { + fade = (1.0 - fade) * 2.0; + } + + var xpos : f32 = position.x * c.scale; + var ypos : f32 = position.y * c.scale; + let angle : f32 = 3.14159 * 2.0 * fade; + let xrot : f32 = xpos * cos(angle) - ypos * sin(angle); + let yrot : f32 = xpos * sin(angle) + ypos * cos(angle); + xpos = xrot + c.offsetX; + ypos = yrot + c.offsetY; + + var output : VertexOut; + output.v_color = vec4f(fade, 1.0 - fade, 0.0, 1.0) + color; + output.Position = vec4f(xpos, ypos, 0.0, 1.0); + return output; + })"); + + wgpu::ShaderModule fsModule = dawn::utils::CreateShaderModule(device, R"( + @fragment fn main(@location(0) v_color : vec4f) -> @location(0) vec4f { + return v_color; + })"); + + wgpu::BindGroupLayout bgl = dawn::utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform, true}}); + + dawn::utils::ComboRenderPipelineDescriptor descriptor; + descriptor.layout = dawn::utils::MakeBasicPipelineLayout(device, &bgl); + descriptor.vertex.module = vsModule; + descriptor.cFragment.module = fsModule; + descriptor.cTargets[0].format = GetPreferredSwapChainTextureFormat(); + + pipeline = device.CreateRenderPipeline(&descriptor); + + shaderData.resize(kNumTriangles); + for (auto& data : shaderData) { + data.scale = RandomFloat(0.2f, 0.4f); + data.time = 0.0; + data.offsetX = RandomFloat(-0.9f, 0.9f); + data.offsetY = RandomFloat(-0.9f, 0.9f); + data.scalar = RandomFloat(0.5f, 2.0f); + data.scalarOffset = RandomFloat(0.0f, 10.0f); + } + + wgpu::BufferDescriptor bufferDesc; + bufferDesc.size = kNumTriangles * sizeof(ShaderData); + bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform; + ubo = device.CreateBuffer(&bufferDesc); + + bindGroup = dawn::utils::MakeBindGroup(device, bgl, {{0, ubo, 0, sizeof(ShaderData)}}); +} + +int frameCount = 0; +void frame() { + wgpu::TextureView backbufferView = swapchain.GetCurrentTextureView(); + + for (auto& data : shaderData) { + data.time = frameCount / 60.0f; + } + queue.WriteBuffer(ubo, 0, shaderData.data(), kNumTriangles * sizeof(ShaderData)); + + dawn::utils::ComboRenderPassDescriptor renderPass({backbufferView}); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetPipeline(pipeline); + + for (size_t i = 0; i < kNumTriangles; i++) { + uint32_t offset = i * sizeof(ShaderData); + pass.SetBindGroup(0, bindGroup, 1, &offset); + pass.Draw(3); + } + + pass.End(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + swapchain.Present(); + DoFlush(); +} + +int main(int argc, const char* argv[]) { + if (!InitSample(argc, argv)) { + return 1; + } + init(); + + dawn::utils::Timer* timer = dawn::utils::CreateTimer(); + timer->Start(); + while (!ShouldQuit()) { + ProcessEvents(); + frameCount++; + frame(); + if (frameCount % 60 == 0) { + printf("FPS: %lf\n", 60.0 / timer->GetElapsedTime()); + timer->Start(); + } + } +}
diff --git a/src/samples/CHelloTriangle.cpp b/src/samples/CHelloTriangle.cpp new file mode 100644 index 0000000..67252ba --- /dev/null +++ b/src/samples/CHelloTriangle.cpp
@@ -0,0 +1,161 @@ +// Copyright 2017 The Dawn & Tint Authors +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "dawn/samples/SampleUtils.h" + +#include "dawn/utils/SystemUtils.h" +#include "dawn/utils/WGPUHelpers.h" + +WGPUDevice device; +WGPUQueue queue; +WGPUSwapChain swapchain; +WGPURenderPipeline pipeline; + +WGPUTextureFormat swapChainFormat; + +void init() { + device = CreateCppDawnDevice().MoveToCHandle(); + queue = wgpuDeviceGetQueue(device); + swapchain = GetSwapChain().MoveToCHandle(); + swapChainFormat = static_cast<WGPUTextureFormat>(GetPreferredSwapChainTextureFormat()); + + const char* vs = R"( + @vertex fn main( + @builtin(vertex_index) VertexIndex : u32 + ) -> @builtin(position) vec4f { + var pos = array( + vec2f( 0.0, 0.5), + vec2f(-0.5, -0.5), + vec2f( 0.5, -0.5) + ); + return vec4f(pos[VertexIndex], 0.0, 1.0); + })"; + WGPUShaderModule vsModule = dawn::utils::CreateShaderModule(device, vs).MoveToCHandle(); + + const char* fs = R"( + @fragment fn main() -> @location(0) vec4f { + return vec4f(1.0, 0.0, 0.0, 1.0); + })"; + WGPUShaderModule fsModule = dawn::utils::CreateShaderModule(device, fs).MoveToCHandle(); + + { + WGPURenderPipelineDescriptor descriptor = {}; + + // Fragment state + WGPUBlendState blend = {}; + blend.color.operation = WGPUBlendOperation_Add; + blend.color.srcFactor = WGPUBlendFactor_One; + blend.color.dstFactor = WGPUBlendFactor_One; + blend.alpha.operation = WGPUBlendOperation_Add; + blend.alpha.srcFactor = WGPUBlendFactor_One; + blend.alpha.dstFactor = WGPUBlendFactor_One; + + WGPUColorTargetState colorTarget = {}; + colorTarget.format = swapChainFormat; + colorTarget.blend = &blend; + colorTarget.writeMask = WGPUColorWriteMask_All; + + WGPUFragmentState fragment = {}; + fragment.module = fsModule; + fragment.entryPoint = "main"; + fragment.targetCount = 1; + fragment.targets = &colorTarget; + descriptor.fragment = &fragment; + + // Other state + descriptor.layout = nullptr; + descriptor.depthStencil = nullptr; + + descriptor.vertex.module = vsModule; + descriptor.vertex.entryPoint = "main"; + descriptor.vertex.bufferCount = 0; + descriptor.vertex.buffers = nullptr; + + descriptor.multisample.count = 1; + descriptor.multisample.mask = 0xFFFFFFFF; + descriptor.multisample.alphaToCoverageEnabled = false; + + descriptor.primitive.frontFace = WGPUFrontFace_CCW; + descriptor.primitive.cullMode = WGPUCullMode_None; + descriptor.primitive.topology = WGPUPrimitiveTopology_TriangleList; + descriptor.primitive.stripIndexFormat = WGPUIndexFormat_Undefined; + + pipeline = wgpuDeviceCreateRenderPipeline(device, &descriptor); + } + + wgpuShaderModuleRelease(vsModule); + wgpuShaderModuleRelease(fsModule); +} + +void frame() { + WGPUTextureView backbufferView = wgpuSwapChainGetCurrentTextureView(swapchain); + WGPURenderPassDescriptor renderpassInfo = {}; + WGPURenderPassColorAttachment colorAttachment = {}; + { + colorAttachment.view = backbufferView; + colorAttachment.resolveTarget = nullptr; + colorAttachment.clearValue = {0.0f, 0.0f, 0.0f, 0.0f}; + colorAttachment.loadOp = WGPULoadOp_Clear; + colorAttachment.storeOp = WGPUStoreOp_Store; + renderpassInfo.colorAttachmentCount = 1; + renderpassInfo.colorAttachments = &colorAttachment; + renderpassInfo.depthStencilAttachment = nullptr; + } + WGPUCommandBuffer commands; + { + WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr); + + WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &renderpassInfo); + wgpuRenderPassEncoderSetPipeline(pass, pipeline); + wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); + wgpuRenderPassEncoderEnd(pass); + wgpuRenderPassEncoderRelease(pass); + + commands = wgpuCommandEncoderFinish(encoder, nullptr); + wgpuCommandEncoderRelease(encoder); + } + + wgpuQueueSubmit(queue, 1, &commands); + wgpuCommandBufferRelease(commands); + wgpuSwapChainPresent(swapchain); + wgpuTextureViewRelease(backbufferView); + + DoFlush(); +} + +int main(int argc, const char* argv[]) { + if (!InitSample(argc, argv)) { + return 1; + } + init(); + + while (!ShouldQuit()) { + ProcessEvents(); + frame(); + dawn::utils::USleep(16000); + } +}
diff --git a/src/samples/CMakeLists.txt b/src/samples/CMakeLists.txt new file mode 100644 index 0000000..4a1f3dd --- /dev/null +++ b/src/samples/CMakeLists.txt
@@ -0,0 +1,64 @@ +# Copyright 2020 The Dawn & Tint Authors +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +add_library(dawn_sample_utils STATIC ${SAMPLES_PLACEHOLDER_FILE}) +target_include_directories(dawn_sample_utils INTERFACE + "${SAMPLES_SRC_DIR}" +) + +common_compile_options(dawn_sample_utils) +target_sources(dawn_sample_utils PRIVATE + "SampleUtils.cpp" + "SampleUtils.h" +) +target_link_libraries(dawn_sample_utils PUBLIC + dawn_internal_config + dawncpp + dawn_proc + dawn_common + dawn_glfw + dawn_native + dawn_wire + dawn_utils + glfw +) + +add_executable(CppHelloTriangle "CppHelloTriangle.cpp") +common_compile_options(CppHelloTriangle) +target_link_libraries(CppHelloTriangle dawn_sample_utils) + +add_executable(CHelloTriangle "CHelloTriangle.cpp") +common_compile_options(CHelloTriangle) +target_link_libraries(CHelloTriangle dawn_sample_utils) + +add_executable(ComputeBoids "ComputeBoids.cpp") +common_compile_options(ComputeBoids) +target_link_libraries(ComputeBoids dawn_sample_utils) + +add_executable(Animometer "Animometer.cpp") +common_compile_options(Animometer) +target_link_libraries(Animometer dawn_sample_utils)
diff --git a/src/samples/ComputeBoids.cpp b/src/samples/ComputeBoids.cpp new file mode 100644 index 0000000..6062aff --- /dev/null +++ b/src/samples/ComputeBoids.cpp
@@ -0,0 +1,341 @@ +// Copyright 2017 The Dawn & Tint Authors +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <array> +#include <cstring> +#include <random> +#include <vector> + +#include "dawn/samples/SampleUtils.h" + +#include "dawn/utils/ComboRenderPipelineDescriptor.h" +#include "dawn/utils/SystemUtils.h" +#include "dawn/utils/WGPUHelpers.h" + +wgpu::Device device; +wgpu::Queue queue; +wgpu::SwapChain swapchain; +wgpu::TextureView depthStencilView; + +wgpu::Buffer modelBuffer; +std::array<wgpu::Buffer, 2> particleBuffers; + +wgpu::RenderPipeline renderPipeline; + +wgpu::Buffer updateParams; +wgpu::ComputePipeline updatePipeline; +std::array<wgpu::BindGroup, 2> updateBGs; + +size_t pingpong = 0; + +static const uint32_t kNumParticles = 1024; + +struct Particle { + std::array<float, 2> pos; + std::array<float, 2> vel; +}; + +struct SimParams { + float deltaT; + float rule1Distance; + float rule2Distance; + float rule3Distance; + float rule1Scale; + float rule2Scale; + float rule3Scale; + int particleCount; +}; + +void initBuffers() { + std::array<std::array<float, 2>, 3> model = {{ + {-0.01, -0.02}, + {0.01, -0.02}, + {0.00, 0.02}, + }}; + modelBuffer = + dawn::utils::CreateBufferFromData(device, &model, sizeof(model), wgpu::BufferUsage::Vertex); + + SimParams params = {0.04f, 0.1f, 0.025f, 0.025f, 0.02f, 0.05f, 0.005f, kNumParticles}; + updateParams = dawn::utils::CreateBufferFromData(device, ¶ms, sizeof(params), + wgpu::BufferUsage::Uniform); + + std::vector<Particle> initialParticles(kNumParticles); + { + std::mt19937 generator; + std::uniform_real_distribution<float> dist(-1.0f, 1.0f); + for (auto& p : initialParticles) { + p.pos = {dist(generator), dist(generator)}; + p.vel = {dist(generator) * 0.1f, dist(generator) * 0.1f}; + } + } + + for (size_t i = 0; i < 2; i++) { + wgpu::BufferDescriptor descriptor; + descriptor.size = sizeof(Particle) * kNumParticles; + descriptor.usage = + wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Storage; + particleBuffers[i] = device.CreateBuffer(&descriptor); + + queue.WriteBuffer(particleBuffers[i], 0, + reinterpret_cast<uint8_t*>(initialParticles.data()), + sizeof(Particle) * kNumParticles); + } +} + +void initRender() { + wgpu::ShaderModule vsModule = dawn::utils::CreateShaderModule(device, R"( + struct VertexIn { + @location(0) a_particlePos : vec2f, + @location(1) a_particleVel : vec2f, + @location(2) a_pos : vec2f, + }; + + @vertex + fn main(input : VertexIn) -> @builtin(position) vec4f { + var angle : f32 = -atan2(input.a_particleVel.x, input.a_particleVel.y); + var pos : vec2f = vec2f( + (input.a_pos.x * cos(angle)) - (input.a_pos.y * sin(angle)), + (input.a_pos.x * sin(angle)) + (input.a_pos.y * cos(angle))); + return vec4f(pos + input.a_particlePos, 0.0, 1.0); + } + )"); + + wgpu::ShaderModule fsModule = dawn::utils::CreateShaderModule(device, R"( + @fragment + fn main() -> @location(0) vec4f { + return vec4f(1.0, 1.0, 1.0, 1.0); + } + )"); + + depthStencilView = CreateDefaultDepthStencilView(device); + + dawn::utils::ComboRenderPipelineDescriptor descriptor; + + descriptor.vertex.module = vsModule; + descriptor.vertex.bufferCount = 2; + descriptor.cBuffers[0].arrayStride = sizeof(Particle); + descriptor.cBuffers[0].stepMode = wgpu::VertexStepMode::Instance; + descriptor.cBuffers[0].attributeCount = 2; + descriptor.cAttributes[0].offset = offsetof(Particle, pos); + descriptor.cAttributes[0].format = wgpu::VertexFormat::Float32x2; + descriptor.cAttributes[1].shaderLocation = 1; + descriptor.cAttributes[1].offset = offsetof(Particle, vel); + descriptor.cAttributes[1].format = wgpu::VertexFormat::Float32x2; + descriptor.cBuffers[1].arrayStride = 2 * sizeof(float); + descriptor.cBuffers[1].attributeCount = 1; + descriptor.cBuffers[1].attributes = &descriptor.cAttributes[2]; + descriptor.cAttributes[2].shaderLocation = 2; + descriptor.cAttributes[2].format = wgpu::VertexFormat::Float32x2; + + descriptor.cFragment.module = fsModule; + descriptor.EnableDepthStencil(wgpu::TextureFormat::Depth24PlusStencil8); + descriptor.cTargets[0].format = GetPreferredSwapChainTextureFormat(); + + renderPipeline = device.CreateRenderPipeline(&descriptor); +} + +void initSim() { + wgpu::ShaderModule module = dawn::utils::CreateShaderModule(device, R"( + struct Particle { + pos : vec2f, + vel : vec2f, + }; + struct SimParams { + deltaT : f32, + rule1Distance : f32, + rule2Distance : f32, + rule3Distance : f32, + rule1Scale : f32, + rule2Scale : f32, + rule3Scale : f32, + particleCount : u32, + }; + struct Particles { + particles : array<Particle>, + }; + @binding(0) @group(0) var<uniform> params : SimParams; + @binding(1) @group(0) var<storage, read_write> particlesA : Particles; + @binding(2) @group(0) var<storage, read_write> particlesB : Particles; + + // https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp + @compute @workgroup_size(64) + fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3u) { + var index : u32 = GlobalInvocationID.x; + if (index >= params.particleCount) { + return; + } + var vPos : vec2f = particlesA.particles[index].pos; + var vVel : vec2f = particlesA.particles[index].vel; + var cMass : vec2f = vec2f(0.0, 0.0); + var cVel : vec2f = vec2f(0.0, 0.0); + var colVel : vec2f = vec2f(0.0, 0.0); + var cMassCount : u32 = 0u; + var cVelCount : u32 = 0u; + var pos : vec2f; + var vel : vec2f; + + for (var i : u32 = 0u; i < params.particleCount; i = i + 1u) { + if (i == index) { + continue; + } + + pos = particlesA.particles[i].pos.xy; + vel = particlesA.particles[i].vel.xy; + if (distance(pos, vPos) < params.rule1Distance) { + cMass = cMass + pos; + cMassCount = cMassCount + 1u; + } + if (distance(pos, vPos) < params.rule2Distance) { + colVel = colVel - (pos - vPos); + } + if (distance(pos, vPos) < params.rule3Distance) { + cVel = cVel + vel; + cVelCount = cVelCount + 1u; + } + } + + if (cMassCount > 0u) { + cMass = (cMass / vec2f(f32(cMassCount), f32(cMassCount))) - vPos; + } + + if (cVelCount > 0u) { + cVel = cVel / vec2f(f32(cVelCount), f32(cVelCount)); + } + vVel = vVel + (cMass * params.rule1Scale) + (colVel * params.rule2Scale) + + (cVel * params.rule3Scale); + + // clamp velocity for a more pleasing simulation + vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1); + // kinematic update + vPos = vPos + (vVel * params.deltaT); + + // Wrap around boundary + if (vPos.x < -1.0) { + vPos.x = 1.0; + } + if (vPos.x > 1.0) { + vPos.x = -1.0; + } + if (vPos.y < -1.0) { + vPos.y = 1.0; + } + if (vPos.y > 1.0) { + vPos.y = -1.0; + } + + // Write back + particlesB.particles[index].pos = vPos; + particlesB.particles[index].vel = vVel; + return; + } + )"); + + auto bgl = dawn::utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform}, + {1, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage}, + {2, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage}, + }); + + wgpu::PipelineLayout pl = dawn::utils::MakeBasicPipelineLayout(device, &bgl); + + wgpu::ComputePipelineDescriptor csDesc; + csDesc.layout = pl; + csDesc.compute.module = module; + csDesc.compute.entryPoint = "main"; + updatePipeline = device.CreateComputePipeline(&csDesc); + + for (uint32_t i = 0; i < 2; ++i) { + updateBGs[i] = dawn::utils::MakeBindGroup( + device, bgl, + { + {0, updateParams, 0, sizeof(SimParams)}, + {1, particleBuffers[i], 0, kNumParticles * sizeof(Particle)}, + {2, particleBuffers[(i + 1) % 2], 0, kNumParticles * sizeof(Particle)}, + }); + } +} + +wgpu::CommandBuffer createCommandBuffer(const wgpu::TextureView backbufferView, size_t i) { + auto& bufferDst = particleBuffers[(i + 1) % 2]; + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + { + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(updatePipeline); + pass.SetBindGroup(0, updateBGs[i]); + pass.DispatchWorkgroups(kNumParticles / 64); + pass.End(); + } + + { + dawn::utils::ComboRenderPassDescriptor renderPass({backbufferView}, depthStencilView); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetPipeline(renderPipeline); + pass.SetVertexBuffer(0, bufferDst); + pass.SetVertexBuffer(1, modelBuffer); + pass.Draw(3, kNumParticles); + pass.End(); + } + + return encoder.Finish(); +} + +void init() { + device = CreateCppDawnDevice(); + + queue = device.GetQueue(); + swapchain = GetSwapChain(); + + initBuffers(); + initRender(); + initSim(); +} + +void frame() { + wgpu::TextureView backbufferView = swapchain.GetCurrentTextureView(); + + wgpu::CommandBuffer commandBuffer = createCommandBuffer(backbufferView, pingpong); + queue.Submit(1, &commandBuffer); + swapchain.Present(); + DoFlush(); + + pingpong = (pingpong + 1) % 2; +} + +int main(int argc, const char* argv[]) { + if (!InitSample(argc, argv)) { + return 1; + } + init(); + + while (!ShouldQuit()) { + ProcessEvents(); + frame(); + dawn::utils::USleep(16000); + } +}
diff --git a/src/samples/CppHelloTriangle.cpp b/src/samples/CppHelloTriangle.cpp new file mode 100644 index 0000000..b1b47a5 --- /dev/null +++ b/src/samples/CppHelloTriangle.cpp
@@ -0,0 +1,195 @@ +// Copyright 2017 The Dawn & Tint Authors +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <vector> + +#include "dawn/samples/SampleUtils.h" + +#include "dawn/utils/ComboRenderPipelineDescriptor.h" +#include "dawn/utils/SystemUtils.h" +#include "dawn/utils/WGPUHelpers.h" + +wgpu::Device device; + +wgpu::Buffer indexBuffer; +wgpu::Buffer vertexBuffer; + +wgpu::Texture texture; +wgpu::Sampler sampler; + +wgpu::Queue queue; +wgpu::SwapChain swapchain; +wgpu::TextureView depthStencilView; +wgpu::RenderPipeline pipeline; +wgpu::BindGroup bindGroup; + +void initBuffers() { + static const uint32_t indexData[3] = { + 0, + 1, + 2, + }; + indexBuffer = dawn::utils::CreateBufferFromData(device, indexData, sizeof(indexData), + wgpu::BufferUsage::Index); + + static const float vertexData[12] = { + 0.0f, 0.5f, 0.0f, 1.0f, -0.5f, -0.5f, 0.0f, 1.0f, 0.5f, -0.5f, 0.0f, 1.0f, + }; + vertexBuffer = dawn::utils::CreateBufferFromData(device, vertexData, sizeof(vertexData), + wgpu::BufferUsage::Vertex); +} + +void initTextures() { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = 1024; + descriptor.size.height = 1024; + descriptor.size.depthOrArrayLayers = 1; + descriptor.sampleCount = 1; + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding; + texture = device.CreateTexture(&descriptor); + + sampler = device.CreateSampler(); + + // Initialize the texture with arbitrary data until we can load images + std::vector<uint8_t> data(4 * 1024 * 1024, 0); + for (size_t i = 0; i < data.size(); ++i) { + data[i] = static_cast<uint8_t>(i % 253); + } + + wgpu::Buffer stagingBuffer = dawn::utils::CreateBufferFromData( + device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc); + wgpu::ImageCopyBuffer imageCopyBuffer = + dawn::utils::CreateImageCopyBuffer(stagingBuffer, 0, 4 * 1024); + wgpu::ImageCopyTexture imageCopyTexture = + dawn::utils::CreateImageCopyTexture(texture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {1024, 1024, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size); + + wgpu::CommandBuffer copy = encoder.Finish(); + queue.Submit(1, ©); +} + +void init() { + device = CreateCppDawnDevice(); + + queue = device.GetQueue(); + swapchain = GetSwapChain(); + + initBuffers(); + initTextures(); + + wgpu::ShaderModule vsModule = dawn::utils::CreateShaderModule(device, R"( + @vertex fn main(@location(0) pos : vec4f) + -> @builtin(position) vec4f { + return pos; + })"); + + wgpu::ShaderModule fsModule = dawn::utils::CreateShaderModule(device, R"( + @group(0) @binding(0) var mySampler: sampler; + @group(0) @binding(1) var myTexture : texture_2d<f32>; + + @fragment fn main(@builtin(position) FragCoord : vec4f) + -> @location(0) vec4f { + return textureSample(myTexture, mySampler, FragCoord.xy / vec2f(640.0, 480.0)); + })"); + + auto bgl = dawn::utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}, + {1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}, + }); + + wgpu::PipelineLayout pl = dawn::utils::MakeBasicPipelineLayout(device, &bgl); + + depthStencilView = CreateDefaultDepthStencilView(device); + + dawn::utils::ComboRenderPipelineDescriptor descriptor; + descriptor.layout = dawn::utils::MakeBasicPipelineLayout(device, &bgl); + descriptor.vertex.module = vsModule; + descriptor.vertex.bufferCount = 1; + descriptor.cBuffers[0].arrayStride = 4 * sizeof(float); + descriptor.cBuffers[0].attributeCount = 1; + descriptor.cAttributes[0].format = wgpu::VertexFormat::Float32x4; + descriptor.cFragment.module = fsModule; + descriptor.cTargets[0].format = GetPreferredSwapChainTextureFormat(); + descriptor.EnableDepthStencil(wgpu::TextureFormat::Depth24PlusStencil8); + + pipeline = device.CreateRenderPipeline(&descriptor); + + wgpu::TextureView view = texture.CreateView(); + + bindGroup = dawn::utils::MakeBindGroup(device, bgl, {{0, sampler}, {1, view}}); +} + +struct { + uint32_t a; + float b; +} s; +void frame() { + s.a = (s.a + 1) % 256; + s.b += 0.02f; + if (s.b >= 1.0f) { + s.b = 0.0f; + } + + wgpu::TextureView backbufferView = swapchain.GetCurrentTextureView(); + dawn::utils::ComboRenderPassDescriptor renderPass({backbufferView}, depthStencilView); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + pass.SetVertexBuffer(0, vertexBuffer); + pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32); + pass.DrawIndexed(3); + pass.End(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + swapchain.Present(); + DoFlush(); +} + +int main(int argc, const char* argv[]) { + if (!InitSample(argc, argv)) { + return 1; + } + init(); + + while (!ShouldQuit()) { + ProcessEvents(); + frame(); + dawn::utils::USleep(16000); + } +}
diff --git a/src/samples/SampleUtils.cpp b/src/samples/SampleUtils.cpp new file mode 100644 index 0000000..2def23b --- /dev/null +++ b/src/samples/SampleUtils.cpp
@@ -0,0 +1,443 @@ +// Copyright 2017 The Dawn & Tint Authors +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "SampleUtils.h" + +#include <algorithm> +#include <cstring> +#include <memory> +#include <string> +#include <string_view> +#include <vector> + +#include "GLFW/glfw3.h" +#include "dawn/common/Assert.h" +#include "dawn/common/Log.h" +#include "dawn/common/Platform.h" +#include "dawn/common/SystemUtils.h" +#include "dawn/dawn_proc.h" +#include "dawn/native/DawnNative.h" +#include "dawn/utils/TerribleCommandBuffer.h" +#include "dawn/wire/WireClient.h" +#include "dawn/wire/WireServer.h" +#include "webgpu/webgpu_glfw.h" + +void PrintDeviceError(WGPUErrorType errorType, const char* message, void*) { + const char* errorTypeName = ""; + switch (errorType) { + case WGPUErrorType_Validation: + errorTypeName = "Validation"; + break; + case WGPUErrorType_OutOfMemory: + errorTypeName = "Out of memory"; + break; + case WGPUErrorType_Unknown: + errorTypeName = "Unknown"; + break; + case WGPUErrorType_DeviceLost: + errorTypeName = "Device lost"; + break; + default: + DAWN_UNREACHABLE(); + return; + } + dawn::ErrorLog() << errorTypeName << " error: " << message; +} + +void DeviceLostCallback(WGPUDeviceLostReason reason, const char* message, void*) { + dawn::ErrorLog() << "Device lost: " << message; +} + +void PrintGLFWError(int code, const char* message) { + dawn::ErrorLog() << "GLFW error: " << code << " - " << message; +} + +void DeviceLogCallback(WGPULoggingType type, const char* message, void*) { + dawn::ErrorLog() << "Device log: " << message; +} + +enum class CmdBufType { + None, + Terrible, + // TODO(cwallez@chromium.org): double terrible cmdbuf +}; + +// Default to D3D12, Metal, Vulkan, OpenGL in that order as D3D12 and Metal are the preferred on +// their respective platforms, and Vulkan is preferred to OpenGL +#if defined(DAWN_ENABLE_BACKEND_D3D12) +static wgpu::BackendType backendType = wgpu::BackendType::D3D12; +#elif defined(DAWN_ENABLE_BACKEND_D3D11) +static wgpu::BackendType backendType = wgpu::BackendType::D3D11; +#elif defined(DAWN_ENABLE_BACKEND_METAL) +static wgpu::BackendType backendType = wgpu::BackendType::Metal; +#elif defined(DAWN_ENABLE_BACKEND_VULKAN) +static wgpu::BackendType backendType = wgpu::BackendType::Vulkan; +#elif defined(DAWN_ENABLE_BACKEND_OPENGLES) +static wgpu::BackendType backendType = wgpu::BackendType::OpenGLES; +#elif defined(DAWN_ENABLE_BACKEND_DESKTOP_GL) +static wgpu::BackendType backendType = wgpu::BackendType::OpenGL; +#else +#error +#endif + +static wgpu::AdapterType adapterType = wgpu::AdapterType::Unknown; + +static std::vector<std::string> enableToggles; +static std::vector<std::string> disableToggles; + +static CmdBufType cmdBufType = CmdBufType::Terrible; +static std::unique_ptr<dawn::native::Instance> instance; +static wgpu::SwapChain swapChain; + +static GLFWwindow* window = nullptr; + +static dawn::wire::WireServer* wireServer = nullptr; +static dawn::wire::WireClient* wireClient = nullptr; +static dawn::utils::TerribleCommandBuffer* c2sBuf = nullptr; +static dawn::utils::TerribleCommandBuffer* s2cBuf = nullptr; + +static constexpr uint32_t kWidth = 640; +static constexpr uint32_t kHeight = 480; + +wgpu::Device CreateCppDawnDevice() { + dawn::ScopedEnvironmentVar angleDefaultPlatform; + if (dawn::GetEnvironmentVar("ANGLE_DEFAULT_PLATFORM").first.empty()) { + angleDefaultPlatform.Set("ANGLE_DEFAULT_PLATFORM", "swiftshader"); + } + + glfwSetErrorCallback(PrintGLFWError); + if (!glfwInit()) { + return wgpu::Device(); + } + + // Create the test window with no client API. + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE); + window = glfwCreateWindow(kWidth, kHeight, "Dawn window", nullptr, nullptr); + if (!window) { + return wgpu::Device(); + } + + WGPUInstanceDescriptor instanceDescriptor{}; + instanceDescriptor.features.timedWaitAnyEnable = true; + instance = std::make_unique<dawn::native::Instance>(&instanceDescriptor); + + wgpu::RequestAdapterOptions options = {}; + options.backendType = backendType; + + // Get an adapter for the backend to use, and create the device. + auto adapters = instance->EnumerateAdapters(&options); + wgpu::DawnAdapterPropertiesPowerPreference power_props{}; + wgpu::AdapterProperties adapterProperties{}; + adapterProperties.nextInChain = &power_props; + // Find the first adapter which satisfies the adapterType requirement. + auto isAdapterType = [&adapterProperties](const auto& adapter) -> bool { + // picks the first adapter when adapterType is unknown. + if (adapterType == wgpu::AdapterType::Unknown) { + return true; + } + adapter.GetProperties(&adapterProperties); + return adapterProperties.adapterType == adapterType; + }; + auto preferredAdapter = std::find_if(adapters.begin(), adapters.end(), isAdapterType); + if (preferredAdapter == adapters.end()) { + fprintf(stderr, "Failed to find an adapter! Please try another adapter type.\n"); + return wgpu::Device(); + } + + std::vector<const char*> enableToggleNames; + std::vector<const char*> disabledToggleNames; + for (const std::string& toggle : enableToggles) { + enableToggleNames.push_back(toggle.c_str()); + } + + for (const std::string& toggle : disableToggles) { + disabledToggleNames.push_back(toggle.c_str()); + } + WGPUDawnTogglesDescriptor toggles; + toggles.chain.sType = WGPUSType_DawnTogglesDescriptor; + toggles.chain.next = nullptr; + toggles.enabledToggles = enableToggleNames.data(); + toggles.enabledToggleCount = enableToggleNames.size(); + toggles.disabledToggles = disabledToggleNames.data(); + toggles.disabledToggleCount = disabledToggleNames.size(); + + WGPUDeviceDescriptor deviceDesc = {}; + deviceDesc.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&toggles); + + WGPUDevice backendDevice = preferredAdapter->CreateDevice(&deviceDesc); + DawnProcTable backendProcs = dawn::native::GetProcs(); + + // Create the swapchain + auto surfaceChainedDesc = wgpu::glfw::SetupWindowAndGetSurfaceDescriptor(window); + WGPUSurfaceDescriptor surfaceDesc; + surfaceDesc.nextInChain = reinterpret_cast<WGPUChainedStruct*>(surfaceChainedDesc.get()); + WGPUSurface surface = backendProcs.instanceCreateSurface(instance->Get(), &surfaceDesc); + + WGPUSwapChainDescriptor swapChainDesc = {}; + swapChainDesc.usage = WGPUTextureUsage_RenderAttachment; + swapChainDesc.format = static_cast<WGPUTextureFormat>(GetPreferredSwapChainTextureFormat()); + swapChainDesc.width = kWidth; + swapChainDesc.height = kHeight; + swapChainDesc.presentMode = WGPUPresentMode_Mailbox; + WGPUSwapChain backendSwapChain = + backendProcs.deviceCreateSwapChain(backendDevice, surface, &swapChainDesc); + + // Choose whether to use the backend procs and devices/swapchains directly, or set up the wire. + WGPUDevice cDevice = nullptr; + DawnProcTable procs; + + switch (cmdBufType) { + case CmdBufType::None: + procs = backendProcs; + cDevice = backendDevice; + swapChain = wgpu::SwapChain::Acquire(backendSwapChain); + break; + + case CmdBufType::Terrible: { + c2sBuf = new dawn::utils::TerribleCommandBuffer(); + s2cBuf = new dawn::utils::TerribleCommandBuffer(); + + dawn::wire::WireServerDescriptor serverDesc = {}; + serverDesc.procs = &backendProcs; + serverDesc.serializer = s2cBuf; + + wireServer = new dawn::wire::WireServer(serverDesc); + c2sBuf->SetHandler(wireServer); + + dawn::wire::WireClientDescriptor clientDesc = {}; + clientDesc.serializer = c2sBuf; + + wireClient = new dawn::wire::WireClient(clientDesc); + procs = dawn::wire::client::GetProcs(); + s2cBuf->SetHandler(wireClient); + + auto deviceReservation = wireClient->ReserveDevice(); + wireServer->InjectDevice(backendDevice, deviceReservation.id, + deviceReservation.generation); + cDevice = deviceReservation.device; + + auto swapChainReservation = wireClient->ReserveSwapChain(cDevice, &swapChainDesc); + wireServer->InjectSwapChain(backendSwapChain, swapChainReservation.id, + swapChainReservation.generation, deviceReservation.id, + deviceReservation.generation); + swapChain = wgpu::SwapChain::Acquire(swapChainReservation.swapchain); + } break; + } + + dawnProcSetProcs(&procs); + procs.deviceSetUncapturedErrorCallback(cDevice, PrintDeviceError, nullptr); + procs.deviceSetDeviceLostCallback(cDevice, DeviceLostCallback, nullptr); + procs.deviceSetLoggingCallback(cDevice, DeviceLogCallback, nullptr); + return wgpu::Device::Acquire(cDevice); +} + +wgpu::TextureFormat GetPreferredSwapChainTextureFormat() { + // TODO(dawn:1362): Return the adapter's preferred format when implemented. + return wgpu::TextureFormat::BGRA8Unorm; +} + +wgpu::SwapChain GetSwapChain() { + return swapChain; +} + +wgpu::TextureView CreateDefaultDepthStencilView(const wgpu::Device& device) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = kWidth; + descriptor.size.height = kHeight; + descriptor.size.depthOrArrayLayers = 1; + descriptor.sampleCount = 1; + descriptor.format = wgpu::TextureFormat::Depth24PlusStencil8; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::RenderAttachment; + auto depthStencilTexture = device.CreateTexture(&descriptor); + return depthStencilTexture.CreateView(); +} + +bool InitSample(int argc, const char** argv) { + for (int i = 1; i < argc; i++) { + std::string_view arg(argv[i]); + std::string_view opt, value; + + static constexpr struct Option { + const char* shortOpt; + const char* longOpt; + bool hasValue; + } options[] = { + {"-b", "--backend=", true}, {"-c", "--cmd-buf=", true}, + {"-e", "--enable-toggle=", true}, {"-d", "--disable-toggle=", true}, + {"-a", "--adapter-type=", true}, {"-h", "--help", false}, + }; + + for (const Option& option : options) { + if (!option.hasValue) { + if (arg == option.shortOpt || arg == option.longOpt) { + opt = option.shortOpt; + break; + } + continue; + } + + if (arg == option.shortOpt) { + opt = option.shortOpt; + if (++i < argc) { + value = argv[i]; + } + break; + } + + if (arg.rfind(option.longOpt, 0) == 0) { + opt = option.shortOpt; + if (option.hasValue) { + value = arg.substr(strlen(option.longOpt)); + } + break; + } + } + + if (opt == "-b") { + if (value == "d3d11") { + backendType = wgpu::BackendType::D3D11; + continue; + } + if (value == "d3d12") { + backendType = wgpu::BackendType::D3D12; + continue; + } + if (value == "metal") { + backendType = wgpu::BackendType::Metal; + continue; + } + if (value == "null") { + backendType = wgpu::BackendType::Null; + continue; + } + if (value == "opengl") { + backendType = wgpu::BackendType::OpenGL; + continue; + } + if (value == "opengles") { + backendType = wgpu::BackendType::OpenGLES; + continue; + } + if (value == "vulkan") { + backendType = wgpu::BackendType::Vulkan; + continue; + } + fprintf(stderr, + "--backend expects a backend name (opengl, opengles, metal, d3d12, null, " + "vulkan)\n"); + return false; + } + + if (opt == "-c") { + if (value == "none") { + cmdBufType = CmdBufType::None; + continue; + } + if (value == "terrible") { + cmdBufType = CmdBufType::Terrible; + continue; + } + fprintf(stderr, "--command-buffer expects a command buffer name (none, terrible)\n"); + return false; + } + + if (opt == "-e") { + enableToggles.push_back(std::string(value)); + continue; + } + + if (opt == "-d") { + disableToggles.push_back(std::string(value)); + continue; + } + + if (opt == "-a") { + if (value == "discrete") { + adapterType = wgpu::AdapterType::DiscreteGPU; + continue; + } + if (value == "integrated") { + adapterType = wgpu::AdapterType::IntegratedGPU; + continue; + } + if (value == "cpu") { + adapterType = wgpu::AdapterType::CPU; + continue; + } + fprintf(stderr, "--adapter-type expects an adapter type (discrete, integrated, cpu)\n"); + return false; + } + + if (opt == "-h") { + printf( + "Usage: %s [-b BACKEND] [-c COMMAND_BUFFER] [-e TOGGLE] [-d TOGGLE] [-a " + "ADAPTER]\n", + argv[0]); + printf(" BACKEND is one of: d3d12, metal, null, opengl, opengles, vulkan\n"); + printf(" COMMAND_BUFFER is one of: none, terrible\n"); + printf(" TOGGLE is device toggle name to enable or disable\n"); + printf(" ADAPTER is one of: discrete, integrated, cpu\n"); + return false; + } + } + + // TODO(dawn:810): Reenable once the OpenGL(ES) backend is able to create its own context such + // that it can use surface-based swapchains. + if (backendType == wgpu::BackendType::OpenGL || backendType == wgpu::BackendType::OpenGLES) { + fprintf(stderr, + "The OpenGL(ES) backend is temporarily not supported for samples. See " + "https://crbug.com/dawn/810"); + return false; + } + + return true; +} + +void DoFlush() { + if (cmdBufType == CmdBufType::Terrible) { + bool c2sSuccess = c2sBuf->Flush(); + bool s2cSuccess = s2cBuf->Flush(); + + DAWN_ASSERT(c2sSuccess && s2cSuccess); + } + glfwPollEvents(); +} + +bool ShouldQuit() { + return glfwWindowShouldClose(window); +} + +GLFWwindow* GetGLFWWindow() { + return window; +} + +void ProcessEvents() { + dawn::native::InstanceProcessEvents(instance->Get()); +}
diff --git a/src/samples/SampleUtils.h b/src/samples/SampleUtils.h new file mode 100644 index 0000000..fb7e289 --- /dev/null +++ b/src/samples/SampleUtils.h
@@ -0,0 +1,46 @@ +// Copyright 2017 The Dawn & Tint Authors +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef SRC_DAWN_SAMPLES_SAMPLEUTILS_H_ +#define SRC_DAWN_SAMPLES_SAMPLEUTILS_H_ + +#include "dawn/webgpu_cpp.h" + +bool InitSample(int argc, const char** argv); +void DoFlush(); +bool ShouldQuit(); + +struct GLFWwindow; +struct GLFWwindow* GetGLFWWindow(); + +wgpu::Device CreateCppDawnDevice(); +wgpu::TextureFormat GetPreferredSwapChainTextureFormat(); +wgpu::SwapChain GetSwapChain(); +wgpu::TextureView CreateDefaultDepthStencilView(const wgpu::Device& device); +void ProcessEvents(); + +#endif // SRC_DAWN_SAMPLES_SAMPLEUTILS_H_
diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt new file mode 100644 index 0000000..d7bfc62 --- /dev/null +++ b/third_party/CMakeLists.txt
@@ -0,0 +1,66 @@ +# Copyright 2022 The Dawn & Tint Authors +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Don't build testing in third_party dependencies +set(BUILD_TESTING OFF) + +# fetch_dawn_dependencies.py is an alternative to using depot_tools +# It is particularly interesting when building dawn as a subdirectory in +# a parent project that does not want to use depot_tools. +if (${DAWN_FETCH_DEPENDENCIES}) + find_package(PythonInterp 3 REQUIRED) + + set(EXTRA_FETCH_ARGS) + + message(STATUS "Running fetch_dawn_dependencies:") + execute_process( + COMMAND + ${PYTHON_EXECUTABLE} + "${PROJECT_SOURCE_DIR}/tools/fetch_dawn_dependencies.py" + --directory ${PROJECT_SOURCE_DIR} + ${EXTRA_FETCH_ARGS} + ) +endif () + +if (NOT TARGET dawn) + set(DAWN_BUILD_SAMPLES OFF CACHE BOOL "" FORCE) + set(DAWN_USE_GLFW ON CACHE BOOL "" FORCE) + + message(STATUS "Dawn Samples: using Dawn at ${SAMPLES_DAWN_DIR}") + add_subdirectory(${SAMPLES_DAWN_DIR} "${CMAKE_CURRENT_BINARY_DIR}/dawn") +endif() + +if (NOT TARGET glfw) + set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) + set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) + set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + set(GLFW_BUILD_X11 ${DAWN_USE_X11} CACHE BOOL "" FORCE) + set(GLFW_BUILD_WAYLAND ${DAWN_USE_WAYLAND} CACHE BOOL "" FORCE) + + message(STATUS "Dawn Samples: using GLFW at ${SAMPLES_GLFW_DIR}") + add_subdirectory(${SAMPLES_GLFW_DIR} "${CMAKE_CURRENT_BINARY_DIR}/glfw") +endif()
diff --git a/tools/fetch_dawn_dependencies.py b/tools/fetch_dawn_dependencies.py new file mode 100644 index 0000000..e5b46ea --- /dev/null +++ b/tools/fetch_dawn_dependencies.py
@@ -0,0 +1,215 @@ +# Copyright 2023 The Dawn & Tint Authors +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" +Helper script to download Dawn's source dependencies without the need to +install depot_tools by manually. This script implements a subset of +`gclient sync`. + +This helps embedders, for example through CMake, get all the sources with +a single add_subdirectory call (or FetchContent) instead of more complex setups + +Note that this script executes blindly the content of DEPS file, run it only on +a project that you trust not to contain malicious DEPS files. +""" + +import os +import sys +import subprocess +import argparse +from pathlib import Path + +parser = argparse.ArgumentParser( + prog='fetch_dawn_dependencies', + description=__doc__, +) + +parser.add_argument('-d', + '--directory', + type=str, + default="", + help=""" + Working directory, in which we read and apply DEPS files recusively. If not + specified, the current working directory is used. + """) + +parser.add_argument('-g', + '--git', + type=str, + default="git", + help=""" + Path to the git command used to. By default, git is retrieved from the PATH. + You may also use this option to specify extra argument for all calls to git. + """) + +parser.add_argument('-s', + '--shallow', + action='store_true', + default=True, + help=""" + Clone repositories without commit history (only getting data for the + requested commit). + NB: The git server hosting the dependencies must have turned on the + `uploadpack.allowReachableSHA1InWant` option. + NB2: git submodules may not work as expected (but they are not used by Dawn + dependencies). + """) +parser.add_argument('-ns', + '--no-shallow', + action='store_false', + dest='shallow', + help="Deactivate shallow cloning.") + + +def main(args): + # The dependencies that we need to pull from the DEPS files. + # Dependencies of dependencies are prefixed by their ancestors. + required_submodules = [ + 'third_party/dawn', + 'third_party/glfw', + ] + + root_dir = Path(args.directory).resolve() + + process_dir(args, root_dir, required_submodules) + + +def process_dir(args, dir_path, required_submodules): + """ + Install dependencies for the provided directory by processing the DEPS file + that it contains (if it exists). + Recursively install dependencies in sub-directories that are created by + cloning dependencies. + """ + deps_path = dir_path / 'DEPS' + if not deps_path.is_file(): + return + + log(f"Listing dependencies from {dir_path}") + DEPS = open(deps_path).read() + + ldict = {} + exec(DEPS, globals(), ldict) + deps = ldict.get('deps') + variables = ldict.get('vars', {}) + + if deps is None: + log(f"ERROR: DEPS file '{deps_path}' does not define a 'deps' variable" + ) + exit(1) + + for submodule in required_submodules: + if submodule not in deps: + continue + submodule_path = dir_path / Path(submodule) + + raw_url = deps[submodule]['url'] + git_url, git_tag = raw_url.format(**variables).rsplit('@', 1) + + # Run git from within the submodule's path (don't use for clone) + git = lambda *x: subprocess.run([args.git, '-C', submodule_path, *x], + capture_output=True) + + log(f"Fetching dependency '{submodule}'") + if (submodule_path / ".git").is_dir(): + # The module was already cloned, but we may need to update it + proc = git('rev-parse', 'HEAD') + need_update = proc.stdout.decode().strip() != git_tag + + if need_update: + # The module was already cloned, but we may need to update it + proc = git('cat-file', '-t', git_tag) + git_tag_exists = proc.returncode == 0 + + if not git_tag_exists: + log(f"Updating '{submodule_path}' from '{git_url}'") + if args.shallow: + git('fetch', 'origin', git_tag, '--depth', '1') + else: + git('fetch', 'origin') + + log(f"Checking out tag '{git_tag}'") + git('checkout', git_tag) + + else: + os.makedirs(submodule_path / ".git", exist_ok=True) + if args.shallow: + log(f"Shallow cloning '{git_url}' at '{git_tag}' into '{submodule_path}'" + ) + shallow_clone(git, git_url, git_tag) + else: + log(f"Cloning '{git_url}' into '{submodule_path}'") + subprocess.run([ + args.git, + 'clone', + '--recurse-submodules', + git_url, + submodule_path, + ], + capture_output=True) + + log(f"Checking out tag '{git_tag}'") + git('checkout', git_tag) + + # Recursive call + required_subsubmodules = [ + m[len(submodule) + 1:] for m in required_submodules + if m.startswith(submodule + "/") + ] + process_dir(args, submodule_path, required_subsubmodules) + + +def shallow_clone(git, git_url, git_tag): + """ + Fetching only 1 commit is not exposed in the git clone API, so we decompose + it manually in git init, git fetch, git reset. + """ + git('init') + git('fetch', git_url, git_tag, '--depth', '1') + + +def log(msg): + """Just makes it look good in the CMake log flow.""" + print(f"-- -- {msg}") + + +class Var: + """ + Mock Var class, that the content of DEPS files assume to exist when they + are exec-ed. + """ + def __init__(self, name): + self.name = name + + def __add__(self, text): + return self.name + text + + def __radd__(self, text): + return text + self.name + + +if __name__ == "__main__": + main(parser.parse_args())