| // 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/BindGroupMock.h" | |
| #include "mocks/BufferMock.h" | |
| #include "mocks/CommandBufferMock.h" | |
| #include "mocks/ComputePipelineMock.h" | |
| #include "mocks/DeviceMock.h" | |
| #include "mocks/ExternalTextureMock.h" | |
| #include "mocks/PipelineLayoutMock.h" | |
| #include "mocks/QuerySetMock.h" | |
| #include "mocks/RenderPipelineMock.h" | |
| #include "mocks/SamplerMock.h" | |
| #include "mocks/ShaderModuleMock.h" | |
| #include "mocks/SwapChainMock.h" | |
| #include "mocks/TextureMock.h" | |
| #include "tests/DawnNativeTest.h" | |
| #include "utils/ComboRenderPipelineDescriptor.h" | |
| namespace dawn_native { namespace { | |
| using ::testing::_; | |
| using ::testing::ByMove; | |
| using ::testing::InSequence; | |
| using ::testing::Return; | |
| using ::testing::Test; | |
| class DestroyObjectTests : public Test { | |
| public: | |
| DestroyObjectTests() : Test() { | |
| // Skipping validation on descriptors as coverage for validation is already present. | |
| mDevice.SetToggle(Toggle::SkipValidation, true); | |
| } | |
| Ref<TextureMock> GetTexture() { | |
| if (mTexture != nullptr) { | |
| return mTexture; | |
| } | |
| mTexture = | |
| AcquireRef(new TextureMock(&mDevice, TextureBase::TextureState::OwnedInternal)); | |
| EXPECT_CALL(*mTexture.Get(), DestroyImpl).Times(1); | |
| return mTexture; | |
| } | |
| Ref<PipelineLayoutMock> GetPipelineLayout() { | |
| if (mPipelineLayout != nullptr) { | |
| return mPipelineLayout; | |
| } | |
| mPipelineLayout = AcquireRef(new PipelineLayoutMock(&mDevice)); | |
| EXPECT_CALL(*mPipelineLayout.Get(), DestroyImpl).Times(1); | |
| return mPipelineLayout; | |
| } | |
| Ref<ShaderModuleMock> GetVertexShaderModule() { | |
| if (mVsModule != nullptr) { | |
| return mVsModule; | |
| } | |
| DAWN_TRY_ASSIGN_WITH_CLEANUP( | |
| mVsModule, ShaderModuleMock::Create(&mDevice, R"( | |
| [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> { | |
| return vec4<f32>(0.0, 0.0, 0.0, 1.0); | |
| })"), | |
| { ASSERT(false); }, mVsModule); | |
| EXPECT_CALL(*mVsModule.Get(), DestroyImpl).Times(1); | |
| return mVsModule; | |
| } | |
| Ref<ShaderModuleMock> GetComputeShaderModule() { | |
| if (mCsModule != nullptr) { | |
| return mCsModule; | |
| } | |
| DAWN_TRY_ASSIGN_WITH_CLEANUP( | |
| mCsModule, ShaderModuleMock::Create(&mDevice, R"( | |
| [[stage(compute), workgroup_size(1)]] fn main() { | |
| })"), | |
| { ASSERT(false); }, mCsModule); | |
| EXPECT_CALL(*mCsModule.Get(), DestroyImpl).Times(1); | |
| return mCsModule; | |
| } | |
| protected: | |
| DeviceMock mDevice; | |
| // The following lazy-initialized objects are used to facilitate creation of dependent | |
| // objects under test. | |
| Ref<TextureMock> mTexture; | |
| Ref<PipelineLayoutMock> mPipelineLayout; | |
| Ref<ShaderModuleMock> mVsModule; | |
| Ref<ShaderModuleMock> mCsModule; | |
| }; | |
| TEST_F(DestroyObjectTests, BindGroupExplicit) { | |
| BindGroupMock bindGroupMock(&mDevice); | |
| EXPECT_CALL(bindGroupMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(bindGroupMock.IsAlive()); | |
| bindGroupMock.Destroy(); | |
| EXPECT_FALSE(bindGroupMock.IsAlive()); | |
| } | |
| // If the reference count on API objects reach 0, they should delete themselves. Note that GTest | |
| // will also complain if there is a memory leak. | |
| TEST_F(DestroyObjectTests, BindGroupImplicit) { | |
| BindGroupMock* bindGroupMock = new BindGroupMock(&mDevice); | |
| EXPECT_CALL(*bindGroupMock, DestroyImpl).Times(1); | |
| { | |
| BindGroupDescriptor desc = {}; | |
| Ref<BindGroupBase> bindGroup; | |
| EXPECT_CALL(mDevice, CreateBindGroupImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(bindGroupMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(bindGroup, mDevice.CreateBindGroup(&desc)); | |
| EXPECT_TRUE(bindGroup->IsAlive()); | |
| } | |
| } | |
| TEST_F(DestroyObjectTests, BindGroupLayoutExplicit) { | |
| BindGroupLayoutMock bindGroupLayoutMock(&mDevice); | |
| EXPECT_CALL(bindGroupLayoutMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(bindGroupLayoutMock.IsAlive()); | |
| bindGroupLayoutMock.Destroy(); | |
| EXPECT_FALSE(bindGroupLayoutMock.IsAlive()); | |
| } | |
| // If the reference count on API objects reach 0, they should delete themselves. Note that GTest | |
| // will also complain if there is a memory leak. | |
| TEST_F(DestroyObjectTests, BindGroupLayoutImplicit) { | |
| BindGroupLayoutMock* bindGroupLayoutMock = new BindGroupLayoutMock(&mDevice); | |
| EXPECT_CALL(*bindGroupLayoutMock, DestroyImpl).Times(1); | |
| { | |
| BindGroupLayoutDescriptor desc = {}; | |
| Ref<BindGroupLayoutBase> bindGroupLayout; | |
| EXPECT_CALL(mDevice, CreateBindGroupLayoutImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(bindGroupLayoutMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(bindGroupLayout, mDevice.CreateBindGroupLayout(&desc)); | |
| EXPECT_TRUE(bindGroupLayout->IsAlive()); | |
| EXPECT_TRUE(bindGroupLayout->IsCachedReference()); | |
| } | |
| } | |
| TEST_F(DestroyObjectTests, BufferExplicit) { | |
| { | |
| BufferMock bufferMock(&mDevice, BufferBase::BufferState::Unmapped); | |
| EXPECT_CALL(bufferMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(bufferMock.IsAlive()); | |
| bufferMock.Destroy(); | |
| EXPECT_FALSE(bufferMock.IsAlive()); | |
| } | |
| { | |
| BufferMock bufferMock(&mDevice, BufferBase::BufferState::Mapped); | |
| { | |
| InSequence seq; | |
| EXPECT_CALL(bufferMock, DestroyImpl).Times(1); | |
| EXPECT_CALL(bufferMock, UnmapImpl).Times(1); | |
| } | |
| EXPECT_TRUE(bufferMock.IsAlive()); | |
| bufferMock.Destroy(); | |
| EXPECT_FALSE(bufferMock.IsAlive()); | |
| } | |
| } | |
| // If the reference count on API objects reach 0, they should delete themselves. Note that GTest | |
| // will also complain if there is a memory leak. | |
| TEST_F(DestroyObjectTests, BufferImplicit) { | |
| { | |
| BufferMock* bufferMock = new BufferMock(&mDevice, BufferBase::BufferState::Unmapped); | |
| EXPECT_CALL(*bufferMock, DestroyImpl).Times(1); | |
| { | |
| BufferDescriptor desc = {}; | |
| Ref<BufferBase> buffer; | |
| EXPECT_CALL(mDevice, CreateBufferImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(bufferMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(buffer, mDevice.CreateBuffer(&desc)); | |
| EXPECT_TRUE(buffer->IsAlive()); | |
| } | |
| } | |
| { | |
| BufferMock* bufferMock = new BufferMock(&mDevice, BufferBase::BufferState::Mapped); | |
| { | |
| InSequence seq; | |
| EXPECT_CALL(*bufferMock, DestroyImpl).Times(1); | |
| EXPECT_CALL(*bufferMock, UnmapImpl).Times(1); | |
| } | |
| { | |
| BufferDescriptor desc = {}; | |
| Ref<BufferBase> buffer; | |
| EXPECT_CALL(mDevice, CreateBufferImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(bufferMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(buffer, mDevice.CreateBuffer(&desc)); | |
| EXPECT_TRUE(buffer->IsAlive()); | |
| } | |
| } | |
| } | |
| TEST_F(DestroyObjectTests, CommandBufferExplicit) { | |
| CommandBufferMock commandBufferMock(&mDevice); | |
| EXPECT_CALL(commandBufferMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(commandBufferMock.IsAlive()); | |
| commandBufferMock.Destroy(); | |
| EXPECT_FALSE(commandBufferMock.IsAlive()); | |
| } | |
| // If the reference count on API objects reach 0, they should delete themselves. Note that GTest | |
| // will also complain if there is a memory leak. | |
| TEST_F(DestroyObjectTests, CommandBufferImplicit) { | |
| CommandBufferMock* commandBufferMock = new CommandBufferMock(&mDevice); | |
| EXPECT_CALL(*commandBufferMock, DestroyImpl).Times(1); | |
| { | |
| CommandBufferDescriptor desc = {}; | |
| Ref<CommandBufferBase> commandBuffer; | |
| EXPECT_CALL(mDevice, CreateCommandBuffer) | |
| .WillOnce(Return(ByMove(AcquireRef(commandBufferMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(commandBuffer, mDevice.CreateCommandBuffer(nullptr, &desc)); | |
| EXPECT_TRUE(commandBuffer->IsAlive()); | |
| } | |
| } | |
| TEST_F(DestroyObjectTests, ComputePipelineExplicit) { | |
| ComputePipelineMock computePipelineMock(&mDevice); | |
| EXPECT_CALL(computePipelineMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(computePipelineMock.IsAlive()); | |
| computePipelineMock.Destroy(); | |
| EXPECT_FALSE(computePipelineMock.IsAlive()); | |
| } | |
| // If the reference count on API objects reach 0, they should delete themselves. Note that GTest | |
| // will also complain if there is a memory leak. | |
| TEST_F(DestroyObjectTests, ComputePipelineImplicit) { | |
| // ComputePipelines usually set their hash values at construction, but the mock does not, so | |
| // we set it here. | |
| constexpr size_t hash = 0x12345; | |
| ComputePipelineMock* computePipelineMock = new ComputePipelineMock(&mDevice); | |
| computePipelineMock->SetContentHash(hash); | |
| ON_CALL(*computePipelineMock, ComputeContentHash).WillByDefault(Return(hash)); | |
| // Compute pipelines are initialized during their creation via the device. | |
| EXPECT_CALL(*computePipelineMock, Initialize).Times(1); | |
| EXPECT_CALL(*computePipelineMock, DestroyImpl).Times(1); | |
| { | |
| ComputePipelineDescriptor desc = {}; | |
| desc.layout = GetPipelineLayout().Get(); | |
| desc.compute.module = GetComputeShaderModule().Get(); | |
| Ref<ComputePipelineBase> computePipeline; | |
| EXPECT_CALL(mDevice, CreateUninitializedComputePipelineImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(computePipelineMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(computePipeline, mDevice.CreateComputePipeline(&desc)); | |
| EXPECT_TRUE(computePipeline->IsAlive()); | |
| EXPECT_TRUE(computePipeline->IsCachedReference()); | |
| } | |
| } | |
| TEST_F(DestroyObjectTests, ExternalTextureExplicit) { | |
| ExternalTextureMock externalTextureMock(&mDevice); | |
| EXPECT_CALL(externalTextureMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(externalTextureMock.IsAlive()); | |
| externalTextureMock.Destroy(); | |
| EXPECT_FALSE(externalTextureMock.IsAlive()); | |
| } | |
| // We can use an actual ExternalTexture object to test the implicit case. | |
| TEST_F(DestroyObjectTests, ExternalTextureImplicit) { | |
| ExternalTextureDescriptor desc = {}; | |
| Ref<ExternalTextureBase> externalTexture; | |
| DAWN_ASSERT_AND_ASSIGN(externalTexture, mDevice.CreateExternalTexture(&desc)); | |
| EXPECT_TRUE(externalTexture->IsAlive()); | |
| } | |
| TEST_F(DestroyObjectTests, PipelineLayoutExplicit) { | |
| PipelineLayoutMock pipelineLayoutMock(&mDevice); | |
| EXPECT_CALL(pipelineLayoutMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(pipelineLayoutMock.IsAlive()); | |
| pipelineLayoutMock.Destroy(); | |
| EXPECT_FALSE(pipelineLayoutMock.IsAlive()); | |
| } | |
| // If the reference count on API objects reach 0, they should delete themselves. Note that GTest | |
| // will also complain if there is a memory leak. | |
| TEST_F(DestroyObjectTests, PipelineLayoutImplicit) { | |
| PipelineLayoutMock* pipelineLayoutMock = new PipelineLayoutMock(&mDevice); | |
| EXPECT_CALL(*pipelineLayoutMock, DestroyImpl).Times(1); | |
| { | |
| PipelineLayoutDescriptor desc = {}; | |
| Ref<PipelineLayoutBase> pipelineLayout; | |
| EXPECT_CALL(mDevice, CreatePipelineLayoutImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(pipelineLayoutMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(pipelineLayout, mDevice.CreatePipelineLayout(&desc)); | |
| EXPECT_TRUE(pipelineLayout->IsAlive()); | |
| EXPECT_TRUE(pipelineLayout->IsCachedReference()); | |
| } | |
| } | |
| TEST_F(DestroyObjectTests, QuerySetExplicit) { | |
| QuerySetMock querySetMock(&mDevice); | |
| EXPECT_CALL(querySetMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(querySetMock.IsAlive()); | |
| querySetMock.Destroy(); | |
| EXPECT_FALSE(querySetMock.IsAlive()); | |
| } | |
| // If the reference count on API objects reach 0, they should delete themselves. Note that GTest | |
| // will also complain if there is a memory leak. | |
| TEST_F(DestroyObjectTests, QuerySetImplicit) { | |
| QuerySetMock* querySetMock = new QuerySetMock(&mDevice); | |
| EXPECT_CALL(*querySetMock, DestroyImpl).Times(1); | |
| { | |
| QuerySetDescriptor desc = {}; | |
| Ref<QuerySetBase> querySet; | |
| EXPECT_CALL(mDevice, CreateQuerySetImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(querySetMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(querySet, mDevice.CreateQuerySet(&desc)); | |
| EXPECT_TRUE(querySet->IsAlive()); | |
| } | |
| } | |
| TEST_F(DestroyObjectTests, RenderPipelineExplicit) { | |
| RenderPipelineMock renderPipelineMock(&mDevice); | |
| EXPECT_CALL(renderPipelineMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(renderPipelineMock.IsAlive()); | |
| renderPipelineMock.Destroy(); | |
| EXPECT_FALSE(renderPipelineMock.IsAlive()); | |
| } | |
| // If the reference count on API objects reach 0, they should delete themselves. Note that GTest | |
| // will also complain if there is a memory leak. | |
| TEST_F(DestroyObjectTests, RenderPipelineImplicit) { | |
| // RenderPipelines usually set their hash values at construction, but the mock does not, so | |
| // we set it here. | |
| constexpr size_t hash = 0x12345; | |
| RenderPipelineMock* renderPipelineMock = new RenderPipelineMock(&mDevice); | |
| renderPipelineMock->SetContentHash(hash); | |
| ON_CALL(*renderPipelineMock, ComputeContentHash).WillByDefault(Return(hash)); | |
| // Render pipelines are initialized during their creation via the device. | |
| EXPECT_CALL(*renderPipelineMock, Initialize).Times(1); | |
| EXPECT_CALL(*renderPipelineMock, DestroyImpl).Times(1); | |
| { | |
| RenderPipelineDescriptor desc = {}; | |
| desc.layout = GetPipelineLayout().Get(); | |
| desc.vertex.module = GetVertexShaderModule().Get(); | |
| Ref<RenderPipelineBase> renderPipeline; | |
| EXPECT_CALL(mDevice, CreateUninitializedRenderPipelineImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(renderPipelineMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(renderPipeline, mDevice.CreateRenderPipeline(&desc)); | |
| EXPECT_TRUE(renderPipeline->IsAlive()); | |
| EXPECT_TRUE(renderPipeline->IsCachedReference()); | |
| } | |
| } | |
| TEST_F(DestroyObjectTests, SamplerExplicit) { | |
| SamplerMock samplerMock(&mDevice); | |
| EXPECT_CALL(samplerMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(samplerMock.IsAlive()); | |
| samplerMock.Destroy(); | |
| EXPECT_FALSE(samplerMock.IsAlive()); | |
| } | |
| // If the reference count on API objects reach 0, they should delete themselves. Note that GTest | |
| // will also complain if there is a memory leak. | |
| TEST_F(DestroyObjectTests, SamplerImplicit) { | |
| SamplerMock* samplerMock = new SamplerMock(&mDevice); | |
| EXPECT_CALL(*samplerMock, DestroyImpl).Times(1); | |
| { | |
| SamplerDescriptor desc = {}; | |
| Ref<SamplerBase> sampler; | |
| EXPECT_CALL(mDevice, CreateSamplerImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(samplerMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(sampler, mDevice.CreateSampler(&desc)); | |
| EXPECT_TRUE(sampler->IsAlive()); | |
| EXPECT_TRUE(sampler->IsCachedReference()); | |
| } | |
| } | |
| TEST_F(DestroyObjectTests, ShaderModuleExplicit) { | |
| ShaderModuleMock shaderModuleMock(&mDevice); | |
| EXPECT_CALL(shaderModuleMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(shaderModuleMock.IsAlive()); | |
| shaderModuleMock.Destroy(); | |
| EXPECT_FALSE(shaderModuleMock.IsAlive()); | |
| } | |
| // If the reference count on API objects reach 0, they should delete themselves. Note that GTest | |
| // will also complain if there is a memory leak. | |
| TEST_F(DestroyObjectTests, ShaderModuleImplicit) { | |
| ShaderModuleMock* shaderModuleMock = new ShaderModuleMock(&mDevice); | |
| EXPECT_CALL(*shaderModuleMock, DestroyImpl).Times(1); | |
| { | |
| ShaderModuleWGSLDescriptor wgslDesc; | |
| wgslDesc.source = R"( | |
| [[stage(compute), workgroup_size(1)]] fn main() { | |
| } | |
| )"; | |
| ShaderModuleDescriptor desc = {}; | |
| desc.nextInChain = &wgslDesc; | |
| Ref<ShaderModuleBase> shaderModule; | |
| EXPECT_CALL(mDevice, CreateShaderModuleImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(shaderModuleMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(shaderModule, mDevice.CreateShaderModule(&desc)); | |
| EXPECT_TRUE(shaderModule->IsAlive()); | |
| EXPECT_TRUE(shaderModule->IsCachedReference()); | |
| } | |
| } | |
| TEST_F(DestroyObjectTests, SwapChainExplicit) { | |
| SwapChainMock swapChainMock(&mDevice); | |
| EXPECT_CALL(swapChainMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(swapChainMock.IsAlive()); | |
| swapChainMock.Destroy(); | |
| EXPECT_FALSE(swapChainMock.IsAlive()); | |
| } | |
| // If the reference count on API objects reach 0, they should delete themselves. Note that GTest | |
| // will also complain if there is a memory leak. | |
| TEST_F(DestroyObjectTests, SwapChainImplicit) { | |
| SwapChainMock* swapChainMock = new SwapChainMock(&mDevice); | |
| EXPECT_CALL(*swapChainMock, DestroyImpl).Times(1); | |
| { | |
| SwapChainDescriptor desc = {}; | |
| Ref<SwapChainBase> swapChain; | |
| EXPECT_CALL(mDevice, CreateSwapChainImpl(_)) | |
| .WillOnce(Return(ByMove(AcquireRef(swapChainMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(swapChain, mDevice.CreateSwapChain(nullptr, &desc)); | |
| EXPECT_TRUE(swapChain->IsAlive()); | |
| } | |
| } | |
| TEST_F(DestroyObjectTests, TextureExplicit) { | |
| { | |
| TextureMock textureMock(&mDevice, TextureBase::TextureState::OwnedInternal); | |
| EXPECT_CALL(textureMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(textureMock.IsAlive()); | |
| textureMock.Destroy(); | |
| EXPECT_FALSE(textureMock.IsAlive()); | |
| } | |
| { | |
| TextureMock textureMock(&mDevice, TextureBase::TextureState::OwnedExternal); | |
| EXPECT_CALL(textureMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(textureMock.IsAlive()); | |
| textureMock.Destroy(); | |
| EXPECT_FALSE(textureMock.IsAlive()); | |
| } | |
| } | |
| // If the reference count on API objects reach 0, they should delete themselves. Note that GTest | |
| // will also complain if there is a memory leak. | |
| TEST_F(DestroyObjectTests, TextureImplicit) { | |
| { | |
| TextureMock* textureMock = | |
| new TextureMock(&mDevice, TextureBase::TextureState::OwnedInternal); | |
| EXPECT_CALL(*textureMock, DestroyImpl).Times(1); | |
| { | |
| TextureDescriptor desc = {}; | |
| Ref<TextureBase> texture; | |
| EXPECT_CALL(mDevice, CreateTextureImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(textureMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(texture, mDevice.CreateTexture(&desc)); | |
| EXPECT_TRUE(texture->IsAlive()); | |
| } | |
| } | |
| { | |
| TextureMock* textureMock = | |
| new TextureMock(&mDevice, TextureBase::TextureState::OwnedExternal); | |
| EXPECT_CALL(*textureMock, DestroyImpl).Times(1); | |
| { | |
| TextureDescriptor desc = {}; | |
| Ref<TextureBase> texture; | |
| EXPECT_CALL(mDevice, CreateTextureImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(textureMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(texture, mDevice.CreateTexture(&desc)); | |
| EXPECT_TRUE(texture->IsAlive()); | |
| } | |
| } | |
| } | |
| TEST_F(DestroyObjectTests, TextureViewExplicit) { | |
| TextureViewMock textureViewMock(GetTexture().Get()); | |
| EXPECT_CALL(textureViewMock, DestroyImpl).Times(1); | |
| EXPECT_TRUE(textureViewMock.IsAlive()); | |
| textureViewMock.Destroy(); | |
| EXPECT_FALSE(textureViewMock.IsAlive()); | |
| } | |
| // If the reference count on API objects reach 0, they should delete themselves. Note that GTest | |
| // will also complain if there is a memory leak. | |
| TEST_F(DestroyObjectTests, TextureViewImplicit) { | |
| TextureViewMock* textureViewMock = new TextureViewMock(GetTexture().Get()); | |
| EXPECT_CALL(*textureViewMock, DestroyImpl).Times(1); | |
| { | |
| TextureViewDescriptor desc = {}; | |
| Ref<TextureViewBase> textureView; | |
| EXPECT_CALL(mDevice, CreateTextureViewImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(textureViewMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(textureView, | |
| mDevice.CreateTextureView(GetTexture().Get(), &desc)); | |
| EXPECT_TRUE(textureView->IsAlive()); | |
| } | |
| } | |
| // Destroying the objects on the mDevice should result in all created objects being destroyed in | |
| // order. | |
| TEST_F(DestroyObjectTests, DestroyObjects) { | |
| BindGroupMock* bindGroupMock = new BindGroupMock(&mDevice); | |
| BindGroupLayoutMock* bindGroupLayoutMock = new BindGroupLayoutMock(&mDevice); | |
| BufferMock* bufferMock = new BufferMock(&mDevice, BufferBase::BufferState::Unmapped); | |
| CommandBufferMock* commandBufferMock = new CommandBufferMock(&mDevice); | |
| ComputePipelineMock* computePipelineMock = new ComputePipelineMock(&mDevice); | |
| PipelineLayoutMock* pipelineLayoutMock = new PipelineLayoutMock(&mDevice); | |
| QuerySetMock* querySetMock = new QuerySetMock(&mDevice); | |
| RenderPipelineMock* renderPipelineMock = new RenderPipelineMock(&mDevice); | |
| SamplerMock* samplerMock = new SamplerMock(&mDevice); | |
| ShaderModuleMock* shaderModuleMock = new ShaderModuleMock(&mDevice); | |
| SwapChainMock* swapChainMock = new SwapChainMock(&mDevice); | |
| TextureMock* textureMock = | |
| new TextureMock(&mDevice, TextureBase::TextureState::OwnedInternal); | |
| TextureViewMock* textureViewMock = new TextureViewMock(GetTexture().Get()); | |
| { | |
| InSequence seq; | |
| EXPECT_CALL(*commandBufferMock, DestroyImpl).Times(1); | |
| EXPECT_CALL(*renderPipelineMock, DestroyImpl).Times(1); | |
| EXPECT_CALL(*computePipelineMock, DestroyImpl).Times(1); | |
| EXPECT_CALL(*pipelineLayoutMock, DestroyImpl).Times(1); | |
| EXPECT_CALL(*swapChainMock, DestroyImpl).Times(1); | |
| EXPECT_CALL(*bindGroupMock, DestroyImpl).Times(1); | |
| EXPECT_CALL(*bindGroupLayoutMock, DestroyImpl).Times(1); | |
| EXPECT_CALL(*shaderModuleMock, DestroyImpl).Times(1); | |
| EXPECT_CALL(*textureViewMock, DestroyImpl).Times(1); | |
| EXPECT_CALL(*textureMock, DestroyImpl).Times(1); | |
| EXPECT_CALL(*querySetMock, DestroyImpl).Times(1); | |
| EXPECT_CALL(*samplerMock, DestroyImpl).Times(1); | |
| EXPECT_CALL(*bufferMock, DestroyImpl).Times(1); | |
| } | |
| Ref<BindGroupBase> bindGroup; | |
| { | |
| BindGroupDescriptor desc = {}; | |
| EXPECT_CALL(mDevice, CreateBindGroupImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(bindGroupMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(bindGroup, mDevice.CreateBindGroup(&desc)); | |
| EXPECT_TRUE(bindGroup->IsAlive()); | |
| } | |
| Ref<BindGroupLayoutBase> bindGroupLayout; | |
| { | |
| BindGroupLayoutDescriptor desc = {}; | |
| EXPECT_CALL(mDevice, CreateBindGroupLayoutImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(bindGroupLayoutMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(bindGroupLayout, mDevice.CreateBindGroupLayout(&desc)); | |
| EXPECT_TRUE(bindGroupLayout->IsAlive()); | |
| EXPECT_TRUE(bindGroupLayout->IsCachedReference()); | |
| } | |
| Ref<BufferBase> buffer; | |
| { | |
| BufferDescriptor desc = {}; | |
| EXPECT_CALL(mDevice, CreateBufferImpl).WillOnce(Return(ByMove(AcquireRef(bufferMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(buffer, mDevice.CreateBuffer(&desc)); | |
| EXPECT_TRUE(buffer->IsAlive()); | |
| } | |
| Ref<CommandBufferBase> commandBuffer; | |
| { | |
| CommandBufferDescriptor desc = {}; | |
| EXPECT_CALL(mDevice, CreateCommandBuffer) | |
| .WillOnce(Return(ByMove(AcquireRef(commandBufferMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(commandBuffer, mDevice.CreateCommandBuffer(nullptr, &desc)); | |
| EXPECT_TRUE(commandBuffer->IsAlive()); | |
| } | |
| Ref<ComputePipelineBase> computePipeline; | |
| { | |
| // Compute pipelines usually set their hash values at construction, but the mock does | |
| // not, so we set it here. | |
| constexpr size_t hash = 0x12345; | |
| computePipelineMock->SetContentHash(hash); | |
| ON_CALL(*computePipelineMock, ComputeContentHash).WillByDefault(Return(hash)); | |
| // Compute pipelines are initialized during their creation via the device. | |
| EXPECT_CALL(*computePipelineMock, Initialize).Times(1); | |
| ComputePipelineDescriptor desc = {}; | |
| desc.layout = GetPipelineLayout().Get(); | |
| desc.compute.module = GetComputeShaderModule().Get(); | |
| EXPECT_CALL(mDevice, CreateUninitializedComputePipelineImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(computePipelineMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(computePipeline, mDevice.CreateComputePipeline(&desc)); | |
| EXPECT_TRUE(computePipeline->IsAlive()); | |
| EXPECT_TRUE(computePipeline->IsCachedReference()); | |
| } | |
| Ref<ExternalTextureBase> externalTexture; | |
| { | |
| ExternalTextureDescriptor desc = {}; | |
| DAWN_ASSERT_AND_ASSIGN(externalTexture, mDevice.CreateExternalTexture(&desc)); | |
| EXPECT_TRUE(externalTexture->IsAlive()); | |
| } | |
| Ref<PipelineLayoutBase> pipelineLayout; | |
| { | |
| PipelineLayoutDescriptor desc = {}; | |
| EXPECT_CALL(mDevice, CreatePipelineLayoutImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(pipelineLayoutMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(pipelineLayout, mDevice.CreatePipelineLayout(&desc)); | |
| EXPECT_TRUE(pipelineLayout->IsAlive()); | |
| EXPECT_TRUE(pipelineLayout->IsCachedReference()); | |
| } | |
| Ref<QuerySetBase> querySet; | |
| { | |
| QuerySetDescriptor desc = {}; | |
| EXPECT_CALL(mDevice, CreateQuerySetImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(querySetMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(querySet, mDevice.CreateQuerySet(&desc)); | |
| EXPECT_TRUE(querySet->IsAlive()); | |
| } | |
| Ref<RenderPipelineBase> renderPipeline; | |
| { | |
| // Render pipelines usually set their hash values at construction, but the mock does | |
| // not, so we set it here. | |
| constexpr size_t hash = 0x12345; | |
| renderPipelineMock->SetContentHash(hash); | |
| ON_CALL(*renderPipelineMock, ComputeContentHash).WillByDefault(Return(hash)); | |
| // Render pipelines are initialized during their creation via the device. | |
| EXPECT_CALL(*renderPipelineMock, Initialize).Times(1); | |
| RenderPipelineDescriptor desc = {}; | |
| desc.layout = GetPipelineLayout().Get(); | |
| desc.vertex.module = GetVertexShaderModule().Get(); | |
| EXPECT_CALL(mDevice, CreateUninitializedRenderPipelineImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(renderPipelineMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(renderPipeline, mDevice.CreateRenderPipeline(&desc)); | |
| EXPECT_TRUE(renderPipeline->IsAlive()); | |
| EXPECT_TRUE(renderPipeline->IsCachedReference()); | |
| } | |
| Ref<SamplerBase> sampler; | |
| { | |
| SamplerDescriptor desc = {}; | |
| EXPECT_CALL(mDevice, CreateSamplerImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(samplerMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(sampler, mDevice.CreateSampler(&desc)); | |
| EXPECT_TRUE(sampler->IsAlive()); | |
| EXPECT_TRUE(sampler->IsCachedReference()); | |
| } | |
| Ref<ShaderModuleBase> shaderModule; | |
| { | |
| ShaderModuleWGSLDescriptor wgslDesc; | |
| wgslDesc.source = R"( | |
| [[stage(compute), workgroup_size(1)]] fn main() { | |
| } | |
| )"; | |
| ShaderModuleDescriptor desc = {}; | |
| desc.nextInChain = &wgslDesc; | |
| EXPECT_CALL(mDevice, CreateShaderModuleImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(shaderModuleMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(shaderModule, mDevice.CreateShaderModule(&desc)); | |
| EXPECT_TRUE(shaderModule->IsAlive()); | |
| EXPECT_TRUE(shaderModule->IsCachedReference()); | |
| } | |
| Ref<SwapChainBase> swapChain; | |
| { | |
| SwapChainDescriptor desc = {}; | |
| EXPECT_CALL(mDevice, CreateSwapChainImpl(_)) | |
| .WillOnce(Return(ByMove(AcquireRef(swapChainMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(swapChain, mDevice.CreateSwapChain(nullptr, &desc)); | |
| EXPECT_TRUE(swapChain->IsAlive()); | |
| } | |
| Ref<TextureBase> texture; | |
| { | |
| TextureDescriptor desc = {}; | |
| EXPECT_CALL(mDevice, CreateTextureImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(textureMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(texture, mDevice.CreateTexture(&desc)); | |
| EXPECT_TRUE(texture->IsAlive()); | |
| } | |
| Ref<TextureViewBase> textureView; | |
| { | |
| TextureViewDescriptor desc = {}; | |
| EXPECT_CALL(mDevice, CreateTextureViewImpl) | |
| .WillOnce(Return(ByMove(AcquireRef(textureViewMock)))); | |
| DAWN_ASSERT_AND_ASSIGN(textureView, | |
| mDevice.CreateTextureView(GetTexture().Get(), &desc)); | |
| EXPECT_TRUE(textureView->IsAlive()); | |
| } | |
| mDevice.DestroyObjects(); | |
| EXPECT_FALSE(bindGroup->IsAlive()); | |
| EXPECT_FALSE(bindGroupLayout->IsAlive()); | |
| EXPECT_FALSE(buffer->IsAlive()); | |
| EXPECT_FALSE(commandBuffer->IsAlive()); | |
| EXPECT_FALSE(computePipeline->IsAlive()); | |
| EXPECT_FALSE(externalTexture->IsAlive()); | |
| EXPECT_FALSE(pipelineLayout->IsAlive()); | |
| EXPECT_FALSE(querySet->IsAlive()); | |
| EXPECT_FALSE(renderPipeline->IsAlive()); | |
| EXPECT_FALSE(sampler->IsAlive()); | |
| EXPECT_FALSE(shaderModule->IsAlive()); | |
| EXPECT_FALSE(swapChain->IsAlive()); | |
| EXPECT_FALSE(texture->IsAlive()); | |
| EXPECT_FALSE(textureView->IsAlive()); | |
| } | |
| }} // namespace dawn_native:: |