| // 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:: |