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%">
+
+  [![Matrix Space](https://img.shields.io/static/v1?label=Space&message=%23webgpu-dawn&color=blue&logo=matrix)](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, &params, 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, &copySize);
+
+    wgpu::CommandBuffer copy = encoder.Finish();
+    queue.Submit(1, &copy);
+}
+
+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())