Adds error promoting to device loss when disallowed error occurs in a scope.
- Defaults consume error calls to only allow validation and device loss errors. - Allows OOM errors on Buffers, QuerySets, and Textures only. - Adds initial suite of unit tests (and any necessary updates to mock framework). Bug: dawn:1336 Change-Id: I82112ea6c147e894280e605bf8ae0ce00488c9f3 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/119800 Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Loko Kung <lokokung@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
8812918fa4
commit
53893a3d77
|
@ -209,6 +209,7 @@ DeviceBase::DeviceBase(AdapterBase* adapter,
|
|||
}
|
||||
|
||||
DeviceBase::DeviceBase() : mState(State::Alive), mToggles(ToggleStage::Device) {
|
||||
GetDefaultLimits(&mLimits.v1);
|
||||
mFormatTable = BuildFormatTable(this);
|
||||
}
|
||||
|
||||
|
@ -465,9 +466,12 @@ void DeviceBase::APIDestroy() {
|
|||
Destroy();
|
||||
}
|
||||
|
||||
void DeviceBase::HandleError(InternalErrorType type,
|
||||
const char* message,
|
||||
void DeviceBase::HandleError(std::unique_ptr<ErrorData> error,
|
||||
InternalErrorType additionalAllowedErrors,
|
||||
WGPUDeviceLostReason lost_reason) {
|
||||
InternalErrorType allowedErrors =
|
||||
InternalErrorType::Validation | InternalErrorType::DeviceLost | additionalAllowedErrors;
|
||||
InternalErrorType type = error->GetType();
|
||||
if (type == InternalErrorType::DeviceLost) {
|
||||
mState = State::Disconnected;
|
||||
|
||||
|
@ -481,10 +485,12 @@ void DeviceBase::HandleError(InternalErrorType type,
|
|||
// A real device lost happened. Set the state to disconnected as the device cannot be
|
||||
// used. Also tags all commands as completed since the device stopped running.
|
||||
AssumeCommandsComplete();
|
||||
} else if (type == InternalErrorType::Internal) {
|
||||
// If we receive an internal error, assume the backend can't recover and proceed with
|
||||
// device destruction. We first wait for all previous commands to be completed so that
|
||||
// backend objects can be freed immediately, before handling the loss.
|
||||
} else if (!(allowedErrors & type)) {
|
||||
// If we receive an error which we did not explicitly allow, assume the backend can't
|
||||
// recover and proceed with device destruction. We first wait for all previous commands to
|
||||
// be completed so that backend objects can be freed immediately, before handling the loss.
|
||||
error->AppendContext("handling unexpected error type %s when allowed errors are %s.", type,
|
||||
allowedErrors);
|
||||
|
||||
// Move away from the Alive state so that the application cannot use this device
|
||||
// anymore.
|
||||
|
@ -503,6 +509,9 @@ void DeviceBase::HandleError(InternalErrorType type,
|
|||
type = InternalErrorType::DeviceLost;
|
||||
}
|
||||
|
||||
// TODO(lokokung) Update call sites that take the c-string to take string_view.
|
||||
const std::string messageStr = error->GetFormattedMessage();
|
||||
const char* message = messageStr.c_str();
|
||||
if (type == InternalErrorType::DeviceLost) {
|
||||
// The device was lost, call the application callback.
|
||||
if (mDeviceLostCallback != nullptr) {
|
||||
|
@ -533,10 +542,11 @@ void DeviceBase::HandleError(InternalErrorType type,
|
|||
}
|
||||
}
|
||||
|
||||
void DeviceBase::ConsumeError(std::unique_ptr<ErrorData> error) {
|
||||
void DeviceBase::ConsumeError(std::unique_ptr<ErrorData> error,
|
||||
InternalErrorType additionalAllowedErrors) {
|
||||
ASSERT(error != nullptr);
|
||||
AppendDebugLayerMessages(error.get());
|
||||
HandleError(error->GetType(), error->GetFormattedMessage().c_str());
|
||||
HandleError(std::move(error), additionalAllowedErrors);
|
||||
}
|
||||
|
||||
void DeviceBase::APISetLoggingCallback(wgpu::LoggingCallback callback, void* userdata) {
|
||||
|
@ -651,7 +661,10 @@ void DeviceBase::APIForceLoss(wgpu::DeviceLostReason reason, const char* message
|
|||
if (mState != State::Alive) {
|
||||
return;
|
||||
}
|
||||
HandleError(InternalErrorType::Internal, message, ToAPI(reason));
|
||||
// Note that since we are passing None as the allowedErrors, an additional message will be
|
||||
// appended noting that the error was unexpected. Since this call is for testing only it is not
|
||||
// too important, but useful for users to understand where the extra message is coming from.
|
||||
HandleError(DAWN_INTERNAL_ERROR(message), InternalErrorType::None, ToAPI(reason));
|
||||
}
|
||||
|
||||
DeviceBase::State DeviceBase::GetState() const {
|
||||
|
@ -1033,8 +1046,8 @@ BindGroupLayoutBase* DeviceBase::APICreateBindGroupLayout(
|
|||
}
|
||||
BufferBase* DeviceBase::APICreateBuffer(const BufferDescriptor* descriptor) {
|
||||
Ref<BufferBase> result = nullptr;
|
||||
if (ConsumedError(CreateBuffer(descriptor), &result, "calling %s.CreateBuffer(%s).", this,
|
||||
descriptor)) {
|
||||
if (ConsumedError(CreateBuffer(descriptor), &result, InternalErrorType::OutOfMemory,
|
||||
"calling %s.CreateBuffer(%s).", this, descriptor)) {
|
||||
ASSERT(result == nullptr);
|
||||
return BufferBase::MakeError(this, descriptor);
|
||||
}
|
||||
|
@ -1090,8 +1103,8 @@ PipelineLayoutBase* DeviceBase::APICreatePipelineLayout(
|
|||
}
|
||||
QuerySetBase* DeviceBase::APICreateQuerySet(const QuerySetDescriptor* descriptor) {
|
||||
Ref<QuerySetBase> result;
|
||||
if (ConsumedError(CreateQuerySet(descriptor), &result, "calling %s.CreateQuerySet(%s).", this,
|
||||
descriptor)) {
|
||||
if (ConsumedError(CreateQuerySet(descriptor), &result, InternalErrorType::OutOfMemory,
|
||||
"calling %s.CreateQuerySet(%s).", this, descriptor)) {
|
||||
return QuerySetBase::MakeError(this, descriptor);
|
||||
}
|
||||
return result.Detach();
|
||||
|
@ -1174,8 +1187,8 @@ SwapChainBase* DeviceBase::APICreateSwapChain(Surface* surface,
|
|||
}
|
||||
TextureBase* DeviceBase::APICreateTexture(const TextureDescriptor* descriptor) {
|
||||
Ref<TextureBase> result;
|
||||
if (ConsumedError(CreateTexture(descriptor), &result, "calling %s.CreateTexture(%s).", this,
|
||||
descriptor)) {
|
||||
if (ConsumedError(CreateTexture(descriptor), &result, InternalErrorType::OutOfMemory,
|
||||
"calling %s.CreateTexture(%s).", this, descriptor)) {
|
||||
return TextureBase::MakeError(this, descriptor);
|
||||
}
|
||||
return result.Detach();
|
||||
|
@ -1191,12 +1204,14 @@ BufferBase* DeviceBase::APICreateErrorBuffer(const BufferDescriptor* desc) {
|
|||
// MapppedAtCreation == false.
|
||||
MaybeError maybeError = ValidateBufferDescriptor(this, &fakeDescriptor);
|
||||
if (maybeError.IsError()) {
|
||||
ConsumedError(maybeError.AcquireError(), "calling %s.CreateBuffer(%s).", this, desc);
|
||||
ConsumedError(maybeError.AcquireError(), InternalErrorType::OutOfMemory,
|
||||
"calling %s.CreateBuffer(%s).", this, desc);
|
||||
} else {
|
||||
const DawnBufferDescriptorErrorInfoFromWireClient* clientErrorInfo = nullptr;
|
||||
FindInChain(desc->nextInChain, &clientErrorInfo);
|
||||
if (clientErrorInfo != nullptr && clientErrorInfo->outOfMemory) {
|
||||
ConsumedError(DAWN_OUT_OF_MEMORY_ERROR("Failed to allocate memory for buffer mapping"));
|
||||
ConsumedError(DAWN_OUT_OF_MEMORY_ERROR("Failed to allocate memory for buffer mapping"),
|
||||
InternalErrorType::OutOfMemory);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1377,12 +1392,12 @@ void DeviceBase::APIInjectError(wgpu::ErrorType type, const char* message) {
|
|||
// This method should only be used to make error scope reject. For DeviceLost there is the
|
||||
// LoseForTesting function that can be used instead.
|
||||
if (type != wgpu::ErrorType::Validation && type != wgpu::ErrorType::OutOfMemory) {
|
||||
HandleError(InternalErrorType::Validation,
|
||||
"Invalid injected error, must be Validation or OutOfMemory");
|
||||
HandleError(
|
||||
DAWN_VALIDATION_ERROR("Invalid injected error, must be Validation or OutOfMemory"));
|
||||
return;
|
||||
}
|
||||
|
||||
HandleError(FromWGPUErrorType(type), message);
|
||||
HandleError(DAWN_MAKE_ERROR(FromWGPUErrorType(type), message), InternalErrorType::OutOfMemory);
|
||||
}
|
||||
|
||||
void DeviceBase::APIValidateTextureDescriptor(const TextureDescriptor* desc) {
|
||||
|
|
|
@ -69,23 +69,28 @@ class DeviceBase : public RefCountedWithExternalCount {
|
|||
// Handles the error, causing a device loss if applicable. Almost always when a device loss
|
||||
// occurs because of an error, we want to call the device loss callback with an undefined
|
||||
// reason, but the ForceLoss API allows for an injection of the reason, hence the default
|
||||
// argument.
|
||||
void HandleError(InternalErrorType type,
|
||||
const char* message,
|
||||
// argument. The `additionalAllowedErrors` mask allows specifying additional errors are allowed
|
||||
// (on top of validation and device loss errors). Note that "allowed" is defined as surfacing to
|
||||
// users as the respective error rather than causing a device loss instead.
|
||||
void HandleError(std::unique_ptr<ErrorData> error,
|
||||
InternalErrorType additionalAllowedErrors = InternalErrorType::None,
|
||||
WGPUDeviceLostReason lost_reason = WGPUDeviceLostReason_Undefined);
|
||||
|
||||
bool ConsumedError(MaybeError maybeError) {
|
||||
bool ConsumedError(MaybeError maybeError,
|
||||
InternalErrorType additionalAllowedErrors = InternalErrorType::None) {
|
||||
if (DAWN_UNLIKELY(maybeError.IsError())) {
|
||||
ConsumeError(maybeError.AcquireError());
|
||||
ConsumeError(maybeError.AcquireError(), additionalAllowedErrors);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool ConsumedError(ResultOrError<T> resultOrError, T* result) {
|
||||
bool ConsumedError(ResultOrError<T> resultOrError,
|
||||
T* result,
|
||||
InternalErrorType additionalAllowedErrors = InternalErrorType::None) {
|
||||
if (DAWN_UNLIKELY(resultOrError.IsError())) {
|
||||
ConsumeError(resultOrError.AcquireError());
|
||||
ConsumeError(resultOrError.AcquireError(), additionalAllowedErrors);
|
||||
return true;
|
||||
}
|
||||
*result = resultOrError.AcquireSuccess();
|
||||
|
@ -93,47 +98,52 @@ class DeviceBase : public RefCountedWithExternalCount {
|
|||
}
|
||||
|
||||
template <typename... Args>
|
||||
bool ConsumedError(MaybeError maybeError, const char* formatStr, const Args&... args) {
|
||||
bool ConsumedError(MaybeError maybeError,
|
||||
InternalErrorType additionalAllowedErrors,
|
||||
const char* formatStr,
|
||||
const Args&... args) {
|
||||
if (DAWN_UNLIKELY(maybeError.IsError())) {
|
||||
std::unique_ptr<ErrorData> error = maybeError.AcquireError();
|
||||
if (error->GetType() == InternalErrorType::Validation) {
|
||||
std::string out;
|
||||
absl::UntypedFormatSpec format(formatStr);
|
||||
if (absl::FormatUntyped(&out, format, {absl::FormatArg(args)...})) {
|
||||
error->AppendContext(std::move(out));
|
||||
} else {
|
||||
error->AppendContext(
|
||||
absl::StrFormat("[Failed to format error: \"%s\"]", formatStr));
|
||||
error->AppendContext(formatStr, args...);
|
||||
}
|
||||
}
|
||||
ConsumeError(std::move(error));
|
||||
ConsumeError(std::move(error), additionalAllowedErrors);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
bool ConsumedError(MaybeError maybeError, const char* formatStr, Args&&... args) {
|
||||
return ConsumedError(std::move(maybeError), InternalErrorType::None, formatStr,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
bool ConsumedError(ResultOrError<T> resultOrError,
|
||||
T* result,
|
||||
InternalErrorType additionalAllowedErrors,
|
||||
const char* formatStr,
|
||||
const Args&... args) {
|
||||
if (DAWN_UNLIKELY(resultOrError.IsError())) {
|
||||
std::unique_ptr<ErrorData> error = resultOrError.AcquireError();
|
||||
if (error->GetType() == InternalErrorType::Validation) {
|
||||
error->AppendContext(formatStr, args...);
|
||||
}
|
||||
ConsumeError(std::move(error), additionalAllowedErrors);
|
||||
return true;
|
||||
}
|
||||
*result = resultOrError.AcquireSuccess();
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
bool ConsumedError(ResultOrError<T> resultOrError,
|
||||
T* result,
|
||||
const char* formatStr,
|
||||
const Args&... args) {
|
||||
if (DAWN_UNLIKELY(resultOrError.IsError())) {
|
||||
std::unique_ptr<ErrorData> error = resultOrError.AcquireError();
|
||||
if (error->GetType() == InternalErrorType::Validation) {
|
||||
std::string out;
|
||||
absl::UntypedFormatSpec format(formatStr);
|
||||
if (absl::FormatUntyped(&out, format, {absl::FormatArg(args)...})) {
|
||||
error->AppendContext(std::move(out));
|
||||
} else {
|
||||
error->AppendContext(
|
||||
absl::StrFormat("[Failed to format error: \"%s\"]", formatStr));
|
||||
}
|
||||
}
|
||||
ConsumeError(std::move(error));
|
||||
return true;
|
||||
}
|
||||
*result = resultOrError.AcquireSuccess();
|
||||
return false;
|
||||
Args&&... args) {
|
||||
return ConsumedError(std::move(resultOrError), result, InternalErrorType::None, formatStr,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
MaybeError ValidateObject(const ApiObjectBase* object) const;
|
||||
|
@ -489,7 +499,8 @@ class DeviceBase : public RefCountedWithExternalCount {
|
|||
|
||||
void SetWGSLExtensionAllowList();
|
||||
|
||||
void ConsumeError(std::unique_ptr<ErrorData> error);
|
||||
void ConsumeError(std::unique_ptr<ErrorData> error,
|
||||
InternalErrorType additionalAllowedErrors = InternalErrorType::None);
|
||||
|
||||
// Each backend should implement to check their passed fences if there are any and return a
|
||||
// completed serial. Return 0 should indicate no fences to check.
|
||||
|
|
|
@ -87,7 +87,7 @@ void EncodingContext::HandleError(std::unique_ptr<ErrorData> error) {
|
|||
mError = std::move(error);
|
||||
}
|
||||
} else {
|
||||
mDevice->HandleError(error->GetType(), error->GetFormattedMessage().c_str());
|
||||
mDevice->HandleError(std::move(error));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,4 +61,64 @@ InternalErrorType FromWGPUErrorType(wgpu::ErrorType type) {
|
|||
}
|
||||
}
|
||||
|
||||
absl::FormatConvertResult<absl::FormatConversionCharSet::kString |
|
||||
absl::FormatConversionCharSet::kIntegral>
|
||||
AbslFormatConvert(InternalErrorType value,
|
||||
const absl::FormatConversionSpec& spec,
|
||||
absl::FormatSink* s) {
|
||||
if (spec.conversion_char() == absl::FormatConversionChar::s) {
|
||||
if (!static_cast<bool>(value)) {
|
||||
s->Append("None");
|
||||
return {true};
|
||||
}
|
||||
|
||||
bool moreThanOneBit = !HasZeroOrOneBits(value);
|
||||
if (moreThanOneBit) {
|
||||
s->Append("(");
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
if (value & InternalErrorType::Validation) {
|
||||
if (!first) {
|
||||
s->Append("|");
|
||||
}
|
||||
first = false;
|
||||
s->Append("Validation");
|
||||
value &= ~InternalErrorType::Validation;
|
||||
}
|
||||
if (value & InternalErrorType::DeviceLost) {
|
||||
if (!first) {
|
||||
s->Append("|");
|
||||
}
|
||||
first = false;
|
||||
s->Append("DeviceLost");
|
||||
value &= ~InternalErrorType::DeviceLost;
|
||||
}
|
||||
if (value & InternalErrorType::Internal) {
|
||||
if (!first) {
|
||||
s->Append("|");
|
||||
}
|
||||
first = false;
|
||||
s->Append("Internal");
|
||||
value &= ~InternalErrorType::Internal;
|
||||
}
|
||||
if (value & InternalErrorType::OutOfMemory) {
|
||||
if (!first) {
|
||||
s->Append("|");
|
||||
}
|
||||
first = false;
|
||||
s->Append("OutOfMemory");
|
||||
value &= ~InternalErrorType::OutOfMemory;
|
||||
}
|
||||
|
||||
if (moreThanOneBit) {
|
||||
s->Append(")");
|
||||
}
|
||||
} else {
|
||||
s->Append(absl::StrFormat(
|
||||
"%u", static_cast<typename std::underlying_type<InternalErrorType>::type>(value)));
|
||||
}
|
||||
return {true};
|
||||
}
|
||||
|
||||
} // namespace dawn::native
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "dawn/common/Result.h"
|
||||
#include "dawn/native/ErrorData.h"
|
||||
#include "dawn/native/Toggles.h"
|
||||
|
@ -27,7 +26,13 @@
|
|||
|
||||
namespace dawn::native {
|
||||
|
||||
enum class InternalErrorType : uint32_t { Validation, DeviceLost, Internal, OutOfMemory };
|
||||
enum class InternalErrorType : uint32_t {
|
||||
None = 0,
|
||||
Validation = 1,
|
||||
DeviceLost = 2,
|
||||
Internal = 4,
|
||||
OutOfMemory = 8
|
||||
};
|
||||
|
||||
// MaybeError and ResultOrError are meant to be used as return value for function that are not
|
||||
// expected to, but might fail. The handling of error is potentially much slower than successes.
|
||||
|
@ -204,6 +209,22 @@ void IgnoreErrors(MaybeError maybeError);
|
|||
wgpu::ErrorType ToWGPUErrorType(InternalErrorType type);
|
||||
InternalErrorType FromWGPUErrorType(wgpu::ErrorType type);
|
||||
|
||||
absl::FormatConvertResult<absl::FormatConversionCharSet::kString |
|
||||
absl::FormatConversionCharSet::kIntegral>
|
||||
AbslFormatConvert(InternalErrorType value,
|
||||
const absl::FormatConversionSpec& spec,
|
||||
absl::FormatSink* s);
|
||||
|
||||
} // namespace dawn::native
|
||||
|
||||
// Enable dawn enum bitmask for error types.
|
||||
namespace dawn {
|
||||
|
||||
template <>
|
||||
struct IsDawnBitmask<native::InternalErrorType> {
|
||||
static constexpr bool enable = true;
|
||||
};
|
||||
|
||||
} // namespace dawn
|
||||
|
||||
#endif // SRC_DAWN_NATIVE_ERROR_H_
|
||||
|
|
|
@ -18,8 +18,10 @@
|
|||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "dawn/common/Compiler.h"
|
||||
|
||||
namespace wgpu {
|
||||
|
@ -50,6 +52,16 @@ class [[nodiscard]] ErrorData {
|
|||
};
|
||||
void AppendBacktrace(const char* file, const char* function, int line);
|
||||
void AppendContext(std::string context);
|
||||
template <typename... Args>
|
||||
void AppendContext(const char* formatStr, const Args&... args) {
|
||||
std::string out;
|
||||
absl::UntypedFormatSpec format(formatStr);
|
||||
if (absl::FormatUntyped(&out, format, {absl::FormatArg(args)...})) {
|
||||
AppendContext(std::move(out));
|
||||
} else {
|
||||
AppendContext(absl::StrFormat("[Failed to format error: \"%s\"]", formatStr));
|
||||
}
|
||||
}
|
||||
void AppendDebugGroup(std::string label);
|
||||
void AppendBackendMessage(std::string message);
|
||||
|
||||
|
|
|
@ -141,6 +141,8 @@ MaybeError ValidateProgrammableStage(DeviceBase* device,
|
|||
|
||||
WGPUCreatePipelineAsyncStatus CreatePipelineAsyncStatusFromErrorType(InternalErrorType error) {
|
||||
switch (error) {
|
||||
case InternalErrorType::None:
|
||||
return WGPUCreatePipelineAsyncStatus_Success;
|
||||
case InternalErrorType::Validation:
|
||||
return WGPUCreatePipelineAsyncStatus_ValidationError;
|
||||
case InternalErrorType::DeviceLost:
|
||||
|
|
|
@ -30,7 +30,7 @@ thread_local DeviceBase* tlDevice = nullptr;
|
|||
|
||||
void TintICEReporter(const tint::diag::List& diagnostics) {
|
||||
if (tlDevice) {
|
||||
tlDevice->HandleError(InternalErrorType::Internal, diagnostics.str().c_str());
|
||||
tlDevice->HandleError(DAWN_INTERNAL_ERROR(diagnostics.str()));
|
||||
#if DAWN_ENABLE_ASSERTS
|
||||
for (const tint::diag::Diagnostic& diag : diagnostics) {
|
||||
if (diag.severity >= tint::diag::Severity::InternalCompilerError) {
|
||||
|
|
|
@ -99,7 +99,7 @@ TextureBase* OldSwapChain::GetNextTextureImpl(const TextureDescriptor* descripto
|
|||
DawnSwapChainNextTexture next = {};
|
||||
DawnSwapChainError error = im.GetNextTexture(im.userData, &next);
|
||||
if (error) {
|
||||
device->HandleError(InternalErrorType::Internal, error);
|
||||
device->HandleError(DAWN_INTERNAL_ERROR(error));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ TextureBase* OldSwapChain::GetNextTextureImpl(const TextureDescriptor* descripto
|
|||
DawnSwapChainNextTexture next = {};
|
||||
DawnSwapChainError error = im.GetNextTexture(im.userData, &next);
|
||||
if (error) {
|
||||
GetDevice()->HandleError(InternalErrorType::Internal, error);
|
||||
GetDevice()->HandleError(DAWN_INTERNAL_ERROR(error));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ TextureBase* SwapChain::GetNextTextureImpl(const TextureDescriptor* descriptor)
|
|||
DawnSwapChainNextTexture next = {};
|
||||
DawnSwapChainError error = im.GetNextTexture(im.userData, &next);
|
||||
if (error) {
|
||||
GetDevice()->HandleError(InternalErrorType::Internal, error);
|
||||
GetDevice()->HandleError(DAWN_INTERNAL_ERROR(error));
|
||||
return nullptr;
|
||||
}
|
||||
GLuint nativeTexture = next.texture.u32;
|
||||
|
|
|
@ -59,7 +59,7 @@ TextureBase* OldSwapChain::GetNextTextureImpl(const TextureDescriptor* descripto
|
|||
DawnSwapChainError error = im.GetNextTexture(im.userData, &next);
|
||||
|
||||
if (error) {
|
||||
GetDevice()->HandleError(InternalErrorType::Internal, error);
|
||||
GetDevice()->HandleError(DAWN_INTERNAL_ERROR(error));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -309,6 +309,7 @@ dawn_test("dawn_unittests") {
|
|||
"unittests/ToBackendTests.cpp",
|
||||
"unittests/TypedIntegerTests.cpp",
|
||||
"unittests/UnicodeTests.cpp",
|
||||
"unittests/native/AllowedErrorTests.cpp",
|
||||
"unittests/native/BlobTests.cpp",
|
||||
"unittests/native/CacheRequestTests.cpp",
|
||||
"unittests/native/CommandBufferEncodingTests.cpp",
|
||||
|
|
|
@ -0,0 +1,367 @@
|
|||
// Copyright 2023 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "dawn/tests/MockCallback.h"
|
||||
#include "dawn/webgpu_cpp.h"
|
||||
#include "mocks/BufferMock.h"
|
||||
#include "mocks/ComputePipelineMock.h"
|
||||
#include "mocks/DawnMockTest.h"
|
||||
#include "mocks/DeviceMock.h"
|
||||
#include "mocks/ExternalTextureMock.h"
|
||||
#include "mocks/PipelineLayoutMock.h"
|
||||
#include "mocks/RenderPipelineMock.h"
|
||||
#include "mocks/ShaderModuleMock.h"
|
||||
#include "mocks/TextureMock.h"
|
||||
|
||||
namespace dawn::native {
|
||||
namespace {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::ByMove;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::MockCallback;
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::Return;
|
||||
using ::testing::StrictMock;
|
||||
using ::testing::Test;
|
||||
|
||||
static constexpr char kOomErrorMessage[] = "Out of memory error";
|
||||
|
||||
static constexpr std::string_view kComputeShader = R"(
|
||||
@compute @workgroup_size(1) fn main() {}
|
||||
)";
|
||||
|
||||
static constexpr std::string_view kVertexShader = R"(
|
||||
@vertex fn main() -> @builtin(position) vec4f {
|
||||
return vec4f(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
)";
|
||||
|
||||
class AllowedErrorTests : public DawnMockTest {
|
||||
public:
|
||||
AllowedErrorTests() : DawnMockTest() {
|
||||
device.SetDeviceLostCallback(mDeviceLostCb.Callback(), mDeviceLostCb.MakeUserdata(this));
|
||||
device.SetUncapturedErrorCallback(mDeviceErrorCb.Callback(),
|
||||
mDeviceErrorCb.MakeUserdata(this));
|
||||
}
|
||||
|
||||
~AllowedErrorTests() override { device = nullptr; }
|
||||
|
||||
protected:
|
||||
// Device mock callbacks used throughout the tests.
|
||||
StrictMock<MockCallback<wgpu::DeviceLostCallback>> mDeviceLostCb;
|
||||
StrictMock<MockCallback<wgpu::ErrorCallback>> mDeviceErrorCb;
|
||||
};
|
||||
|
||||
//
|
||||
// Exercise APIs where OOM errors cause a device lost.
|
||||
//
|
||||
|
||||
TEST_F(AllowedErrorTests, QueueSubmit) {
|
||||
EXPECT_CALL(*(mDeviceMock->GetQueueMock()), SubmitImpl)
|
||||
.WillOnce(Return(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage)));
|
||||
|
||||
// Expect the device lost because of the error.
|
||||
EXPECT_CALL(mDeviceLostCb,
|
||||
Call(WGPUDeviceLostReason_Undefined, HasSubstr(kOomErrorMessage), this))
|
||||
.Times(1);
|
||||
|
||||
device.GetQueue().Submit(0, nullptr);
|
||||
}
|
||||
|
||||
TEST_F(AllowedErrorTests, QueueWriteBuffer) {
|
||||
BufferDescriptor desc = {};
|
||||
desc.size = 1;
|
||||
desc.usage = wgpu::BufferUsage::CopyDst;
|
||||
BufferMock* bufferMock = new NiceMock<BufferMock>(mDeviceMock, &desc);
|
||||
wgpu::Buffer buffer = wgpu::Buffer::Acquire(ToAPI(bufferMock));
|
||||
|
||||
EXPECT_CALL(*(mDeviceMock->GetQueueMock()), WriteBufferImpl)
|
||||
.WillOnce(Return(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage)));
|
||||
|
||||
// Expect the device lost because of the error.
|
||||
EXPECT_CALL(mDeviceLostCb,
|
||||
Call(WGPUDeviceLostReason_Undefined, HasSubstr(kOomErrorMessage), this))
|
||||
.Times(1);
|
||||
|
||||
constexpr uint8_t data = 8;
|
||||
device.GetQueue().WriteBuffer(buffer, 0, &data, 0);
|
||||
}
|
||||
|
||||
TEST_F(AllowedErrorTests, QueueWriteTexture) {
|
||||
TextureDescriptor desc = {};
|
||||
desc.size.width = 1;
|
||||
desc.size.height = 1;
|
||||
desc.usage = wgpu::TextureUsage::CopyDst;
|
||||
desc.format = wgpu::TextureFormat::RGBA8Unorm;
|
||||
TextureMock* textureMock = new NiceMock<TextureMock>(mDeviceMock, &desc);
|
||||
wgpu::Texture texture = wgpu::Texture::Acquire(ToAPI(textureMock));
|
||||
|
||||
EXPECT_CALL(*(mDeviceMock->GetQueueMock()), WriteTextureImpl)
|
||||
.WillOnce(Return(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage)));
|
||||
|
||||
// Expect the device lost because of the error.
|
||||
EXPECT_CALL(mDeviceLostCb,
|
||||
Call(WGPUDeviceLostReason_Undefined, HasSubstr(kOomErrorMessage), this))
|
||||
.Times(1);
|
||||
|
||||
constexpr uint8_t data[] = {1, 2, 4, 8};
|
||||
wgpu::ImageCopyTexture dest = {};
|
||||
dest.texture = texture;
|
||||
wgpu::TextureDataLayout layout = {};
|
||||
wgpu::Extent3D size = {1, 1};
|
||||
device.GetQueue().WriteTexture(&dest, &data, 4, &layout, &size);
|
||||
}
|
||||
|
||||
// Even though OOM is allowed in buffer creation, when creating a buffer in internal workaround the
|
||||
// OOM should be masked as a device loss.
|
||||
TEST_F(AllowedErrorTests, QueueCopyTextureForBrowserOomBuffer) {
|
||||
wgpu::TextureDescriptor desc = {};
|
||||
desc.size = {4, 4};
|
||||
desc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
|
||||
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding;
|
||||
desc.format = wgpu::TextureFormat::RGBA8Unorm;
|
||||
|
||||
wgpu::ImageCopyTexture src = {};
|
||||
src.texture = device.CreateTexture(&desc);
|
||||
wgpu::ImageCopyTexture dst = {};
|
||||
dst.texture = device.CreateTexture(&desc);
|
||||
wgpu::Extent3D size = {4, 4};
|
||||
wgpu::CopyTextureForBrowserOptions options = {};
|
||||
|
||||
// Copying texture for browser internally allocates a buffer which we will cause to fail here.
|
||||
EXPECT_CALL(*mDeviceMock, CreateBufferImpl)
|
||||
.WillOnce(Return(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage)));
|
||||
|
||||
// Expect the device lost because of the error.
|
||||
EXPECT_CALL(mDeviceLostCb,
|
||||
Call(WGPUDeviceLostReason_Undefined, HasSubstr(kOomErrorMessage), this))
|
||||
.Times(1);
|
||||
device.GetQueue().CopyTextureForBrowser(&src, &dst, &size, &options);
|
||||
}
|
||||
|
||||
// Even though OOM is allowed in buffer creation, when creating a buffer in internal workaround the
|
||||
// OOM should be masked as a device loss.
|
||||
TEST_F(AllowedErrorTests, QueueCopyExternalTextureForBrowserOomBuffer) {
|
||||
wgpu::TextureDescriptor textureDesc = {};
|
||||
textureDesc.size = {4, 4};
|
||||
textureDesc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
|
||||
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding;
|
||||
textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
|
||||
|
||||
wgpu::TextureViewDescriptor textureViewDesc = {};
|
||||
textureViewDesc.format = wgpu::TextureFormat::RGBA8Unorm;
|
||||
|
||||
wgpu::ExternalTextureDescriptor externalTextureDesc = {};
|
||||
std::array<float, 12> placeholderConstantArray;
|
||||
externalTextureDesc.yuvToRgbConversionMatrix = placeholderConstantArray.data();
|
||||
externalTextureDesc.gamutConversionMatrix = placeholderConstantArray.data();
|
||||
externalTextureDesc.srcTransferFunctionParameters = placeholderConstantArray.data();
|
||||
externalTextureDesc.dstTransferFunctionParameters = placeholderConstantArray.data();
|
||||
externalTextureDesc.visibleSize = {4, 4};
|
||||
externalTextureDesc.plane0 = device.CreateTexture(&textureDesc).CreateView(&textureViewDesc);
|
||||
|
||||
wgpu::ImageCopyExternalTexture src = {};
|
||||
src.externalTexture = device.CreateExternalTexture(&externalTextureDesc);
|
||||
wgpu::ImageCopyTexture dst = {};
|
||||
dst.texture = device.CreateTexture(&textureDesc);
|
||||
wgpu::Extent3D size = {4, 4};
|
||||
wgpu::CopyTextureForBrowserOptions options = {};
|
||||
|
||||
// Copying texture for browser internally allocates a buffer which we will cause to fail here.
|
||||
EXPECT_CALL(*mDeviceMock, CreateBufferImpl)
|
||||
.WillOnce(Return(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage)));
|
||||
|
||||
// Expect the device lost because of the error.
|
||||
EXPECT_CALL(mDeviceLostCb,
|
||||
Call(WGPUDeviceLostReason_Undefined, HasSubstr(kOomErrorMessage), this))
|
||||
.Times(1);
|
||||
device.GetQueue().CopyExternalTextureForBrowser(&src, &dst, &size, &options);
|
||||
}
|
||||
|
||||
// OOM error from synchronously initializing a compute pipeline should result in a device loss.
|
||||
TEST_F(AllowedErrorTests, CreateComputePipeline) {
|
||||
Ref<ShaderModuleMock> csModule = ShaderModuleMock::Create(mDeviceMock, kComputeShader.data());
|
||||
|
||||
ComputePipelineDescriptor desc = {};
|
||||
desc.compute.module = csModule.Get();
|
||||
desc.compute.entryPoint = "main";
|
||||
|
||||
Ref<ComputePipelineMock> computePipelineMock = ComputePipelineMock::Create(mDeviceMock, &desc);
|
||||
EXPECT_CALL(*computePipelineMock.Get(), Initialize)
|
||||
.WillOnce(Return(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage)));
|
||||
EXPECT_CALL(*mDeviceMock, CreateUninitializedComputePipelineImpl)
|
||||
.WillOnce(Return(ByMove(std::move(computePipelineMock))));
|
||||
|
||||
// Expect the device lost because of the error.
|
||||
EXPECT_CALL(mDeviceLostCb,
|
||||
Call(WGPUDeviceLostReason_Undefined, HasSubstr(kOomErrorMessage), this))
|
||||
.Times(1);
|
||||
device.CreateComputePipeline(ToCppAPI(&desc));
|
||||
}
|
||||
|
||||
// OOM error from synchronously initializing a render pipeline should result in a device loss.
|
||||
TEST_F(AllowedErrorTests, CreateRenderPipeline) {
|
||||
Ref<ShaderModuleMock> vsModule = ShaderModuleMock::Create(mDeviceMock, kVertexShader.data());
|
||||
|
||||
RenderPipelineDescriptor desc = {};
|
||||
desc.vertex.module = vsModule.Get();
|
||||
desc.vertex.entryPoint = "main";
|
||||
|
||||
Ref<RenderPipelineMock> renderPipelineMock = RenderPipelineMock::Create(mDeviceMock, &desc);
|
||||
EXPECT_CALL(*renderPipelineMock.Get(), Initialize)
|
||||
.WillOnce(Return(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage)));
|
||||
EXPECT_CALL(*mDeviceMock, CreateUninitializedRenderPipelineImpl)
|
||||
.WillOnce(Return(ByMove(std::move(renderPipelineMock))));
|
||||
|
||||
// Expect the device lost because of the error.
|
||||
EXPECT_CALL(mDeviceLostCb,
|
||||
Call(WGPUDeviceLostReason_Undefined, HasSubstr(kOomErrorMessage), this))
|
||||
.Times(1);
|
||||
device.CreateRenderPipeline(ToCppAPI(&desc));
|
||||
}
|
||||
|
||||
//
|
||||
// Exercise async APIs where OOM errors do NOT currently cause a device lost.
|
||||
//
|
||||
|
||||
// OOM error from asynchronously initializing a compute pipeline should not result in a device loss.
|
||||
TEST_F(AllowedErrorTests, CreateComputePipelineAsync) {
|
||||
Ref<ShaderModuleMock> csModule = ShaderModuleMock::Create(mDeviceMock, kComputeShader.data());
|
||||
|
||||
ComputePipelineDescriptor desc = {};
|
||||
desc.compute.module = csModule.Get();
|
||||
desc.compute.entryPoint = "main";
|
||||
|
||||
Ref<ComputePipelineMock> computePipelineMock = ComputePipelineMock::Create(mDeviceMock, &desc);
|
||||
EXPECT_CALL(*computePipelineMock.Get(), Initialize)
|
||||
.WillOnce(Return(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage)));
|
||||
EXPECT_CALL(*mDeviceMock, CreateUninitializedComputePipelineImpl)
|
||||
.WillOnce(Return(ByMove(std::move(computePipelineMock))));
|
||||
|
||||
MockCallback<wgpu::CreateComputePipelineAsyncCallback> cb;
|
||||
EXPECT_CALL(
|
||||
cb, Call(WGPUCreatePipelineAsyncStatus_InternalError, _, HasSubstr(kOomErrorMessage), this))
|
||||
.Times(1);
|
||||
|
||||
device.CreateComputePipelineAsync(ToCppAPI(&desc), cb.Callback(), cb.MakeUserdata(this));
|
||||
device.Tick();
|
||||
|
||||
// Device lost should only happen because of destruction.
|
||||
EXPECT_CALL(mDeviceLostCb, Call(WGPUDeviceLostReason_Destroyed, _, this)).Times(1);
|
||||
}
|
||||
|
||||
// OOM error from asynchronously initializing a render pipeline should not result in a device loss.
|
||||
TEST_F(AllowedErrorTests, CreateRenderPipelineAsync) {
|
||||
Ref<ShaderModuleMock> vsModule = ShaderModuleMock::Create(mDeviceMock, kVertexShader.data());
|
||||
|
||||
RenderPipelineDescriptor desc = {};
|
||||
desc.vertex.module = vsModule.Get();
|
||||
desc.vertex.entryPoint = "main";
|
||||
|
||||
Ref<RenderPipelineMock> renderPipelineMock = RenderPipelineMock::Create(mDeviceMock, &desc);
|
||||
EXPECT_CALL(*renderPipelineMock.Get(), Initialize)
|
||||
.WillOnce(Return(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage)));
|
||||
EXPECT_CALL(*mDeviceMock, CreateUninitializedRenderPipelineImpl)
|
||||
.WillOnce(Return(ByMove(std::move(renderPipelineMock))));
|
||||
|
||||
MockCallback<wgpu::CreateRenderPipelineAsyncCallback> cb;
|
||||
EXPECT_CALL(
|
||||
cb, Call(WGPUCreatePipelineAsyncStatus_InternalError, _, HasSubstr(kOomErrorMessage), this))
|
||||
.Times(1);
|
||||
|
||||
device.CreateRenderPipelineAsync(ToCppAPI(&desc), cb.Callback(), cb.MakeUserdata(this));
|
||||
device.Tick();
|
||||
|
||||
// Device lost should only happen because of destruction.
|
||||
EXPECT_CALL(mDeviceLostCb, Call(WGPUDeviceLostReason_Destroyed, _, this)).Times(1);
|
||||
}
|
||||
|
||||
//
|
||||
// Exercise APIs where OOM error are allowed and surfaced.
|
||||
//
|
||||
|
||||
// OOM error from buffer creation is allowed and surfaced directly.
|
||||
TEST_F(AllowedErrorTests, CreateBuffer) {
|
||||
EXPECT_CALL(*mDeviceMock, CreateBufferImpl)
|
||||
.WillOnce(Return(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage)));
|
||||
|
||||
// Expect the OOM error.
|
||||
EXPECT_CALL(mDeviceErrorCb, Call(WGPUErrorType_OutOfMemory, HasSubstr(kOomErrorMessage), this))
|
||||
.Times(1);
|
||||
|
||||
wgpu::BufferDescriptor desc = {};
|
||||
desc.usage = wgpu::BufferUsage::Uniform;
|
||||
desc.size = 16;
|
||||
device.CreateBuffer(&desc);
|
||||
|
||||
// Device lost should only happen because of destruction.
|
||||
EXPECT_CALL(mDeviceLostCb, Call(WGPUDeviceLostReason_Destroyed, _, this)).Times(1);
|
||||
}
|
||||
|
||||
// OOM error from texture creation is allowed and surfaced directly.
|
||||
TEST_F(AllowedErrorTests, CreateTexture) {
|
||||
EXPECT_CALL(*mDeviceMock, CreateTextureImpl)
|
||||
.WillOnce(Return(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage)));
|
||||
|
||||
// Expect the OOM error.
|
||||
EXPECT_CALL(mDeviceErrorCb, Call(WGPUErrorType_OutOfMemory, HasSubstr(kOomErrorMessage), this))
|
||||
.Times(1);
|
||||
|
||||
wgpu::TextureDescriptor desc = {};
|
||||
desc.usage = wgpu::TextureUsage::CopySrc;
|
||||
desc.size = {4, 4};
|
||||
desc.format = wgpu::TextureFormat::RGBA8Unorm;
|
||||
device.CreateTexture(&desc);
|
||||
|
||||
// Device lost should only happen because of destruction.
|
||||
EXPECT_CALL(mDeviceLostCb, Call(WGPUDeviceLostReason_Destroyed, _, this)).Times(1);
|
||||
}
|
||||
|
||||
// OOM error from query set creation is allowed and surfaced directly.
|
||||
TEST_F(AllowedErrorTests, CreateQuerySet) {
|
||||
EXPECT_CALL(*mDeviceMock, CreateQuerySetImpl)
|
||||
.WillOnce(Return(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage)));
|
||||
|
||||
// Expect the OOM error.
|
||||
EXPECT_CALL(mDeviceErrorCb, Call(WGPUErrorType_OutOfMemory, HasSubstr(kOomErrorMessage), this))
|
||||
.Times(1);
|
||||
|
||||
wgpu::QuerySetDescriptor desc = {};
|
||||
desc.type = wgpu::QueryType::Occlusion;
|
||||
desc.count = 1;
|
||||
device.CreateQuerySet(&desc);
|
||||
|
||||
// Device lost should only happen because of destruction.
|
||||
EXPECT_CALL(mDeviceLostCb, Call(WGPUDeviceLostReason_Destroyed, _, this)).Times(1);
|
||||
}
|
||||
|
||||
TEST_F(AllowedErrorTests, InjectError) {
|
||||
// Expect the OOM error.
|
||||
EXPECT_CALL(mDeviceErrorCb, Call(WGPUErrorType_OutOfMemory, HasSubstr(kOomErrorMessage), this))
|
||||
.Times(1);
|
||||
|
||||
device.InjectError(wgpu::ErrorType::OutOfMemory, kOomErrorMessage);
|
||||
|
||||
// Device lost should only happen because of destruction.
|
||||
EXPECT_CALL(mDeviceLostCb, Call(WGPUDeviceLostReason_Destroyed, _, this)).Times(1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace dawn::native
|
|
@ -16,9 +16,18 @@
|
|||
|
||||
namespace dawn::native {
|
||||
|
||||
using ::testing::Return;
|
||||
|
||||
BufferMock::BufferMock(DeviceMock* device, const BufferDescriptor* descriptor)
|
||||
: BufferBase(device, descriptor) {
|
||||
mBackingData = std::unique_ptr<uint8_t[]>(new uint8_t[GetSize()]);
|
||||
mAllocatedSize = GetSize();
|
||||
|
||||
ON_CALL(*this, DestroyImpl).WillByDefault([this]() { this->BufferBase::DestroyImpl(); });
|
||||
ON_CALL(*this, GetMappedPointer).WillByDefault(Return(mBackingData.get()));
|
||||
ON_CALL(*this, IsCPUWritableAtCreation).WillByDefault([this]() {
|
||||
return (GetUsage() & (wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite)) != 0;
|
||||
});
|
||||
}
|
||||
|
||||
BufferMock::~BufferMock() = default;
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#ifndef SRC_DAWN_TESTS_UNITTESTS_NATIVE_MOCKS_BUFFERMOCK_H_
|
||||
#define SRC_DAWN_TESTS_UNITTESTS_NATIVE_MOCKS_BUFFERMOCK_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
#include "dawn/native/Buffer.h"
|
||||
|
@ -38,6 +40,9 @@ class BufferMock : public BufferBase {
|
|||
MOCK_METHOD(void*, GetMappedPointer, (), (override));
|
||||
|
||||
MOCK_METHOD(bool, IsCPUWritableAtCreation, (), (const, override));
|
||||
|
||||
private:
|
||||
std::unique_ptr<uint8_t[]> mBackingData;
|
||||
};
|
||||
|
||||
} // namespace dawn::native
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "dawn/tests/unittests/native/mocks/BindGroupLayoutMock.h"
|
||||
#include "dawn/tests/unittests/native/mocks/BindGroupMock.h"
|
||||
#include "dawn/tests/unittests/native/mocks/BufferMock.h"
|
||||
#include "dawn/tests/unittests/native/mocks/CommandBufferMock.h"
|
||||
#include "dawn/tests/unittests/native/mocks/ComputePipelineMock.h"
|
||||
#include "dawn/tests/unittests/native/mocks/ExternalTextureMock.h"
|
||||
#include "dawn/tests/unittests/native/mocks/PipelineLayoutMock.h"
|
||||
|
@ -53,6 +54,12 @@ DeviceMock::DeviceMock() {
|
|||
[this](const BufferDescriptor* descriptor) -> ResultOrError<Ref<BufferBase>> {
|
||||
return AcquireRef(new NiceMock<BufferMock>(this, descriptor));
|
||||
}));
|
||||
ON_CALL(*this, CreateCommandBuffer)
|
||||
.WillByDefault(WithArgs<0, 1>(
|
||||
[this](CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)
|
||||
-> ResultOrError<Ref<CommandBufferBase>> {
|
||||
return AcquireRef(new NiceMock<CommandBufferMock>(this, encoder, descriptor));
|
||||
}));
|
||||
ON_CALL(*this, CreateExternalTextureImpl)
|
||||
.WillByDefault(WithArgs<0>([this](const ExternalTextureDescriptor* descriptor)
|
||||
-> ResultOrError<Ref<ExternalTextureBase>> {
|
||||
|
|
Loading…
Reference in New Issue