[dawn][emscripten] Add bare-bone unit testing for Emscripten bindings.
Change-Id: Id950fa83fc3c36e0433e516f96a6c3acbecc1862
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/211235
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a2578b7..bcb91ba1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -77,12 +77,14 @@
set(USE_X11 OFF)
set(USE_WINDOWS_UI OFF)
set(BUILD_SAMPLES OFF)
+set(BUILD_TESTS OFF)
set(TARGET_MACOS OFF)
if (CMAKE_SYSTEM_NAME MATCHES "Emscripten")
# Only the samples are supported for Emscripten at the moment.
# TODO(crbug.com/42240181): Make dawn_end2end_tests work too.
set(ENABLE_EMSCRIPTEN ON)
set(BUILD_SAMPLES ON)
+ set(BUILD_TESTS ON)
set(ENABLE_NULL OFF)
elseif (WIN32)
set(ENABLE_D3D11 ON)
@@ -174,6 +176,7 @@
option(DAWN_TARGET_MACOS "Manually link Apple core frameworks" ${TARGET_MACOS})
option(DAWN_BUILD_SAMPLES "Enables building Dawn's samples" ${BUILD_SAMPLES})
+option(DAWN_BUILD_TESTS "Enables building Dawn's tests" ${BUILD_TESTS})
option(DAWN_BUILD_NODE_BINDINGS "Enables building Dawn's NodeJS bindings" OFF)
option(DAWN_ENABLE_SWIFTSHADER "Enables building Swiftshader as part of the build and Vulkan adapter discovery" OFF)
option(DAWN_BUILD_BENCHMARKS "Build Dawn benchmarks" OFF)
@@ -293,6 +296,7 @@
set_if_not_defined(DAWN_PROTOBUF_DIR "${DAWN_THIRD_PARTY_DIR}/protobuf" "Directory in which to find protobuf")
set_if_not_defined(DAWN_LPM_DIR "${DAWN_THIRD_PARTY_DIR}/libprotobuf-mutator/src" "Directory in which to find libprotobuf")
set_if_not_defined(DAWN_EMDAWNWEBGPU_DIR "${DAWN_THIRD_PARTY_DIR}/emdawnwebgpu" "Directory in which to find Dawn specific Emscripten bindings")
+set_if_not_defined(DAWN_GOOGLETEST_DIR "${DAWN_THIRD_PARTY_DIR}/googletest" "Directory in which to find googletest")
set_if_not_defined(DAWN_SPIRV_TOOLS_DIR "${DAWN_THIRD_PARTY_DIR}/spirv-tools/src" "Directory in which to find SPIRV-Tools")
set_if_not_defined(DAWN_SPIRV_HEADERS_DIR "${DAWN_THIRD_PARTY_DIR}/spirv-headers/src" "Directory in which to find SPIRV-Headers")
diff --git a/src/dawn/samples/CMakeLists.txt b/src/dawn/samples/CMakeLists.txt
index 8eee09c..ff4d9d9 100644
--- a/src/dawn/samples/CMakeLists.txt
+++ b/src/dawn/samples/CMakeLists.txt
@@ -78,6 +78,10 @@
if (${DAWN_ENABLE_EMSCRIPTEN})
set_target_properties(${arg_NAME} PROPERTIES
SUFFIX ".html")
+ target_link_options(${arg_NAME} PUBLIC
+ # We need JSPI for Future implementation.
+ "-sJSPI"
+ )
endif()
target_link_libraries(${arg_NAME} PUBLIC dawn::dawn_sample_utils)
common_compile_options(${arg_NAME})
diff --git a/src/emdawnwebgpu/CMakeLists.txt b/src/emdawnwebgpu/CMakeLists.txt
index 68d40fa..3f67954 100644
--- a/src/emdawnwebgpu/CMakeLists.txt
+++ b/src/emdawnwebgpu/CMakeLists.txt
@@ -194,8 +194,6 @@
target_link_options(emdawnwebgpu_config INTERFACE
# We are using Dawn-generated bindings, not built-in ones
"-sUSE_WEBGPU=0"
- # We need Asyncify for Future implementation.
- "-sASYNCIFY=1"
# The JS libraries needed for bindings
"--js-library=${EM_BUILD_GEN_DIR}/library_webgpu_enum_tables.js"
"--js-library=${EM_BUILD_GEN_DIR}/library_webgpu_generated_struct_info.js"
@@ -220,6 +218,7 @@
ENABLE_EMSCRIPTEN
HEADER_ONLY
HEADERS
+ "${EM_BUILD_GEN_DIR}/include/dawn/webgpu_cpp_print.h"
"${EM_BUILD_GEN_DIR}/include/webgpu/webgpu_cpp.h"
"${EM_BUILD_GEN_DIR}/include/webgpu/webgpu_cpp_chained_struct.h"
"${DAWN_INCLUDE_DIR}/webgpu/webgpu_enum_class_bitmasks.h"
@@ -227,4 +226,26 @@
emdawnwebgpu_c
)
+ if (${DAWN_BUILD_TESTS})
+ set(emdawnwebgpu_test_sources
+ "tests/FuturesTests.cpp"
+ )
+ add_executable(emdawnwebgpu_tests ${emdawnwebgpu_test_sources})
+ set_target_properties(emdawnwebgpu_tests PROPERTIES
+ SUFFIX ".html")
+ target_link_libraries(
+ emdawnwebgpu_tests
+ PUBLIC
+ dawn::dawn_wgpu_utils
+ emdawnwebgpu_cpp
+ gmock_main
+ )
+ target_link_options(emdawnwebgpu_tests PUBLIC
+ # We need ASYNCIFY for Future implementation. Note that for
+ # some reason, at the moment, these tests do not work with
+ # JSPI.
+ "-sASYNCIFY=1"
+ )
+ endif()
+
endif()
diff --git a/src/emdawnwebgpu/tests/FuturesTests.cpp b/src/emdawnwebgpu/tests/FuturesTests.cpp
new file mode 100644
index 0000000..7a47573
--- /dev/null
+++ b/src/emdawnwebgpu/tests/FuturesTests.cpp
@@ -0,0 +1,366 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <dawn/webgpu_cpp_print.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <webgpu/webgpu_cpp.h>
+
+#include <string>
+#include <utility>
+
+namespace {
+
+using testing::_;
+using testing::HasSubstr;
+
+class InstanceLevelTests : public testing::Test {
+ public:
+ void SetUp() override { instance = wgpu::CreateInstance(); }
+
+ protected:
+ wgpu::Adapter RequestAdapter(const wgpu::RequestAdapterOptions* adapterOptions = nullptr) {
+ wgpu::RequestAdapterStatus status;
+ wgpu::Adapter result = nullptr;
+ EXPECT_EQ(instance.WaitAny(
+ instance.RequestAdapter(
+ adapterOptions, wgpu::CallbackMode::AllowSpontaneous,
+ [&status, &result](wgpu::RequestAdapterStatus s, wgpu::Adapter adapter,
+ wgpu::StringView message) {
+ status = s;
+ result = std::move(adapter);
+ }),
+ UINT64_MAX),
+ wgpu::WaitStatus::Success);
+ EXPECT_EQ(status, wgpu::RequestAdapterStatus::Success);
+ return result;
+ }
+
+ wgpu::Instance instance;
+};
+
+TEST_F(InstanceLevelTests, RequestAdapter) {
+ EXPECT_NE(RequestAdapter(), nullptr);
+}
+
+class AdapterLevelTests : public InstanceLevelTests {
+ public:
+ void SetUp() override {
+ InstanceLevelTests::SetUp();
+ adapter = RequestAdapter();
+ }
+
+ protected:
+ wgpu::Device RequestDevice(const wgpu::DeviceDescriptor* descriptor = nullptr) {
+ wgpu::RequestDeviceStatus status;
+ wgpu::Device result = nullptr;
+ EXPECT_EQ(
+ instance.WaitAny(adapter.RequestDevice(
+ descriptor, wgpu::CallbackMode::AllowSpontaneous,
+ [&status, &result](wgpu::RequestDeviceStatus s,
+ wgpu::Device device, wgpu::StringView message) {
+ status = s;
+ result = std::move(device);
+ }),
+ UINT64_MAX),
+ wgpu::WaitStatus::Success);
+ EXPECT_EQ(status, wgpu::RequestDeviceStatus::Success);
+ return result;
+ }
+
+ wgpu::Adapter adapter;
+};
+
+TEST_F(AdapterLevelTests, RequestDevice) {
+ EXPECT_NE(RequestDevice(), nullptr);
+}
+
+TEST_F(AdapterLevelTests, RequestDeviceThenDestroy) {
+ wgpu::Device device = nullptr;
+ wgpu::DeviceLostReason reason = wgpu::DeviceLostReason::Unknown;
+
+ wgpu::DeviceDescriptor descriptor = {};
+ descriptor.SetDeviceLostCallback(
+ wgpu::CallbackMode::AllowSpontaneous,
+ [&device, &reason](const wgpu::Device& d, wgpu::DeviceLostReason r, wgpu::StringView) {
+ reason = r;
+ EXPECT_EQ(device.Get(), d.Get());
+ });
+ device = RequestDevice(&descriptor);
+
+ auto deviceLostFuture = device.GetLostFuture();
+ device.Destroy();
+ ASSERT_EQ(instance.WaitAny(deviceLostFuture, UINT64_MAX), wgpu::WaitStatus::Success);
+ EXPECT_EQ(reason, wgpu::DeviceLostReason::Destroyed);
+}
+
+TEST_F(AdapterLevelTests, RequestDeviceThenDrop) {
+ wgpu::DeviceLostReason reason = wgpu::DeviceLostReason::Unknown;
+
+ wgpu::DeviceDescriptor descriptor = {};
+ descriptor.SetDeviceLostCallback(
+ wgpu::CallbackMode::AllowSpontaneous,
+ [&reason](const wgpu::Device&, wgpu::DeviceLostReason r, wgpu::StringView) { reason = r; });
+ wgpu::Device device = RequestDevice(&descriptor);
+
+ auto deviceLostFuture = device.GetLostFuture();
+ device = nullptr;
+ ASSERT_EQ(instance.WaitAny(deviceLostFuture, UINT64_MAX), wgpu::WaitStatus::Success);
+ EXPECT_EQ(reason, wgpu::DeviceLostReason::Destroyed);
+}
+
+class DeviceLevelTests : public AdapterLevelTests {
+ public:
+ void SetUp() override {
+ AdapterLevelTests::SetUp();
+
+ wgpu::DeviceDescriptor descriptor = {};
+ descriptor.SetDeviceLostCallback(
+ wgpu::CallbackMode::AllowSpontaneous,
+ [](const wgpu::Device&, wgpu::DeviceLostReason reason, wgpu::StringView) {
+ EXPECT_EQ(reason, wgpu::DeviceLostReason::Destroyed);
+ });
+ descriptor.SetUncapturedErrorCallback(
+ [](const wgpu::Device& d, wgpu::ErrorType t, wgpu::StringView m,
+ DeviceLevelTests* self) { self->uncapturedErrorCb.Call(d, t, m); },
+ this);
+ device = RequestDevice(&descriptor);
+ }
+
+ void TearDown() override {
+ // For teardown, we explicitly wait for the device lost so that we can ensure that errors
+ // have been flushed.
+ auto deviceLostFuture = device.GetLostFuture();
+ device = nullptr;
+ EXPECT_EQ(instance.WaitAny(deviceLostFuture, UINT64_MAX), wgpu::WaitStatus::Success);
+ }
+
+ protected:
+ wgpu::ShaderModule CreateShaderModule(const char* source) {
+ wgpu::ShaderSourceWGSL wgsl;
+ wgsl.code = source;
+ wgpu::ShaderModuleDescriptor desc;
+ desc.nextInChain = &wgsl;
+ return device.CreateShaderModule(&desc);
+ }
+
+ wgpu::Device device;
+
+ // Mock callback used for uncaptured errors so that test writers can add expectations on this
+ // callback which will enforce the expectations at teardown of the test.
+ testing::StrictMock<
+ testing::MockFunction<void(const wgpu::Device&, wgpu::ErrorType, wgpu::StringView)>>
+ uncapturedErrorCb;
+};
+
+TEST_F(DeviceLevelTests, ValidationError) {
+ EXPECT_CALL(uncapturedErrorCb, Call(_, wgpu::ErrorType::Validation, _)).Times(1);
+
+ wgpu::BufferDescriptor desc = {};
+ desc.size = 1024;
+ desc.usage = static_cast<wgpu::BufferUsage>(UINT64_MAX);
+ wgpu::Buffer buffer = device.CreateBuffer(&desc);
+}
+
+TEST_F(DeviceLevelTests, PopErrorScope) {
+ device.PushErrorScope(wgpu::ErrorFilter::Validation);
+
+ wgpu::BufferDescriptor desc = {};
+ desc.size = 1024;
+ desc.usage = static_cast<wgpu::BufferUsage>(UINT64_MAX);
+ wgpu::Buffer buffer = device.CreateBuffer(&desc);
+
+ wgpu::PopErrorScopeStatus status;
+ wgpu::ErrorType type;
+ EXPECT_EQ(instance.WaitAny(
+ device.PopErrorScope(wgpu::CallbackMode::AllowSpontaneous,
+ [&status, &type](wgpu::PopErrorScopeStatus s,
+ wgpu::ErrorType t, wgpu::StringView) {
+ status = s;
+ type = t;
+ }),
+ UINT64_MAX),
+ wgpu::WaitStatus::Success);
+ EXPECT_EQ(status, wgpu::PopErrorScopeStatus::Success);
+ EXPECT_EQ(type, wgpu::ErrorType::Validation);
+}
+
+TEST_F(DeviceLevelTests, BufferMapAndWorkDone) {
+ static constexpr uint32_t kData = 100u;
+ size_t kSize = sizeof(uint32_t);
+
+ wgpu::Buffer src;
+ wgpu::Buffer dst;
+ {
+ wgpu::BufferDescriptor desc;
+ desc.label = "src";
+ desc.size = kSize;
+ desc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite;
+ src = device.CreateBuffer(&desc);
+ }
+ {
+ wgpu::BufferDescriptor desc;
+ desc.label = "dst";
+ desc.size = kSize;
+ desc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead;
+ dst = device.CreateBuffer(&desc);
+ }
+
+ // Map the writable buffer and write to it.
+ wgpu::MapAsyncStatus writeStatus = wgpu::MapAsyncStatus::Unknown;
+ EXPECT_EQ(instance.WaitAny(
+ src.MapAsync(wgpu::MapMode::Write, 0, kSize, wgpu::CallbackMode::AllowSpontaneous,
+ [&writeStatus](wgpu::MapAsyncStatus status, wgpu::StringView) {
+ writeStatus = status;
+ }),
+ UINT64_MAX),
+ wgpu::WaitStatus::Success);
+ ASSERT_EQ(writeStatus, wgpu::MapAsyncStatus::Success);
+ auto writeData = static_cast<uint32_t*>(src.GetMappedRange());
+ ASSERT_NE(writeData, nullptr);
+ *writeData = kData;
+ src.Unmap();
+
+ // Copy the buffer to the readable one, and wait for the copy to complete. Note that the wait
+ // for the copy is not strictly necessary since the map async call following it will already
+ // wait for it, but we do it explicitly here to test the additional entry point.
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ encoder.CopyBufferToBuffer(src, 0, dst, 0, kSize);
+ wgpu::CommandBuffer commands = encoder.Finish();
+ wgpu::Queue queue = device.GetQueue();
+ queue.Submit(1, &commands);
+
+ wgpu::QueueWorkDoneStatus copyStatus;
+ EXPECT_EQ(
+ instance.WaitAny(queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowSpontaneous,
+ [©Status](wgpu::QueueWorkDoneStatus status) {
+ copyStatus = status;
+ }),
+ UINT64_MAX),
+ wgpu::WaitStatus::Success);
+ ASSERT_EQ(copyStatus, wgpu::QueueWorkDoneStatus::Success);
+
+ // Map the readable buffer and verify the contents.
+ wgpu::MapAsyncStatus readStatus = wgpu::MapAsyncStatus::Unknown;
+ EXPECT_EQ(instance.WaitAny(
+ dst.MapAsync(wgpu::MapMode::Read, 0, kSize, wgpu::CallbackMode::AllowSpontaneous,
+ [&readStatus](wgpu::MapAsyncStatus status, wgpu::StringView) {
+ readStatus = status;
+ }),
+ UINT64_MAX),
+ wgpu::WaitStatus::Success);
+ ASSERT_EQ(readStatus, wgpu::MapAsyncStatus::Success);
+ auto readData = static_cast<const uint32_t*>(dst.GetConstMappedRange());
+ ASSERT_NE(readData, nullptr);
+ EXPECT_EQ(*readData, kData);
+ dst.Unmap();
+}
+
+TEST_F(DeviceLevelTests, CreateComputePipelineAsync) {
+ wgpu::ComputePipelineDescriptor desc;
+ desc.compute.module = CreateShaderModule(R"(
+ @compute @workgroup_size(1) fn main() {}
+ )");
+
+ wgpu::CreatePipelineAsyncStatus status = wgpu::CreatePipelineAsyncStatus::Unknown;
+ wgpu::ComputePipeline pipeline = nullptr;
+ EXPECT_EQ(instance.WaitAny(device.CreateComputePipelineAsync(
+ &desc, wgpu::CallbackMode::AllowSpontaneous,
+ [&status, &pipeline](wgpu::CreatePipelineAsyncStatus s,
+ wgpu::ComputePipeline p, wgpu::StringView) {
+ status = s;
+ pipeline = std::move(p);
+ }),
+ UINT64_MAX),
+ wgpu::WaitStatus::Success);
+ EXPECT_EQ(status, wgpu::CreatePipelineAsyncStatus::Success);
+ EXPECT_NE(pipeline, nullptr);
+}
+
+TEST_F(DeviceLevelTests, CreateRenderPipelineAsync) {
+ wgpu::RenderPipelineDescriptor desc;
+ desc.vertex.module = CreateShaderModule(R"(
+ @vertex fn main() -> @builtin(position) vec4f {
+ return vec4f(0.0, 0.0, 0.0, 1.0);
+ }
+ )");
+
+ wgpu::FragmentState frag;
+ frag.module = CreateShaderModule(R"(
+ @fragment fn main() -> @location(0) vec4f {
+ return vec4f(0.0, 1.0, 0.0, 1.0);
+ }
+ )");
+ wgpu::ColorTargetState target;
+ target.format = wgpu::TextureFormat::RGBA8Unorm;
+ frag.targetCount = 1;
+ frag.targets = ⌖
+ desc.fragment = &frag;
+
+ wgpu::CreatePipelineAsyncStatus status = wgpu::CreatePipelineAsyncStatus::Unknown;
+ wgpu::RenderPipeline pipeline = nullptr;
+ EXPECT_EQ(instance.WaitAny(device.CreateRenderPipelineAsync(
+ &desc, wgpu::CallbackMode::AllowSpontaneous,
+ [&status, &pipeline](wgpu::CreatePipelineAsyncStatus s,
+ wgpu::RenderPipeline p, wgpu::StringView) {
+ status = s;
+ pipeline = std::move(p);
+ }),
+ UINT64_MAX),
+ wgpu::WaitStatus::Success);
+ EXPECT_EQ(status, wgpu::CreatePipelineAsyncStatus::Success);
+ EXPECT_NE(pipeline, nullptr);
+}
+
+TEST_F(DeviceLevelTests, GetCompilationInfo) {
+ wgpu::ShaderModule shader = CreateShaderModule(R"(
+ @fragment fn main(@location(0) x : f32) {
+ return;
+ return;
+ }
+ )");
+
+ wgpu::CompilationMessageType messageType;
+ std::string message;
+ EXPECT_EQ(instance.WaitAny(shader.GetCompilationInfo(
+ wgpu::CallbackMode::AllowSpontaneous,
+ [&message, &messageType](wgpu::CompilationInfoRequestStatus s,
+ const wgpu::CompilationInfo* info) {
+ ASSERT_EQ(s, wgpu::CompilationInfoRequestStatus::Success);
+ ASSERT_NE(info, nullptr);
+ ASSERT_EQ(info->messageCount, 1);
+
+ message = info->messages[0].message;
+ messageType = info->messages[0].type;
+ }),
+ UINT64_MAX),
+ wgpu::WaitStatus::Success);
+ EXPECT_EQ(messageType, wgpu::CompilationMessageType::Warning);
+ EXPECT_THAT(message, HasSubstr("unreachable"));
+}
+
+} // namespace
diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt
index 05c7bb4..1bc7f06 100644
--- a/third_party/CMakeLists.txt
+++ b/third_party/CMakeLists.txt
@@ -33,7 +33,7 @@
# a parent project that does not want to use depot_tools.
if (DAWN_FETCH_DEPENDENCIES)
set(EXTRA_FETCH_ARGS)
- if (NOT TARGET gmock AND ${TINT_BUILD_TESTS})
+ if (NOT TARGET gmock AND (DAWN_BUILD_TESTS OR TINT_BUILD_TESTS))
list(APPEND EXTRA_FETCH_ARGS --use-test-deps)
endif()
@@ -70,6 +70,15 @@
add_subdirectory(${DAWN_ABSEIL_DIR} "${CMAKE_CURRENT_BINARY_DIR}/abseil")
endif()
+if ((DAWN_BUILD_TESTS OR TINT_BUILD_TESTS) AND NOT TARGET gmock)
+ set(gtest_force_shared_crt ON CACHE BOOL "Controls whether a shared run-time library should be used even when Google Test is built as static library" FORCE)
+ if (${DAWN_ENABLE_EMSCRIPTEN})
+ # For Emscripten builds, we need to disable pthreads.
+ set(gtest_disable_pthreads ON)
+ endif()
+ add_subdirectory(${DAWN_GOOGLETEST_DIR} "${CMAKE_CURRENT_BINARY_DIR}/googletest" EXCLUDE_FROM_ALL)
+endif()
+
################################################################################
# End of Emscripten enabled third party directories
################################################################################
@@ -161,11 +170,6 @@
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/google_benchmark/src EXCLUDE_FROM_ALL)
endif()
-if (TINT_BUILD_TESTS AND NOT TARGET gmock)
- set(gtest_force_shared_crt ON CACHE BOOL "Controls whether a shared run-time library should be used even when Google Test is built as static library" FORCE)
- add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/googletest EXCLUDE_FROM_ALL)
-endif()
-
# Populates 'targets' with list of all targets under 'dir'
# Use 'get_all_targets_recursive' instead of this macro
macro(do_get_all_targets_recursive targets dir)
diff --git a/third_party/emdawnwebgpu/library_webgpu.js b/third_party/emdawnwebgpu/library_webgpu.js
index 991f4e8..9e1d3fc 100644
--- a/third_party/emdawnwebgpu/library_webgpu.js
+++ b/third_party/emdawnwebgpu/library_webgpu.js
@@ -576,12 +576,14 @@
obj.label = UTF8ToString(data, length);
},
-#if ASYNCIFY
// Returns a FutureID that was resolved, or kNullFutureId if timed out.
+#if ASYNCIFY
emwgpuWaitAny__async: true,
+#endif
emwgpuWaitAny__i53abi: false,
emwgpuWaitAny__sig: 'jppp',
emwgpuWaitAny: (futurePtr, futureCount, timeoutNSPtr) => {
+#if ASYNCIFY
var promises = WebGPU.Internals.waitAnyPromisesList;
if (timeoutNSPtr) {
var timeoutMS = {{{ gpu.makeGetU64('timeoutNSPtr', 0) }}} / 1000000;
@@ -608,8 +610,10 @@
delete WebGPU.Internals.futures[result];
WebGPU.Internals.waitAnyPromisesList.length = 0;
return result;
- },
+#else
+ assert(false);
#endif
+ },
emwgpuGetPreferredFormat__sig: 'i',
emwgpuGetPreferredFormat: () => {