diff --git a/src/dawn_native/BindGroupLayout.cpp b/src/dawn_native/BindGroupLayout.cpp index 5c2ea42e11..f04769237b 100644 --- a/src/dawn_native/BindGroupLayout.cpp +++ b/src/dawn_native/BindGroupLayout.cpp @@ -360,7 +360,8 @@ namespace dawn_native { 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 @@ namespace dawn_native { 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 5f75eb62ea..61b811f990 100644 --- a/src/dawn_native/BindGroupLayout.h +++ b/src/dawn_native/BindGroupLayout.h @@ -42,13 +42,17 @@ namespace dawn_native { // into a packed range of |BindingIndex| integers. class BindGroupLayoutBase : public ApiObjectBase, public CachedObject { 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 @@ namespace dawn_native { // ignoring their compatibility groups. bool IsLayoutEqual(const BindGroupLayoutBase* other, bool excludePipelineCompatibiltyToken = false) const; - PipelineCompatibilityToken GetPipelineCompatibilityToken() const; struct BufferBindingData { @@ -110,6 +113,9 @@ namespace dawn_native { BindingDataPointers ComputeBindingDataPointers(void* dataStart) const; protected: + // Constructor used only for mocking and testing. + BindGroupLayoutBase(DeviceBase* device); + template SlabAllocator MakeFrontendBindGroupAllocator(size_t size) { return SlabAllocator( diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp index bc6f6a8a94..a867110d95 100644 --- a/src/dawn_native/Device.cpp +++ b/src/dawn_native/Device.cpp @@ -200,6 +200,10 @@ namespace dawn_native { } } + DeviceBase::DeviceBase() : mState(State::Alive) { + mCaches = std::make_unique(); + } + DeviceBase::~DeviceBase() = default; MaybeError DeviceBase::Initialize(QueueBase* defaultQueue) { @@ -270,7 +274,9 @@ namespace dawn_native { // 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 kObjectTypeDependencyOrder = {}; + static constexpr std::array 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 @@ namespace dawn_native { ResultOrError> 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 5d51096f0e..c2969c3ec7 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 @@ namespace dawn_native { 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 f5ed5a91a3..7e6726ed94 100644 --- a/src/dawn_native/Error.h +++ b/src/dawn_native/Error.h @@ -135,13 +135,52 @@ namespace dawn_native { // 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 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 8b14b777ae..6dd18245a8 100644 --- a/src/dawn_native/ObjectBase.h +++ b/src/dawn_native/ObjectBase.h @@ -45,6 +45,8 @@ namespace dawn_native { 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 761b8f74a7..02fb9f17d2 100644 --- a/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp +++ b/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp @@ -131,6 +131,10 @@ namespace dawn_native { namespace d3d12 { device->GetSamplerStagingDescriptorAllocator(GetSamplerDescriptorCount()); } + BindGroupLayout::~BindGroupLayout() { + DestroyApiObject(); + } + ResultOrError> BindGroupLayout::AllocateBindGroup( Device* device, const BindGroupDescriptor* descriptor) { diff --git a/src/dawn_native/d3d12/BindGroupLayoutD3D12.h b/src/dawn_native/d3d12/BindGroupLayoutD3D12.h index abf67021a0..e55c3df28c 100644 --- a/src/dawn_native/d3d12/BindGroupLayoutD3D12.h +++ b/src/dawn_native/d3d12/BindGroupLayoutD3D12.h @@ -64,7 +64,7 @@ namespace dawn_native { namespace d3d12 { 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 1d2c2a9334..bbbc959b58 100644 --- a/src/dawn_native/metal/BindGroupLayoutMTL.h +++ b/src/dawn_native/metal/BindGroupLayoutMTL.h @@ -36,7 +36,7 @@ namespace dawn_native { namespace metal { BindGroupLayout(DeviceBase* device, const BindGroupLayoutDescriptor* descriptor, PipelineCompatibilityToken pipelineCompatibilityToken); - ~BindGroupLayout() override = default; + ~BindGroupLayout() override; SlabAllocator mBindGroupAllocator; }; diff --git a/src/dawn_native/metal/BindGroupLayoutMTL.mm b/src/dawn_native/metal/BindGroupLayoutMTL.mm index 5d748c1f78..a1c8255c39 100644 --- a/src/dawn_native/metal/BindGroupLayoutMTL.mm +++ b/src/dawn_native/metal/BindGroupLayoutMTL.mm @@ -33,6 +33,10 @@ namespace dawn_native { namespace metal { mBindGroupAllocator(MakeFrontendBindGroupAllocator(4096)) { } + BindGroupLayout::~BindGroupLayout() { + DestroyApiObject(); + } + Ref 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 4f08b1bd33..17030dd5b1 100644 --- a/src/dawn_native/null/DeviceNull.cpp +++ b/src/dawn_native/null/DeviceNull.cpp @@ -263,6 +263,18 @@ namespace dawn_native { namespace null { 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 be1c6135b4..0f2913922f 100644 --- a/src/dawn_native/null/DeviceNull.h +++ b/src/dawn_native/null/DeviceNull.h @@ -40,7 +40,7 @@ namespace dawn_native { namespace null { class Adapter; class BindGroup; - using BindGroupLayout = BindGroupLayoutBase; + class BindGroupLayout; class Buffer; class CommandBuffer; using ComputePipeline = ComputePipelineBase; @@ -200,6 +200,16 @@ namespace dawn_native { namespace null { ~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 d008b1d48a..99cd5c2345 100644 --- a/src/dawn_native/opengl/BindGroupLayoutGL.cpp +++ b/src/dawn_native/opengl/BindGroupLayoutGL.cpp @@ -25,6 +25,10 @@ namespace dawn_native { namespace opengl { mBindGroupAllocator(MakeFrontendBindGroupAllocator(4096)) { } + BindGroupLayout::~BindGroupLayout() { + DestroyApiObject(); + } + Ref 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 136bd0a7e5..5061b02012 100644 --- a/src/dawn_native/opengl/BindGroupLayoutGL.h +++ b/src/dawn_native/opengl/BindGroupLayoutGL.h @@ -33,7 +33,7 @@ namespace dawn_native { namespace opengl { void DeallocateBindGroup(BindGroup* bindGroup); private: - ~BindGroupLayout() override = default; + ~BindGroupLayout() override; SlabAllocator mBindGroupAllocator; }; diff --git a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp index b4647582e9..eb5282294c 100644 --- a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp +++ b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp @@ -161,6 +161,7 @@ namespace dawn_native { namespace vulkan { 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 84a74f56a8..6105fb1b64 100644 --- a/src/tests/BUILD.gn +++ b/src/tests/BUILD.gn @@ -126,10 +126,29 @@ dawn_json_generator("mock_webgpu_gen") { ] } +# 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 @@ test("dawn_unittests") { "${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 @@ test("dawn_unittests") { "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 0000000000..d39c8e0d8e --- /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 + +#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 0000000000..94fdafbbec --- /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 + +#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 042784db6d..ff7b7fe556 100644 --- a/src/tests/unittests/ErrorTests.cpp +++ b/src/tests/unittests/ErrorTests.cpp @@ -243,6 +243,78 @@ namespace { ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); } + // Check DAWN_TRY_ASSIGN handles successes correctly. + TEST(ErrorTests, TRY_RESULT_CLEANUP_Success) { + auto ReturnSuccess = []() -> ResultOrError { 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* result = nullptr; + DAWN_TRY_ASSIGN_WITH_CLEANUP(result, ReturnSuccess(), { tryCleanup = true; }); + tryReturned = false; + + EXPECT_EQ(result, &dummySuccess); + return result; + }; + + ResultOrError 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 { + 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* 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 result = Try(); + ASSERT_TRUE(result.IsError()); + + std::unique_ptr 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 { + 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 0000000000..d1974035ec --- /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 + +#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 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 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 0000000000..6f8dba53b2 --- /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 + +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 0000000000..e83e37a2f9 --- /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 + +namespace dawn_native { + + class DeviceMock : public DeviceBase { + public: + // Exposes some protected functions for testing purposes. + using DeviceBase::DestroyObjects; + using DeviceBase::SetToggle; + + MOCK_METHOD(ResultOrError>, + CreateCommandBuffer, + (CommandEncoder*, const CommandBufferDescriptor*), + (override)); + + MOCK_METHOD(ResultOrError>, + 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>, + CreateBindGroupImpl, + (const BindGroupDescriptor*), + (override)); + MOCK_METHOD(ResultOrError>, + CreateBindGroupLayoutImpl, + (const BindGroupLayoutDescriptor*, PipelineCompatibilityToken), + (override)); + MOCK_METHOD(ResultOrError>, + CreateBufferImpl, + (const BufferDescriptor*), + (override)); + MOCK_METHOD(ResultOrError>, + CreateComputePipelineImpl, + (const ComputePipelineDescriptor*), + (override)); + MOCK_METHOD(ResultOrError>, + CreatePipelineLayoutImpl, + (const PipelineLayoutDescriptor*), + (override)); + MOCK_METHOD(ResultOrError>, + CreateQuerySetImpl, + (const QuerySetDescriptor*), + (override)); + MOCK_METHOD(Ref, + CreateUninitializedRenderPipelineImpl, + (const RenderPipelineDescriptor*), + (override)); + MOCK_METHOD(ResultOrError>, + CreateSamplerImpl, + (const SamplerDescriptor*), + (override)); + MOCK_METHOD(ResultOrError>, + CreateShaderModuleImpl, + (const ShaderModuleDescriptor*, ShaderModuleParseResult*), + (override)); + MOCK_METHOD(ResultOrError>, + CreateSwapChainImpl, + (const SwapChainDescriptor*), + (override)); + MOCK_METHOD(ResultOrError>, + CreateSwapChainImpl, + (Surface*, NewSwapChainBase*, const SwapChainDescriptor*), + (override)); + MOCK_METHOD(ResultOrError>, + CreateTextureImpl, + (const TextureDescriptor*), + (override)); + MOCK_METHOD(ResultOrError>, + CreateTextureViewImpl, + (TextureBase*, const TextureViewDescriptor*), + (override)); + + MOCK_METHOD(MaybeError, TickImpl, (), (override)); + + MOCK_METHOD(ResultOrError, CheckAndUpdateCompletedSerials, (), (override)); + MOCK_METHOD(void, DestroyImpl, (), (override)); + MOCK_METHOD(MaybeError, WaitForIdleForDestruction, (), (override)); + }; + +} // namespace dawn_native + +#endif // TESTS_UNITTESTS_NATIVE_MOCKS_DEVICE_MOCK_H_