Adds destroy handling for BindGroupLayout without new backend changes yet.
- Start tracking BindGroupLayout objects at construction
- Utilizes untrack tag for blueprint layouts for caching purposes
- Adds dawn native test file for testing utilities that require static dawn native lib
- Adds testing macros and mocks for simple sanity unit testing
Bug: dawn:628
Change-Id: Ic85b60e9574e67cc5fc1804cc5300cd1f3a0f6fd
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/65862
Commit-Queue: Loko Kung <lokokung@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/BindGroupLayout.cpp b/src/dawn_native/BindGroupLayout.cpp
index 5c2ea42..f047692 100644
--- a/src/dawn_native/BindGroupLayout.cpp
+++ b/src/dawn_native/BindGroupLayout.cpp
@@ -360,7 +360,8 @@
BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device,
const BindGroupLayoutDescriptor* descriptor,
- PipelineCompatibilityToken pipelineCompatibilityToken)
+ PipelineCompatibilityToken pipelineCompatibilityToken,
+ ApiObjectBase::UntrackedByDeviceTag tag)
: ApiObjectBase(device, kLabelNotImplemented),
mBindingInfo(BindingIndex(descriptor->entryCount)),
mPipelineCompatibilityToken(pipelineCompatibilityToken) {
@@ -387,15 +388,31 @@
ASSERT(mBindingInfo.size() <= kMaxBindingsPerPipelineLayoutTyped);
}
+ BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device,
+ const BindGroupLayoutDescriptor* descriptor,
+ PipelineCompatibilityToken pipelineCompatibilityToken)
+ : BindGroupLayoutBase(device, descriptor, pipelineCompatibilityToken, kUntrackedByDevice) {
+ TrackInDevice();
+ }
+
BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag)
: ApiObjectBase(device, tag) {
}
- BindGroupLayoutBase::~BindGroupLayoutBase() {
- // Do not uncache the actual cached object if we are a blueprint
- if (IsCachedReference()) {
- GetDevice()->UncacheBindGroupLayout(this);
+ BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device)
+ : ApiObjectBase(device, kLabelNotImplemented) {
+ TrackInDevice();
+ }
+
+ bool BindGroupLayoutBase::DestroyApiObject() {
+ bool wasDestroyed = ApiObjectBase::DestroyApiObject();
+ if (wasDestroyed) {
+ // Do not uncache the actual cached object if we are a blueprint
+ if (IsCachedReference()) {
+ GetDevice()->UncacheBindGroupLayout(this);
+ }
}
+ return wasDestroyed;
}
// static
diff --git a/src/dawn_native/BindGroupLayout.h b/src/dawn_native/BindGroupLayout.h
index 5f75eb6..61b811f 100644
--- a/src/dawn_native/BindGroupLayout.h
+++ b/src/dawn_native/BindGroupLayout.h
@@ -44,11 +44,15 @@
public:
BindGroupLayoutBase(DeviceBase* device,
const BindGroupLayoutDescriptor* descriptor,
+ PipelineCompatibilityToken pipelineCompatibilityToken,
+ ApiObjectBase::UntrackedByDeviceTag tag);
+ BindGroupLayoutBase(DeviceBase* device,
+ const BindGroupLayoutDescriptor* descriptor,
PipelineCompatibilityToken pipelineCompatibilityToken);
- ~BindGroupLayoutBase() override;
static BindGroupLayoutBase* MakeError(DeviceBase* device);
+ bool DestroyApiObject() override;
ObjectType GetType() const override;
// A map from the BindingNumber to its packed BindingIndex.
@@ -85,7 +89,6 @@
// ignoring their compatibility groups.
bool IsLayoutEqual(const BindGroupLayoutBase* other,
bool excludePipelineCompatibiltyToken = false) const;
-
PipelineCompatibilityToken GetPipelineCompatibilityToken() const;
struct BufferBindingData {
@@ -110,6 +113,9 @@
BindingDataPointers ComputeBindingDataPointers(void* dataStart) const;
protected:
+ // Constructor used only for mocking and testing.
+ BindGroupLayoutBase(DeviceBase* device);
+
template <typename BindGroup>
SlabAllocator<BindGroup> MakeFrontendBindGroupAllocator(size_t size) {
return SlabAllocator<BindGroup>(
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index bc6f6a8..a867110 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -200,6 +200,10 @@
}
}
+ DeviceBase::DeviceBase() : mState(State::Alive) {
+ mCaches = std::make_unique<DeviceBase::Caches>();
+ }
+
DeviceBase::~DeviceBase() = default;
MaybeError DeviceBase::Initialize(QueueBase* defaultQueue) {
@@ -270,7 +274,9 @@
// that this only considers the immediate frontend dependencies, while backend objects could
// add complications and extra dependencies.
// TODO(dawn/628) Add types into the array as they are implemented.
- static constexpr std::array<ObjectType, 0> kObjectTypeDependencyOrder = {};
+ static constexpr std::array<ObjectType, 1> kObjectTypeDependencyOrder = {
+ ObjectType::BindGroupLayout,
+ };
// We first move all objects out from the tracking list into a separate list so that we can
// avoid locking the same mutex twice. We can then iterate across the separate list to call
@@ -631,7 +637,8 @@
ResultOrError<Ref<BindGroupLayoutBase>> DeviceBase::GetOrCreateBindGroupLayout(
const BindGroupLayoutDescriptor* descriptor,
PipelineCompatibilityToken pipelineCompatibilityToken) {
- BindGroupLayoutBase blueprint(this, descriptor, pipelineCompatibilityToken);
+ BindGroupLayoutBase blueprint(this, descriptor, pipelineCompatibilityToken,
+ ApiObjectBase::kUntrackedByDevice);
const size_t blueprintHash = blueprint.ComputeContentHash();
blueprint.SetContentHash(blueprintHash);
diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h
index 5d51096..c2969c3 100644
--- a/src/dawn_native/Device.h
+++ b/src/dawn_native/Device.h
@@ -23,6 +23,7 @@
#include "dawn_native/Limits.h"
#include "dawn_native/ObjectBase.h"
#include "dawn_native/ObjectType_autogen.h"
+#include "dawn_native/RenderPipeline.h"
#include "dawn_native/StagingBuffer.h"
#include "dawn_native/Toggles.h"
@@ -354,6 +355,9 @@
void APISetLabel(const char* label);
protected:
+ // Constructor used only for mocking and testing.
+ DeviceBase();
+
void SetToggle(Toggle toggle, bool isEnabled);
void ForceSetToggle(Toggle toggle, bool isEnabled);
diff --git a/src/dawn_native/Error.h b/src/dawn_native/Error.h
index f5ed5a9..7e6726e 100644
--- a/src/dawn_native/Error.h
+++ b/src/dawn_native/Error.h
@@ -135,13 +135,52 @@
// DAWN_TRY_ASSIGN is the same as DAWN_TRY for ResultOrError and assigns the success value, if
// any, to VAR.
-#define DAWN_TRY_ASSIGN(VAR, EXPR) \
+#define DAWN_TRY_ASSIGN(VAR, EXPR) DAWN_TRY_ASSIGN_WITH_CLEANUP(VAR, EXPR, {})
+
+ // Argument helpers are used to determine which macro implementations should be called when
+ // overloading with different number of variables.
+#define DAWN_ERROR_UNIMPLEMENTED_MACRO_(...) UNREACHABLE()
+#define DAWN_ERROR_GET_5TH_ARG_HELPER_(_1, _2, _3, _4, NAME, ...) NAME
+#define DAWN_ERROR_GET_5TH_ARG_(args) DAWN_ERROR_GET_5TH_ARG_HELPER_ args
+
+ // DAWN_TRY_ASSIGN_WITH_CLEANUP is overloaded with 2 version so that users can override the
+ // return value of the macro when necessary. This is particularly useful if the function
+ // calling the macro may want to return void instead of the error, i.e. in a test where we may
+ // just want to assert and fail if the assign cannot go through. In both the cleanup and return
+ // clauses, users can use the `error` variable to access the pointer to the acquired error.
+ //
+ // Example usages:
+ // 3 Argument Case:
+ // Result res;
+ // DAWN_TRY_ASSIGN_WITH_CLEANUP(
+ // res, GetResultOrErrorFunction(), { AddAdditionalErrorInformation(error.get()); }
+ // );
+ //
+ // 4 Argument Case:
+ // bool FunctionThatReturnsBool() {
+ // DAWN_TRY_ASSIGN_WITH_CLEANUP(
+ // res, GetResultOrErrorFunction(),
+ // { AddAdditionalErrorInformation(error.get()); },
+ // false
+ // );
+ // }
+#define DAWN_TRY_ASSIGN_WITH_CLEANUP(...) \
+ DAWN_ERROR_GET_5TH_ARG_((__VA_ARGS__, DAWN_TRY_ASSIGN_WITH_CLEANUP_IMPL_4_, \
+ DAWN_TRY_ASSIGN_WITH_CLEANUP_IMPL_3_, \
+ DAWN_ERROR_UNIMPLEMENTED_MACRO_)) \
+ (__VA_ARGS__)
+
+#define DAWN_TRY_ASSIGN_WITH_CLEANUP_IMPL_3_(VAR, EXPR, BODY) \
+ DAWN_TRY_ASSIGN_WITH_CLEANUP_IMPL_4_(VAR, EXPR, BODY, std::move(error))
+
+#define DAWN_TRY_ASSIGN_WITH_CLEANUP_IMPL_4_(VAR, EXPR, BODY, RET) \
{ \
auto DAWN_LOCAL_VAR = EXPR; \
if (DAWN_UNLIKELY(DAWN_LOCAL_VAR.IsError())) { \
std::unique_ptr<ErrorData> error = DAWN_LOCAL_VAR.AcquireError(); \
+ {BODY} /* comment to force the formatter to insert a newline */ \
error->AppendBacktrace(__FILE__, __func__, __LINE__); \
- return {std::move(error)}; \
+ return (RET); \
} \
VAR = DAWN_LOCAL_VAR.AcquireSuccess(); \
} \
diff --git a/src/dawn_native/ObjectBase.h b/src/dawn_native/ObjectBase.h
index 8b14b77..6dd1824 100644
--- a/src/dawn_native/ObjectBase.h
+++ b/src/dawn_native/ObjectBase.h
@@ -45,6 +45,8 @@
public:
struct LabelNotImplementedTag {};
static constexpr LabelNotImplementedTag kLabelNotImplemented = {};
+ struct UntrackedByDeviceTag {};
+ static constexpr UntrackedByDeviceTag kUntrackedByDevice = {};
ApiObjectBase(DeviceBase* device, LabelNotImplementedTag tag);
ApiObjectBase(DeviceBase* device, const char* label);
diff --git a/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp b/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
index 761b8f7..02fb9f1 100644
--- a/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
+++ b/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
@@ -131,6 +131,10 @@
device->GetSamplerStagingDescriptorAllocator(GetSamplerDescriptorCount());
}
+ BindGroupLayout::~BindGroupLayout() {
+ DestroyApiObject();
+ }
+
ResultOrError<Ref<BindGroup>> BindGroupLayout::AllocateBindGroup(
Device* device,
const BindGroupDescriptor* descriptor) {
diff --git a/src/dawn_native/d3d12/BindGroupLayoutD3D12.h b/src/dawn_native/d3d12/BindGroupLayoutD3D12.h
index abf6702..e55c3df 100644
--- a/src/dawn_native/d3d12/BindGroupLayoutD3D12.h
+++ b/src/dawn_native/d3d12/BindGroupLayoutD3D12.h
@@ -64,7 +64,7 @@
BindGroupLayout(Device* device,
const BindGroupLayoutDescriptor* descriptor,
PipelineCompatibilityToken pipelineCompatibilityToken);
- ~BindGroupLayout() override = default;
+ ~BindGroupLayout() override;
// Contains the offset into the descriptor heap for the given resource view. Samplers and
// non-samplers are stored in separate descriptor heaps, so the offsets should be unique
diff --git a/src/dawn_native/metal/BindGroupLayoutMTL.h b/src/dawn_native/metal/BindGroupLayoutMTL.h
index 1d2c2a9..bbbc959 100644
--- a/src/dawn_native/metal/BindGroupLayoutMTL.h
+++ b/src/dawn_native/metal/BindGroupLayoutMTL.h
@@ -36,7 +36,7 @@
BindGroupLayout(DeviceBase* device,
const BindGroupLayoutDescriptor* descriptor,
PipelineCompatibilityToken pipelineCompatibilityToken);
- ~BindGroupLayout() override = default;
+ ~BindGroupLayout() override;
SlabAllocator<BindGroup> mBindGroupAllocator;
};
diff --git a/src/dawn_native/metal/BindGroupLayoutMTL.mm b/src/dawn_native/metal/BindGroupLayoutMTL.mm
index 5d748c1..a1c8255 100644
--- a/src/dawn_native/metal/BindGroupLayoutMTL.mm
+++ b/src/dawn_native/metal/BindGroupLayoutMTL.mm
@@ -33,6 +33,10 @@
mBindGroupAllocator(MakeFrontendBindGroupAllocator<BindGroup>(4096)) {
}
+ BindGroupLayout::~BindGroupLayout() {
+ DestroyApiObject();
+ }
+
Ref<BindGroup> BindGroupLayout::AllocateBindGroup(Device* device,
const BindGroupDescriptor* descriptor) {
return AcquireRef(mBindGroupAllocator.Allocate(device, descriptor));
diff --git a/src/dawn_native/null/DeviceNull.cpp b/src/dawn_native/null/DeviceNull.cpp
index 4f08b1b..17030dd 100644
--- a/src/dawn_native/null/DeviceNull.cpp
+++ b/src/dawn_native/null/DeviceNull.cpp
@@ -263,6 +263,18 @@
BindGroupBase(device, descriptor, mBindingDataAllocation) {
}
+ // BindGroupLayout
+
+ BindGroupLayout::BindGroupLayout(DeviceBase* device,
+ const BindGroupLayoutDescriptor* descriptor,
+ PipelineCompatibilityToken pipelineCompatibilityToken)
+ : BindGroupLayoutBase(device, descriptor, pipelineCompatibilityToken) {
+ }
+
+ BindGroupLayout::~BindGroupLayout() {
+ DestroyApiObject();
+ }
+
// Buffer
Buffer::Buffer(Device* device, const BufferDescriptor* descriptor)
diff --git a/src/dawn_native/null/DeviceNull.h b/src/dawn_native/null/DeviceNull.h
index be1c613..0f29139 100644
--- a/src/dawn_native/null/DeviceNull.h
+++ b/src/dawn_native/null/DeviceNull.h
@@ -40,7 +40,7 @@
class Adapter;
class BindGroup;
- using BindGroupLayout = BindGroupLayoutBase;
+ class BindGroupLayout;
class Buffer;
class CommandBuffer;
using ComputePipeline = ComputePipelineBase;
@@ -200,6 +200,16 @@
~BindGroup() override = default;
};
+ class BindGroupLayout final : public BindGroupLayoutBase {
+ public:
+ BindGroupLayout(DeviceBase* device,
+ const BindGroupLayoutDescriptor* descriptor,
+ PipelineCompatibilityToken pipelineCompatibilityToken);
+
+ private:
+ ~BindGroupLayout() override;
+ };
+
class Buffer final : public BufferBase {
public:
Buffer(Device* device, const BufferDescriptor* descriptor);
diff --git a/src/dawn_native/opengl/BindGroupLayoutGL.cpp b/src/dawn_native/opengl/BindGroupLayoutGL.cpp
index d008b1d..99cd5c23 100644
--- a/src/dawn_native/opengl/BindGroupLayoutGL.cpp
+++ b/src/dawn_native/opengl/BindGroupLayoutGL.cpp
@@ -25,6 +25,10 @@
mBindGroupAllocator(MakeFrontendBindGroupAllocator<BindGroup>(4096)) {
}
+ BindGroupLayout::~BindGroupLayout() {
+ DestroyApiObject();
+ }
+
Ref<BindGroup> BindGroupLayout::AllocateBindGroup(Device* device,
const BindGroupDescriptor* descriptor) {
return AcquireRef(mBindGroupAllocator.Allocate(device, descriptor));
diff --git a/src/dawn_native/opengl/BindGroupLayoutGL.h b/src/dawn_native/opengl/BindGroupLayoutGL.h
index 136bd0a..5061b02 100644
--- a/src/dawn_native/opengl/BindGroupLayoutGL.h
+++ b/src/dawn_native/opengl/BindGroupLayoutGL.h
@@ -33,7 +33,7 @@
void DeallocateBindGroup(BindGroup* bindGroup);
private:
- ~BindGroupLayout() override = default;
+ ~BindGroupLayout() override;
SlabAllocator<BindGroup> mBindGroupAllocator;
};
diff --git a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
index b464758..eb52822 100644
--- a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
+++ b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
@@ -161,6 +161,7 @@
device->fn.DestroyDescriptorSetLayout(device->GetVkDevice(), mHandle, nullptr);
mHandle = VK_NULL_HANDLE;
}
+ DestroyApiObject();
}
VkDescriptorSetLayout BindGroupLayout::GetHandle() const {
diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn
index 84a74f5..6105fb1 100644
--- a/src/tests/BUILD.gn
+++ b/src/tests/BUILD.gn
@@ -126,10 +126,29 @@
]
}
+# Source code for mocks used for unit testing are separated from the rest of
+# sources so that they aren't included in non-test builds.
+source_set("dawn_native_mocks_sources") {
+ testonly = true
+
+ deps = [
+ ":gmock_and_gtest",
+ "${dawn_root}/src/dawn_native:dawn_native_sources",
+ "${dawn_root}/src/dawn_native:dawn_native_static",
+ "${dawn_root}/src/utils:dawn_utils",
+ ]
+
+ sources = [
+ "unittests/native/mocks/BindGroupLayoutMock.h",
+ "unittests/native/mocks/DeviceMock.h",
+ ]
+}
+
test("dawn_unittests") {
configs += [ "${dawn_root}/src/common:dawn_internal" ]
deps = [
+ ":dawn_native_mocks_sources",
":gmock_and_gtest",
":mock_webgpu_gen",
"${dawn_root}/src/common",
@@ -150,6 +169,8 @@
"${dawn_root}/src/dawn_wire/client/ClientMemoryTransferService_mock.h",
"${dawn_root}/src/dawn_wire/server/ServerMemoryTransferService_mock.cpp",
"${dawn_root}/src/dawn_wire/server/ServerMemoryTransferService_mock.h",
+ "DawnNativeTest.cpp",
+ "DawnNativeTest.h",
"MockCallback.h",
"ToggleParser.cpp",
"ToggleParser.h",
@@ -188,6 +209,7 @@
"unittests/SystemUtilsTests.cpp",
"unittests/ToBackendTests.cpp",
"unittests/TypedIntegerTests.cpp",
+ "unittests/native/DestroyObjectTests.cpp",
"unittests/validation/BindGroupValidationTests.cpp",
"unittests/validation/BufferValidationTests.cpp",
"unittests/validation/CommandBufferValidationTests.cpp",
diff --git a/src/tests/DawnNativeTest.cpp b/src/tests/DawnNativeTest.cpp
new file mode 100644
index 0000000..d39c8e0
--- /dev/null
+++ b/src/tests/DawnNativeTest.cpp
@@ -0,0 +1,30 @@
+// Copyright 2021 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 "absl/strings/str_cat.h"
+#include "dawn_native/ErrorData.h"
+
+namespace dawn_native {
+
+ void AddFatalDawnFailure(const char* expression, const ErrorData* error) {
+ const auto& backtrace = error->GetBacktrace();
+ GTEST_MESSAGE_AT_(
+ backtrace.at(0).file, backtrace.at(0).line,
+ absl::StrCat(expression, " returned error: ", error->GetMessage()).c_str(),
+ ::testing::TestPartResult::kFatalFailure);
+ }
+
+} // namespace dawn_native
diff --git a/src/tests/DawnNativeTest.h b/src/tests/DawnNativeTest.h
new file mode 100644
index 0000000..94fdafb
--- /dev/null
+++ b/src/tests/DawnNativeTest.h
@@ -0,0 +1,32 @@
+// Copyright 2021 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.
+
+#ifndef TESTS_DAWNNATIVETEST_H_
+#define TESTS_DAWNNATIVETEST_H_
+
+#include <gtest/gtest.h>
+
+#include "dawn_native/ErrorData.h"
+
+namespace dawn_native {
+
+ // This is similar to DAWN_TRY_ASSIGN but produces a fatal GTest error if EXPR is an error.
+#define DAWN_ASSERT_AND_ASSIGN(VAR, EXPR) \
+ DAWN_TRY_ASSIGN_WITH_CLEANUP(VAR, EXPR, {}, AddFatalDawnFailure(#EXPR, error.get()))
+
+ void AddFatalDawnFailure(const char* expression, const ErrorData* error);
+
+} // namespace dawn_native
+
+#endif // TESTS_DAWNNATIVETEST_H_
diff --git a/src/tests/unittests/ErrorTests.cpp b/src/tests/unittests/ErrorTests.cpp
index 042784d..ff7b7fe 100644
--- a/src/tests/unittests/ErrorTests.cpp
+++ b/src/tests/unittests/ErrorTests.cpp
@@ -243,6 +243,78 @@
ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage);
}
+ // Check DAWN_TRY_ASSIGN handles successes correctly.
+ TEST(ErrorTests, TRY_RESULT_CLEANUP_Success) {
+ auto ReturnSuccess = []() -> ResultOrError<int*> { return &dummySuccess; };
+
+ // We need to check that DAWN_TRY_ASSIGN_WITH_CLEANUP doesn't return on successes and the
+ // cleanup is not called.
+ bool tryReturned = true;
+ bool tryCleanup = false;
+
+ auto Try = [ReturnSuccess, &tryReturned, &tryCleanup]() -> ResultOrError<int*> {
+ int* result = nullptr;
+ DAWN_TRY_ASSIGN_WITH_CLEANUP(result, ReturnSuccess(), { tryCleanup = true; });
+ tryReturned = false;
+
+ EXPECT_EQ(result, &dummySuccess);
+ return result;
+ };
+
+ ResultOrError<int*> result = Try();
+ ASSERT_TRUE(result.IsSuccess());
+ ASSERT_FALSE(tryReturned);
+ ASSERT_FALSE(tryCleanup);
+ ASSERT_EQ(result.AcquireSuccess(), &dummySuccess);
+ }
+
+ // Check DAWN_TRY_ASSIGN handles cleanups.
+ TEST(ErrorTests, TRY_RESULT_CLEANUP_Cleanup) {
+ auto ReturnError = []() -> ResultOrError<int*> {
+ return DAWN_VALIDATION_ERROR(dummyErrorMessage);
+ };
+
+ // We need to check that DAWN_TRY_ASSIGN_WITH_CLEANUP calls cleanup when error.
+ bool tryCleanup = false;
+
+ auto Try = [ReturnError, &tryCleanup]() -> ResultOrError<int*> {
+ int* result = nullptr;
+ DAWN_TRY_ASSIGN_WITH_CLEANUP(result, ReturnError(), { tryCleanup = true; });
+ DAWN_UNUSED(result);
+
+ // DAWN_TRY_ASSIGN_WITH_CLEANUP should return before this point
+ EXPECT_FALSE(true);
+ return &dummySuccess;
+ };
+
+ ResultOrError<int*> result = Try();
+ ASSERT_TRUE(result.IsError());
+
+ std::unique_ptr<ErrorData> errorData = result.AcquireError();
+ ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage);
+ ASSERT_TRUE(tryCleanup);
+ }
+
+ // Check DAWN_TRY_ASSIGN can override return value when needed.
+ TEST(ErrorTests, TRY_RESULT_CLEANUP_OverrideReturn) {
+ auto ReturnError = []() -> ResultOrError<int*> {
+ return DAWN_VALIDATION_ERROR(dummyErrorMessage);
+ };
+
+ auto Try = [ReturnError]() -> bool {
+ int* result = nullptr;
+ DAWN_TRY_ASSIGN_WITH_CLEANUP(result, ReturnError(), {}, true);
+ DAWN_UNUSED(result);
+
+ // DAWN_TRY_ASSIGN_WITH_CLEANUP should return before this point
+ EXPECT_FALSE(true);
+ return false;
+ };
+
+ bool result = Try();
+ ASSERT_TRUE(result);
+ }
+
// Check a MaybeError can be DAWN_TRIED in a function that returns an ResultOrError
// Check DAWN_TRY handles errors correctly.
TEST(ErrorTests, TRY_ConversionToErrorOrResult) {
diff --git a/src/tests/unittests/native/DestroyObjectTests.cpp b/src/tests/unittests/native/DestroyObjectTests.cpp
new file mode 100644
index 0000000..d197403
--- /dev/null
+++ b/src/tests/unittests/native/DestroyObjectTests.cpp
@@ -0,0 +1,73 @@
+// Copyright 2021 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 "dawn_native/Toggles.h"
+#include "mocks/BindGroupLayoutMock.h"
+#include "mocks/DeviceMock.h"
+#include "tests/DawnNativeTest.h"
+
+namespace dawn_native { namespace {
+
+ using ::testing::ByMove;
+ using ::testing::InSequence;
+ using ::testing::Return;
+
+ TEST(DestroyObjectTests, BindGroupLayout) {
+ // Skipping validation on descriptors as coverage for validation is already present.
+ DeviceMock device;
+ device.SetToggle(Toggle::SkipValidation, true);
+
+ BindGroupLayoutMock* bindGroupLayoutMock = new BindGroupLayoutMock(&device);
+ EXPECT_CALL(*bindGroupLayoutMock, DestroyApiObjectImpl).Times(1);
+
+ BindGroupLayoutDescriptor desc = {};
+ Ref<BindGroupLayoutBase> bindGroupLayout;
+ EXPECT_CALL(device, CreateBindGroupLayoutImpl)
+ .WillOnce(Return(ByMove(AcquireRef(bindGroupLayoutMock))));
+ DAWN_ASSERT_AND_ASSIGN(bindGroupLayout, device.CreateBindGroupLayout(&desc));
+
+ EXPECT_TRUE(bindGroupLayout->IsAlive());
+ EXPECT_TRUE(bindGroupLayout->IsCachedReference());
+
+ bindGroupLayout->DestroyApiObject();
+ EXPECT_FALSE(bindGroupLayout->IsAlive());
+ }
+
+ // Destroying the objects on the device should result in all created objects being destroyed in
+ // order.
+ TEST(DestroyObjectTests, DestroyObjects) {
+ DeviceMock device;
+ device.SetToggle(Toggle::SkipValidation, true);
+
+ BindGroupLayoutMock* bindGroupLayoutMock = new BindGroupLayoutMock(&device);
+ {
+ InSequence seq;
+ EXPECT_CALL(*bindGroupLayoutMock, DestroyApiObjectImpl).Times(1);
+ }
+
+ BindGroupLayoutDescriptor desc = {};
+ Ref<BindGroupLayoutBase> bindGroupLayout;
+ EXPECT_CALL(device, CreateBindGroupLayoutImpl)
+ .WillOnce(Return(ByMove(AcquireRef(bindGroupLayoutMock))));
+ DAWN_ASSERT_AND_ASSIGN(bindGroupLayout, device.CreateBindGroupLayout(&desc));
+ EXPECT_TRUE(bindGroupLayout->IsAlive());
+ EXPECT_TRUE(bindGroupLayout->IsCachedReference());
+
+ device.DestroyObjects();
+ EXPECT_FALSE(bindGroupLayout->IsAlive());
+ }
+
+}} // namespace dawn_native::
diff --git a/src/tests/unittests/native/mocks/BindGroupLayoutMock.h b/src/tests/unittests/native/mocks/BindGroupLayoutMock.h
new file mode 100644
index 0000000..6f8dba5
--- /dev/null
+++ b/src/tests/unittests/native/mocks/BindGroupLayoutMock.h
@@ -0,0 +1,38 @@
+// Copyright 2021 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.
+
+#ifndef TESTS_UNITTESTS_NATIVE_MOCKS_BINDGROUPLAYOUT_MOCK_H_
+#define TESTS_UNITTESTS_NATIVE_MOCKS_BINDGROUPLAYOUT_MOCK_H_
+
+#include "dawn_native/BindGroupLayout.h"
+#include "dawn_native/Device.h"
+
+#include <gmock/gmock.h>
+
+namespace dawn_native {
+
+ class BindGroupLayoutMock final : public BindGroupLayoutBase {
+ public:
+ BindGroupLayoutMock(DeviceBase* device) : BindGroupLayoutBase(device) {
+ }
+ ~BindGroupLayoutMock() override {
+ DestroyApiObject();
+ }
+
+ MOCK_METHOD(void, DestroyApiObjectImpl, (), (override));
+ };
+
+} // namespace dawn_native
+
+#endif // TESTS_UNITTESTS_NATIVE_MOCKS_BINDGROUPLAYOUT_MOCK_H_
diff --git a/src/tests/unittests/native/mocks/DeviceMock.h b/src/tests/unittests/native/mocks/DeviceMock.h
new file mode 100644
index 0000000..e83e37a
--- /dev/null
+++ b/src/tests/unittests/native/mocks/DeviceMock.h
@@ -0,0 +1,116 @@
+// Copyright 2021 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.
+
+#ifndef TESTS_UNITTESTS_NATIVE_MOCKS_DEVICE_MOCK_H_
+#define TESTS_UNITTESTS_NATIVE_MOCKS_DEVICE_MOCK_H_
+
+#include "dawn_native/Device.h"
+
+#include <gmock/gmock.h>
+
+namespace dawn_native {
+
+ class DeviceMock : public DeviceBase {
+ public:
+ // Exposes some protected functions for testing purposes.
+ using DeviceBase::DestroyObjects;
+ using DeviceBase::SetToggle;
+
+ MOCK_METHOD(ResultOrError<Ref<CommandBufferBase>>,
+ CreateCommandBuffer,
+ (CommandEncoder*, const CommandBufferDescriptor*),
+ (override));
+
+ MOCK_METHOD(ResultOrError<std::unique_ptr<StagingBufferBase>>,
+ CreateStagingBuffer,
+ (size_t),
+ (override));
+ MOCK_METHOD(MaybeError,
+ CopyFromStagingToBuffer,
+ (StagingBufferBase*, uint64_t, BufferBase*, uint64_t, uint64_t),
+ (override));
+ MOCK_METHOD(
+ MaybeError,
+ CopyFromStagingToTexture,
+ (const StagingBufferBase*, const TextureDataLayout&, TextureCopy*, const Extent3D&),
+ (override));
+
+ MOCK_METHOD(uint32_t, GetOptimalBytesPerRowAlignment, (), (const, override));
+ MOCK_METHOD(uint64_t, GetOptimalBufferToTextureCopyOffsetAlignment, (), (const, override));
+
+ MOCK_METHOD(float, GetTimestampPeriodInNS, (), (const, override));
+
+ MOCK_METHOD(ResultOrError<Ref<BindGroupBase>>,
+ CreateBindGroupImpl,
+ (const BindGroupDescriptor*),
+ (override));
+ MOCK_METHOD(ResultOrError<Ref<BindGroupLayoutBase>>,
+ CreateBindGroupLayoutImpl,
+ (const BindGroupLayoutDescriptor*, PipelineCompatibilityToken),
+ (override));
+ MOCK_METHOD(ResultOrError<Ref<BufferBase>>,
+ CreateBufferImpl,
+ (const BufferDescriptor*),
+ (override));
+ MOCK_METHOD(ResultOrError<Ref<ComputePipelineBase>>,
+ CreateComputePipelineImpl,
+ (const ComputePipelineDescriptor*),
+ (override));
+ MOCK_METHOD(ResultOrError<Ref<PipelineLayoutBase>>,
+ CreatePipelineLayoutImpl,
+ (const PipelineLayoutDescriptor*),
+ (override));
+ MOCK_METHOD(ResultOrError<Ref<QuerySetBase>>,
+ CreateQuerySetImpl,
+ (const QuerySetDescriptor*),
+ (override));
+ MOCK_METHOD(Ref<RenderPipelineBase>,
+ CreateUninitializedRenderPipelineImpl,
+ (const RenderPipelineDescriptor*),
+ (override));
+ MOCK_METHOD(ResultOrError<Ref<SamplerBase>>,
+ CreateSamplerImpl,
+ (const SamplerDescriptor*),
+ (override));
+ MOCK_METHOD(ResultOrError<Ref<ShaderModuleBase>>,
+ CreateShaderModuleImpl,
+ (const ShaderModuleDescriptor*, ShaderModuleParseResult*),
+ (override));
+ MOCK_METHOD(ResultOrError<Ref<SwapChainBase>>,
+ CreateSwapChainImpl,
+ (const SwapChainDescriptor*),
+ (override));
+ MOCK_METHOD(ResultOrError<Ref<NewSwapChainBase>>,
+ CreateSwapChainImpl,
+ (Surface*, NewSwapChainBase*, const SwapChainDescriptor*),
+ (override));
+ MOCK_METHOD(ResultOrError<Ref<TextureBase>>,
+ CreateTextureImpl,
+ (const TextureDescriptor*),
+ (override));
+ MOCK_METHOD(ResultOrError<Ref<TextureViewBase>>,
+ CreateTextureViewImpl,
+ (TextureBase*, const TextureViewDescriptor*),
+ (override));
+
+ MOCK_METHOD(MaybeError, TickImpl, (), (override));
+
+ MOCK_METHOD(ResultOrError<ExecutionSerial>, CheckAndUpdateCompletedSerials, (), (override));
+ MOCK_METHOD(void, DestroyImpl, (), (override));
+ MOCK_METHOD(MaybeError, WaitForIdleForDestruction, (), (override));
+ };
+
+} // namespace dawn_native
+
+#endif // TESTS_UNITTESTS_NATIVE_MOCKS_DEVICE_MOCK_H_