1 // Copyright 2021 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <gtest/gtest.h> 16 17 #include "dawn_native/Toggles.h" 18 #include "mocks/BindGroupLayoutMock.h" 19 #include "mocks/BindGroupMock.h" 20 #include "mocks/BufferMock.h" 21 #include "mocks/CommandBufferMock.h" 22 #include "mocks/ComputePipelineMock.h" 23 #include "mocks/DeviceMock.h" 24 #include "mocks/ExternalTextureMock.h" 25 #include "mocks/PipelineLayoutMock.h" 26 #include "mocks/QuerySetMock.h" 27 #include "mocks/RenderPipelineMock.h" 28 #include "mocks/SamplerMock.h" 29 #include "mocks/ShaderModuleMock.h" 30 #include "mocks/SwapChainMock.h" 31 #include "mocks/TextureMock.h" 32 #include "tests/DawnNativeTest.h" 33 #include "utils/ComboRenderPipelineDescriptor.h" 34 35 namespace dawn_native { namespace { 36 37 using ::testing::_; 38 using ::testing::ByMove; 39 using ::testing::InSequence; 40 using ::testing::Return; 41 using ::testing::Test; 42 43 class DestroyObjectTests : public Test { 44 public: DestroyObjectTests()45 DestroyObjectTests() : Test() { 46 // Skipping validation on descriptors as coverage for validation is already present. 47 mDevice.SetToggle(Toggle::SkipValidation, true); 48 } 49 GetTexture()50 Ref<TextureMock> GetTexture() { 51 if (mTexture != nullptr) { 52 return mTexture; 53 } 54 mTexture = 55 AcquireRef(new TextureMock(&mDevice, TextureBase::TextureState::OwnedInternal)); 56 EXPECT_CALL(*mTexture.Get(), DestroyImpl).Times(1); 57 return mTexture; 58 } 59 GetPipelineLayout()60 Ref<PipelineLayoutMock> GetPipelineLayout() { 61 if (mPipelineLayout != nullptr) { 62 return mPipelineLayout; 63 } 64 mPipelineLayout = AcquireRef(new PipelineLayoutMock(&mDevice)); 65 EXPECT_CALL(*mPipelineLayout.Get(), DestroyImpl).Times(1); 66 return mPipelineLayout; 67 } 68 GetVertexShaderModule()69 Ref<ShaderModuleMock> GetVertexShaderModule() { 70 if (mVsModule != nullptr) { 71 return mVsModule; 72 } 73 DAWN_TRY_ASSIGN_WITH_CLEANUP( 74 mVsModule, ShaderModuleMock::Create(&mDevice, R"( 75 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> { 76 return vec4<f32>(0.0, 0.0, 0.0, 1.0); 77 })"), 78 { ASSERT(false); }, mVsModule); 79 EXPECT_CALL(*mVsModule.Get(), DestroyImpl).Times(1); 80 return mVsModule; 81 } 82 GetComputeShaderModule()83 Ref<ShaderModuleMock> GetComputeShaderModule() { 84 if (mCsModule != nullptr) { 85 return mCsModule; 86 } 87 DAWN_TRY_ASSIGN_WITH_CLEANUP( 88 mCsModule, ShaderModuleMock::Create(&mDevice, R"( 89 [[stage(compute), workgroup_size(1)]] fn main() { 90 })"), 91 { ASSERT(false); }, mCsModule); 92 EXPECT_CALL(*mCsModule.Get(), DestroyImpl).Times(1); 93 return mCsModule; 94 } 95 96 protected: 97 DeviceMock mDevice; 98 99 // The following lazy-initialized objects are used to facilitate creation of dependent 100 // objects under test. 101 Ref<TextureMock> mTexture; 102 Ref<PipelineLayoutMock> mPipelineLayout; 103 Ref<ShaderModuleMock> mVsModule; 104 Ref<ShaderModuleMock> mCsModule; 105 }; 106 TEST_F(DestroyObjectTests,BindGroupExplicit)107 TEST_F(DestroyObjectTests, BindGroupExplicit) { 108 BindGroupMock bindGroupMock(&mDevice); 109 EXPECT_CALL(bindGroupMock, DestroyImpl).Times(1); 110 111 EXPECT_TRUE(bindGroupMock.IsAlive()); 112 bindGroupMock.Destroy(); 113 EXPECT_FALSE(bindGroupMock.IsAlive()); 114 } 115 116 // If the reference count on API objects reach 0, they should delete themselves. Note that GTest 117 // will also complain if there is a memory leak. TEST_F(DestroyObjectTests,BindGroupImplicit)118 TEST_F(DestroyObjectTests, BindGroupImplicit) { 119 BindGroupMock* bindGroupMock = new BindGroupMock(&mDevice); 120 EXPECT_CALL(*bindGroupMock, DestroyImpl).Times(1); 121 { 122 BindGroupDescriptor desc = {}; 123 Ref<BindGroupBase> bindGroup; 124 EXPECT_CALL(mDevice, CreateBindGroupImpl) 125 .WillOnce(Return(ByMove(AcquireRef(bindGroupMock)))); 126 DAWN_ASSERT_AND_ASSIGN(bindGroup, mDevice.CreateBindGroup(&desc)); 127 128 EXPECT_TRUE(bindGroup->IsAlive()); 129 } 130 } 131 TEST_F(DestroyObjectTests,BindGroupLayoutExplicit)132 TEST_F(DestroyObjectTests, BindGroupLayoutExplicit) { 133 BindGroupLayoutMock bindGroupLayoutMock(&mDevice); 134 EXPECT_CALL(bindGroupLayoutMock, DestroyImpl).Times(1); 135 136 EXPECT_TRUE(bindGroupLayoutMock.IsAlive()); 137 bindGroupLayoutMock.Destroy(); 138 EXPECT_FALSE(bindGroupLayoutMock.IsAlive()); 139 } 140 141 // If the reference count on API objects reach 0, they should delete themselves. Note that GTest 142 // will also complain if there is a memory leak. TEST_F(DestroyObjectTests,BindGroupLayoutImplicit)143 TEST_F(DestroyObjectTests, BindGroupLayoutImplicit) { 144 BindGroupLayoutMock* bindGroupLayoutMock = new BindGroupLayoutMock(&mDevice); 145 EXPECT_CALL(*bindGroupLayoutMock, DestroyImpl).Times(1); 146 { 147 BindGroupLayoutDescriptor desc = {}; 148 Ref<BindGroupLayoutBase> bindGroupLayout; 149 EXPECT_CALL(mDevice, CreateBindGroupLayoutImpl) 150 .WillOnce(Return(ByMove(AcquireRef(bindGroupLayoutMock)))); 151 DAWN_ASSERT_AND_ASSIGN(bindGroupLayout, mDevice.CreateBindGroupLayout(&desc)); 152 153 EXPECT_TRUE(bindGroupLayout->IsAlive()); 154 EXPECT_TRUE(bindGroupLayout->IsCachedReference()); 155 } 156 } 157 TEST_F(DestroyObjectTests,BufferExplicit)158 TEST_F(DestroyObjectTests, BufferExplicit) { 159 { 160 BufferMock bufferMock(&mDevice, BufferBase::BufferState::Unmapped); 161 EXPECT_CALL(bufferMock, DestroyImpl).Times(1); 162 163 EXPECT_TRUE(bufferMock.IsAlive()); 164 bufferMock.Destroy(); 165 EXPECT_FALSE(bufferMock.IsAlive()); 166 } 167 { 168 BufferMock bufferMock(&mDevice, BufferBase::BufferState::Mapped); 169 { 170 InSequence seq; 171 EXPECT_CALL(bufferMock, DestroyImpl).Times(1); 172 EXPECT_CALL(bufferMock, UnmapImpl).Times(1); 173 } 174 175 EXPECT_TRUE(bufferMock.IsAlive()); 176 bufferMock.Destroy(); 177 EXPECT_FALSE(bufferMock.IsAlive()); 178 } 179 } 180 181 // If the reference count on API objects reach 0, they should delete themselves. Note that GTest 182 // will also complain if there is a memory leak. TEST_F(DestroyObjectTests,BufferImplicit)183 TEST_F(DestroyObjectTests, BufferImplicit) { 184 { 185 BufferMock* bufferMock = new BufferMock(&mDevice, BufferBase::BufferState::Unmapped); 186 EXPECT_CALL(*bufferMock, DestroyImpl).Times(1); 187 { 188 BufferDescriptor desc = {}; 189 Ref<BufferBase> buffer; 190 EXPECT_CALL(mDevice, CreateBufferImpl) 191 .WillOnce(Return(ByMove(AcquireRef(bufferMock)))); 192 DAWN_ASSERT_AND_ASSIGN(buffer, mDevice.CreateBuffer(&desc)); 193 194 EXPECT_TRUE(buffer->IsAlive()); 195 } 196 } 197 { 198 BufferMock* bufferMock = new BufferMock(&mDevice, BufferBase::BufferState::Mapped); 199 { 200 InSequence seq; 201 EXPECT_CALL(*bufferMock, DestroyImpl).Times(1); 202 EXPECT_CALL(*bufferMock, UnmapImpl).Times(1); 203 } 204 { 205 BufferDescriptor desc = {}; 206 Ref<BufferBase> buffer; 207 EXPECT_CALL(mDevice, CreateBufferImpl) 208 .WillOnce(Return(ByMove(AcquireRef(bufferMock)))); 209 DAWN_ASSERT_AND_ASSIGN(buffer, mDevice.CreateBuffer(&desc)); 210 211 EXPECT_TRUE(buffer->IsAlive()); 212 } 213 } 214 } 215 TEST_F(DestroyObjectTests,CommandBufferExplicit)216 TEST_F(DestroyObjectTests, CommandBufferExplicit) { 217 CommandBufferMock commandBufferMock(&mDevice); 218 EXPECT_CALL(commandBufferMock, DestroyImpl).Times(1); 219 220 EXPECT_TRUE(commandBufferMock.IsAlive()); 221 commandBufferMock.Destroy(); 222 EXPECT_FALSE(commandBufferMock.IsAlive()); 223 } 224 225 // If the reference count on API objects reach 0, they should delete themselves. Note that GTest 226 // will also complain if there is a memory leak. TEST_F(DestroyObjectTests,CommandBufferImplicit)227 TEST_F(DestroyObjectTests, CommandBufferImplicit) { 228 CommandBufferMock* commandBufferMock = new CommandBufferMock(&mDevice); 229 EXPECT_CALL(*commandBufferMock, DestroyImpl).Times(1); 230 { 231 CommandBufferDescriptor desc = {}; 232 Ref<CommandBufferBase> commandBuffer; 233 EXPECT_CALL(mDevice, CreateCommandBuffer) 234 .WillOnce(Return(ByMove(AcquireRef(commandBufferMock)))); 235 DAWN_ASSERT_AND_ASSIGN(commandBuffer, mDevice.CreateCommandBuffer(nullptr, &desc)); 236 237 EXPECT_TRUE(commandBuffer->IsAlive()); 238 } 239 } 240 TEST_F(DestroyObjectTests,ComputePipelineExplicit)241 TEST_F(DestroyObjectTests, ComputePipelineExplicit) { 242 ComputePipelineMock computePipelineMock(&mDevice); 243 EXPECT_CALL(computePipelineMock, DestroyImpl).Times(1); 244 245 EXPECT_TRUE(computePipelineMock.IsAlive()); 246 computePipelineMock.Destroy(); 247 EXPECT_FALSE(computePipelineMock.IsAlive()); 248 } 249 250 // If the reference count on API objects reach 0, they should delete themselves. Note that GTest 251 // will also complain if there is a memory leak. TEST_F(DestroyObjectTests,ComputePipelineImplicit)252 TEST_F(DestroyObjectTests, ComputePipelineImplicit) { 253 // ComputePipelines usually set their hash values at construction, but the mock does not, so 254 // we set it here. 255 constexpr size_t hash = 0x12345; 256 ComputePipelineMock* computePipelineMock = new ComputePipelineMock(&mDevice); 257 computePipelineMock->SetContentHash(hash); 258 ON_CALL(*computePipelineMock, ComputeContentHash).WillByDefault(Return(hash)); 259 260 // Compute pipelines are initialized during their creation via the device. 261 EXPECT_CALL(*computePipelineMock, Initialize).Times(1); 262 EXPECT_CALL(*computePipelineMock, DestroyImpl).Times(1); 263 264 { 265 ComputePipelineDescriptor desc = {}; 266 desc.layout = GetPipelineLayout().Get(); 267 desc.compute.module = GetComputeShaderModule().Get(); 268 269 Ref<ComputePipelineBase> computePipeline; 270 EXPECT_CALL(mDevice, CreateUninitializedComputePipelineImpl) 271 .WillOnce(Return(ByMove(AcquireRef(computePipelineMock)))); 272 DAWN_ASSERT_AND_ASSIGN(computePipeline, mDevice.CreateComputePipeline(&desc)); 273 274 EXPECT_TRUE(computePipeline->IsAlive()); 275 EXPECT_TRUE(computePipeline->IsCachedReference()); 276 } 277 } 278 TEST_F(DestroyObjectTests,ExternalTextureExplicit)279 TEST_F(DestroyObjectTests, ExternalTextureExplicit) { 280 ExternalTextureMock externalTextureMock(&mDevice); 281 EXPECT_CALL(externalTextureMock, DestroyImpl).Times(1); 282 283 EXPECT_TRUE(externalTextureMock.IsAlive()); 284 externalTextureMock.Destroy(); 285 EXPECT_FALSE(externalTextureMock.IsAlive()); 286 } 287 288 // We can use an actual ExternalTexture object to test the implicit case. TEST_F(DestroyObjectTests,ExternalTextureImplicit)289 TEST_F(DestroyObjectTests, ExternalTextureImplicit) { 290 ExternalTextureDescriptor desc = {}; 291 Ref<ExternalTextureBase> externalTexture; 292 DAWN_ASSERT_AND_ASSIGN(externalTexture, mDevice.CreateExternalTexture(&desc)); 293 294 EXPECT_TRUE(externalTexture->IsAlive()); 295 } 296 TEST_F(DestroyObjectTests,PipelineLayoutExplicit)297 TEST_F(DestroyObjectTests, PipelineLayoutExplicit) { 298 PipelineLayoutMock pipelineLayoutMock(&mDevice); 299 EXPECT_CALL(pipelineLayoutMock, DestroyImpl).Times(1); 300 301 EXPECT_TRUE(pipelineLayoutMock.IsAlive()); 302 pipelineLayoutMock.Destroy(); 303 EXPECT_FALSE(pipelineLayoutMock.IsAlive()); 304 } 305 306 // If the reference count on API objects reach 0, they should delete themselves. Note that GTest 307 // will also complain if there is a memory leak. TEST_F(DestroyObjectTests,PipelineLayoutImplicit)308 TEST_F(DestroyObjectTests, PipelineLayoutImplicit) { 309 PipelineLayoutMock* pipelineLayoutMock = new PipelineLayoutMock(&mDevice); 310 EXPECT_CALL(*pipelineLayoutMock, DestroyImpl).Times(1); 311 { 312 PipelineLayoutDescriptor desc = {}; 313 Ref<PipelineLayoutBase> pipelineLayout; 314 EXPECT_CALL(mDevice, CreatePipelineLayoutImpl) 315 .WillOnce(Return(ByMove(AcquireRef(pipelineLayoutMock)))); 316 DAWN_ASSERT_AND_ASSIGN(pipelineLayout, mDevice.CreatePipelineLayout(&desc)); 317 318 EXPECT_TRUE(pipelineLayout->IsAlive()); 319 EXPECT_TRUE(pipelineLayout->IsCachedReference()); 320 } 321 } 322 TEST_F(DestroyObjectTests,QuerySetExplicit)323 TEST_F(DestroyObjectTests, QuerySetExplicit) { 324 QuerySetMock querySetMock(&mDevice); 325 EXPECT_CALL(querySetMock, DestroyImpl).Times(1); 326 327 EXPECT_TRUE(querySetMock.IsAlive()); 328 querySetMock.Destroy(); 329 EXPECT_FALSE(querySetMock.IsAlive()); 330 } 331 332 // If the reference count on API objects reach 0, they should delete themselves. Note that GTest 333 // will also complain if there is a memory leak. TEST_F(DestroyObjectTests,QuerySetImplicit)334 TEST_F(DestroyObjectTests, QuerySetImplicit) { 335 QuerySetMock* querySetMock = new QuerySetMock(&mDevice); 336 EXPECT_CALL(*querySetMock, DestroyImpl).Times(1); 337 { 338 QuerySetDescriptor desc = {}; 339 Ref<QuerySetBase> querySet; 340 EXPECT_CALL(mDevice, CreateQuerySetImpl) 341 .WillOnce(Return(ByMove(AcquireRef(querySetMock)))); 342 DAWN_ASSERT_AND_ASSIGN(querySet, mDevice.CreateQuerySet(&desc)); 343 344 EXPECT_TRUE(querySet->IsAlive()); 345 } 346 } 347 TEST_F(DestroyObjectTests,RenderPipelineExplicit)348 TEST_F(DestroyObjectTests, RenderPipelineExplicit) { 349 RenderPipelineMock renderPipelineMock(&mDevice); 350 EXPECT_CALL(renderPipelineMock, DestroyImpl).Times(1); 351 352 EXPECT_TRUE(renderPipelineMock.IsAlive()); 353 renderPipelineMock.Destroy(); 354 EXPECT_FALSE(renderPipelineMock.IsAlive()); 355 } 356 357 // If the reference count on API objects reach 0, they should delete themselves. Note that GTest 358 // will also complain if there is a memory leak. TEST_F(DestroyObjectTests,RenderPipelineImplicit)359 TEST_F(DestroyObjectTests, RenderPipelineImplicit) { 360 // RenderPipelines usually set their hash values at construction, but the mock does not, so 361 // we set it here. 362 constexpr size_t hash = 0x12345; 363 RenderPipelineMock* renderPipelineMock = new RenderPipelineMock(&mDevice); 364 renderPipelineMock->SetContentHash(hash); 365 ON_CALL(*renderPipelineMock, ComputeContentHash).WillByDefault(Return(hash)); 366 367 // Render pipelines are initialized during their creation via the device. 368 EXPECT_CALL(*renderPipelineMock, Initialize).Times(1); 369 EXPECT_CALL(*renderPipelineMock, DestroyImpl).Times(1); 370 371 { 372 RenderPipelineDescriptor desc = {}; 373 desc.layout = GetPipelineLayout().Get(); 374 desc.vertex.module = GetVertexShaderModule().Get(); 375 376 Ref<RenderPipelineBase> renderPipeline; 377 EXPECT_CALL(mDevice, CreateUninitializedRenderPipelineImpl) 378 .WillOnce(Return(ByMove(AcquireRef(renderPipelineMock)))); 379 DAWN_ASSERT_AND_ASSIGN(renderPipeline, mDevice.CreateRenderPipeline(&desc)); 380 381 EXPECT_TRUE(renderPipeline->IsAlive()); 382 EXPECT_TRUE(renderPipeline->IsCachedReference()); 383 } 384 } 385 TEST_F(DestroyObjectTests,SamplerExplicit)386 TEST_F(DestroyObjectTests, SamplerExplicit) { 387 SamplerMock samplerMock(&mDevice); 388 EXPECT_CALL(samplerMock, DestroyImpl).Times(1); 389 390 EXPECT_TRUE(samplerMock.IsAlive()); 391 samplerMock.Destroy(); 392 EXPECT_FALSE(samplerMock.IsAlive()); 393 } 394 395 // If the reference count on API objects reach 0, they should delete themselves. Note that GTest 396 // will also complain if there is a memory leak. TEST_F(DestroyObjectTests,SamplerImplicit)397 TEST_F(DestroyObjectTests, SamplerImplicit) { 398 SamplerMock* samplerMock = new SamplerMock(&mDevice); 399 EXPECT_CALL(*samplerMock, DestroyImpl).Times(1); 400 { 401 SamplerDescriptor desc = {}; 402 Ref<SamplerBase> sampler; 403 EXPECT_CALL(mDevice, CreateSamplerImpl) 404 .WillOnce(Return(ByMove(AcquireRef(samplerMock)))); 405 DAWN_ASSERT_AND_ASSIGN(sampler, mDevice.CreateSampler(&desc)); 406 407 EXPECT_TRUE(sampler->IsAlive()); 408 EXPECT_TRUE(sampler->IsCachedReference()); 409 } 410 } 411 TEST_F(DestroyObjectTests,ShaderModuleExplicit)412 TEST_F(DestroyObjectTests, ShaderModuleExplicit) { 413 ShaderModuleMock shaderModuleMock(&mDevice); 414 EXPECT_CALL(shaderModuleMock, DestroyImpl).Times(1); 415 416 EXPECT_TRUE(shaderModuleMock.IsAlive()); 417 shaderModuleMock.Destroy(); 418 EXPECT_FALSE(shaderModuleMock.IsAlive()); 419 } 420 421 // If the reference count on API objects reach 0, they should delete themselves. Note that GTest 422 // will also complain if there is a memory leak. TEST_F(DestroyObjectTests,ShaderModuleImplicit)423 TEST_F(DestroyObjectTests, ShaderModuleImplicit) { 424 ShaderModuleMock* shaderModuleMock = new ShaderModuleMock(&mDevice); 425 EXPECT_CALL(*shaderModuleMock, DestroyImpl).Times(1); 426 { 427 ShaderModuleWGSLDescriptor wgslDesc; 428 wgslDesc.source = R"( 429 [[stage(compute), workgroup_size(1)]] fn main() { 430 } 431 )"; 432 ShaderModuleDescriptor desc = {}; 433 desc.nextInChain = &wgslDesc; 434 Ref<ShaderModuleBase> shaderModule; 435 EXPECT_CALL(mDevice, CreateShaderModuleImpl) 436 .WillOnce(Return(ByMove(AcquireRef(shaderModuleMock)))); 437 DAWN_ASSERT_AND_ASSIGN(shaderModule, mDevice.CreateShaderModule(&desc)); 438 439 EXPECT_TRUE(shaderModule->IsAlive()); 440 EXPECT_TRUE(shaderModule->IsCachedReference()); 441 } 442 } 443 TEST_F(DestroyObjectTests,SwapChainExplicit)444 TEST_F(DestroyObjectTests, SwapChainExplicit) { 445 SwapChainMock swapChainMock(&mDevice); 446 EXPECT_CALL(swapChainMock, DestroyImpl).Times(1); 447 448 EXPECT_TRUE(swapChainMock.IsAlive()); 449 swapChainMock.Destroy(); 450 EXPECT_FALSE(swapChainMock.IsAlive()); 451 } 452 453 // If the reference count on API objects reach 0, they should delete themselves. Note that GTest 454 // will also complain if there is a memory leak. TEST_F(DestroyObjectTests,SwapChainImplicit)455 TEST_F(DestroyObjectTests, SwapChainImplicit) { 456 SwapChainMock* swapChainMock = new SwapChainMock(&mDevice); 457 EXPECT_CALL(*swapChainMock, DestroyImpl).Times(1); 458 { 459 SwapChainDescriptor desc = {}; 460 Ref<SwapChainBase> swapChain; 461 EXPECT_CALL(mDevice, CreateSwapChainImpl(_)) 462 .WillOnce(Return(ByMove(AcquireRef(swapChainMock)))); 463 DAWN_ASSERT_AND_ASSIGN(swapChain, mDevice.CreateSwapChain(nullptr, &desc)); 464 465 EXPECT_TRUE(swapChain->IsAlive()); 466 } 467 } 468 TEST_F(DestroyObjectTests,TextureExplicit)469 TEST_F(DestroyObjectTests, TextureExplicit) { 470 { 471 TextureMock textureMock(&mDevice, TextureBase::TextureState::OwnedInternal); 472 EXPECT_CALL(textureMock, DestroyImpl).Times(1); 473 474 EXPECT_TRUE(textureMock.IsAlive()); 475 textureMock.Destroy(); 476 EXPECT_FALSE(textureMock.IsAlive()); 477 } 478 { 479 TextureMock textureMock(&mDevice, TextureBase::TextureState::OwnedExternal); 480 EXPECT_CALL(textureMock, DestroyImpl).Times(1); 481 482 EXPECT_TRUE(textureMock.IsAlive()); 483 textureMock.Destroy(); 484 EXPECT_FALSE(textureMock.IsAlive()); 485 } 486 } 487 488 // If the reference count on API objects reach 0, they should delete themselves. Note that GTest 489 // will also complain if there is a memory leak. TEST_F(DestroyObjectTests,TextureImplicit)490 TEST_F(DestroyObjectTests, TextureImplicit) { 491 { 492 TextureMock* textureMock = 493 new TextureMock(&mDevice, TextureBase::TextureState::OwnedInternal); 494 EXPECT_CALL(*textureMock, DestroyImpl).Times(1); 495 { 496 TextureDescriptor desc = {}; 497 Ref<TextureBase> texture; 498 EXPECT_CALL(mDevice, CreateTextureImpl) 499 .WillOnce(Return(ByMove(AcquireRef(textureMock)))); 500 DAWN_ASSERT_AND_ASSIGN(texture, mDevice.CreateTexture(&desc)); 501 502 EXPECT_TRUE(texture->IsAlive()); 503 } 504 } 505 { 506 TextureMock* textureMock = 507 new TextureMock(&mDevice, TextureBase::TextureState::OwnedExternal); 508 EXPECT_CALL(*textureMock, DestroyImpl).Times(1); 509 { 510 TextureDescriptor desc = {}; 511 Ref<TextureBase> texture; 512 EXPECT_CALL(mDevice, CreateTextureImpl) 513 .WillOnce(Return(ByMove(AcquireRef(textureMock)))); 514 DAWN_ASSERT_AND_ASSIGN(texture, mDevice.CreateTexture(&desc)); 515 516 EXPECT_TRUE(texture->IsAlive()); 517 } 518 } 519 } 520 TEST_F(DestroyObjectTests,TextureViewExplicit)521 TEST_F(DestroyObjectTests, TextureViewExplicit) { 522 TextureViewMock textureViewMock(GetTexture().Get()); 523 EXPECT_CALL(textureViewMock, DestroyImpl).Times(1); 524 525 EXPECT_TRUE(textureViewMock.IsAlive()); 526 textureViewMock.Destroy(); 527 EXPECT_FALSE(textureViewMock.IsAlive()); 528 } 529 530 // If the reference count on API objects reach 0, they should delete themselves. Note that GTest 531 // will also complain if there is a memory leak. TEST_F(DestroyObjectTests,TextureViewImplicit)532 TEST_F(DestroyObjectTests, TextureViewImplicit) { 533 TextureViewMock* textureViewMock = new TextureViewMock(GetTexture().Get()); 534 EXPECT_CALL(*textureViewMock, DestroyImpl).Times(1); 535 { 536 TextureViewDescriptor desc = {}; 537 Ref<TextureViewBase> textureView; 538 EXPECT_CALL(mDevice, CreateTextureViewImpl) 539 .WillOnce(Return(ByMove(AcquireRef(textureViewMock)))); 540 DAWN_ASSERT_AND_ASSIGN(textureView, 541 mDevice.CreateTextureView(GetTexture().Get(), &desc)); 542 543 EXPECT_TRUE(textureView->IsAlive()); 544 } 545 } 546 547 // Destroying the objects on the mDevice should result in all created objects being destroyed in 548 // order. TEST_F(DestroyObjectTests,DestroyObjects)549 TEST_F(DestroyObjectTests, DestroyObjects) { 550 BindGroupMock* bindGroupMock = new BindGroupMock(&mDevice); 551 BindGroupLayoutMock* bindGroupLayoutMock = new BindGroupLayoutMock(&mDevice); 552 BufferMock* bufferMock = new BufferMock(&mDevice, BufferBase::BufferState::Unmapped); 553 CommandBufferMock* commandBufferMock = new CommandBufferMock(&mDevice); 554 ComputePipelineMock* computePipelineMock = new ComputePipelineMock(&mDevice); 555 PipelineLayoutMock* pipelineLayoutMock = new PipelineLayoutMock(&mDevice); 556 QuerySetMock* querySetMock = new QuerySetMock(&mDevice); 557 RenderPipelineMock* renderPipelineMock = new RenderPipelineMock(&mDevice); 558 SamplerMock* samplerMock = new SamplerMock(&mDevice); 559 ShaderModuleMock* shaderModuleMock = new ShaderModuleMock(&mDevice); 560 SwapChainMock* swapChainMock = new SwapChainMock(&mDevice); 561 TextureMock* textureMock = 562 new TextureMock(&mDevice, TextureBase::TextureState::OwnedInternal); 563 TextureViewMock* textureViewMock = new TextureViewMock(GetTexture().Get()); 564 { 565 InSequence seq; 566 EXPECT_CALL(*commandBufferMock, DestroyImpl).Times(1); 567 EXPECT_CALL(*renderPipelineMock, DestroyImpl).Times(1); 568 EXPECT_CALL(*computePipelineMock, DestroyImpl).Times(1); 569 EXPECT_CALL(*pipelineLayoutMock, DestroyImpl).Times(1); 570 EXPECT_CALL(*swapChainMock, DestroyImpl).Times(1); 571 EXPECT_CALL(*bindGroupMock, DestroyImpl).Times(1); 572 EXPECT_CALL(*bindGroupLayoutMock, DestroyImpl).Times(1); 573 EXPECT_CALL(*shaderModuleMock, DestroyImpl).Times(1); 574 EXPECT_CALL(*textureViewMock, DestroyImpl).Times(1); 575 EXPECT_CALL(*textureMock, DestroyImpl).Times(1); 576 EXPECT_CALL(*querySetMock, DestroyImpl).Times(1); 577 EXPECT_CALL(*samplerMock, DestroyImpl).Times(1); 578 EXPECT_CALL(*bufferMock, DestroyImpl).Times(1); 579 } 580 581 Ref<BindGroupBase> bindGroup; 582 { 583 BindGroupDescriptor desc = {}; 584 EXPECT_CALL(mDevice, CreateBindGroupImpl) 585 .WillOnce(Return(ByMove(AcquireRef(bindGroupMock)))); 586 DAWN_ASSERT_AND_ASSIGN(bindGroup, mDevice.CreateBindGroup(&desc)); 587 EXPECT_TRUE(bindGroup->IsAlive()); 588 } 589 590 Ref<BindGroupLayoutBase> bindGroupLayout; 591 { 592 BindGroupLayoutDescriptor desc = {}; 593 EXPECT_CALL(mDevice, CreateBindGroupLayoutImpl) 594 .WillOnce(Return(ByMove(AcquireRef(bindGroupLayoutMock)))); 595 DAWN_ASSERT_AND_ASSIGN(bindGroupLayout, mDevice.CreateBindGroupLayout(&desc)); 596 EXPECT_TRUE(bindGroupLayout->IsAlive()); 597 EXPECT_TRUE(bindGroupLayout->IsCachedReference()); 598 } 599 600 Ref<BufferBase> buffer; 601 { 602 BufferDescriptor desc = {}; 603 EXPECT_CALL(mDevice, CreateBufferImpl).WillOnce(Return(ByMove(AcquireRef(bufferMock)))); 604 DAWN_ASSERT_AND_ASSIGN(buffer, mDevice.CreateBuffer(&desc)); 605 EXPECT_TRUE(buffer->IsAlive()); 606 } 607 608 Ref<CommandBufferBase> commandBuffer; 609 { 610 CommandBufferDescriptor desc = {}; 611 EXPECT_CALL(mDevice, CreateCommandBuffer) 612 .WillOnce(Return(ByMove(AcquireRef(commandBufferMock)))); 613 DAWN_ASSERT_AND_ASSIGN(commandBuffer, mDevice.CreateCommandBuffer(nullptr, &desc)); 614 EXPECT_TRUE(commandBuffer->IsAlive()); 615 } 616 617 Ref<ComputePipelineBase> computePipeline; 618 { 619 // Compute pipelines usually set their hash values at construction, but the mock does 620 // not, so we set it here. 621 constexpr size_t hash = 0x12345; 622 computePipelineMock->SetContentHash(hash); 623 ON_CALL(*computePipelineMock, ComputeContentHash).WillByDefault(Return(hash)); 624 625 // Compute pipelines are initialized during their creation via the device. 626 EXPECT_CALL(*computePipelineMock, Initialize).Times(1); 627 628 ComputePipelineDescriptor desc = {}; 629 desc.layout = GetPipelineLayout().Get(); 630 desc.compute.module = GetComputeShaderModule().Get(); 631 EXPECT_CALL(mDevice, CreateUninitializedComputePipelineImpl) 632 .WillOnce(Return(ByMove(AcquireRef(computePipelineMock)))); 633 DAWN_ASSERT_AND_ASSIGN(computePipeline, mDevice.CreateComputePipeline(&desc)); 634 EXPECT_TRUE(computePipeline->IsAlive()); 635 EXPECT_TRUE(computePipeline->IsCachedReference()); 636 } 637 638 Ref<ExternalTextureBase> externalTexture; 639 { 640 ExternalTextureDescriptor desc = {}; 641 DAWN_ASSERT_AND_ASSIGN(externalTexture, mDevice.CreateExternalTexture(&desc)); 642 EXPECT_TRUE(externalTexture->IsAlive()); 643 } 644 645 Ref<PipelineLayoutBase> pipelineLayout; 646 { 647 PipelineLayoutDescriptor desc = {}; 648 EXPECT_CALL(mDevice, CreatePipelineLayoutImpl) 649 .WillOnce(Return(ByMove(AcquireRef(pipelineLayoutMock)))); 650 DAWN_ASSERT_AND_ASSIGN(pipelineLayout, mDevice.CreatePipelineLayout(&desc)); 651 EXPECT_TRUE(pipelineLayout->IsAlive()); 652 EXPECT_TRUE(pipelineLayout->IsCachedReference()); 653 } 654 655 Ref<QuerySetBase> querySet; 656 { 657 QuerySetDescriptor desc = {}; 658 EXPECT_CALL(mDevice, CreateQuerySetImpl) 659 .WillOnce(Return(ByMove(AcquireRef(querySetMock)))); 660 DAWN_ASSERT_AND_ASSIGN(querySet, mDevice.CreateQuerySet(&desc)); 661 EXPECT_TRUE(querySet->IsAlive()); 662 } 663 664 Ref<RenderPipelineBase> renderPipeline; 665 { 666 // Render pipelines usually set their hash values at construction, but the mock does 667 // not, so we set it here. 668 constexpr size_t hash = 0x12345; 669 renderPipelineMock->SetContentHash(hash); 670 ON_CALL(*renderPipelineMock, ComputeContentHash).WillByDefault(Return(hash)); 671 672 // Render pipelines are initialized during their creation via the device. 673 EXPECT_CALL(*renderPipelineMock, Initialize).Times(1); 674 675 RenderPipelineDescriptor desc = {}; 676 desc.layout = GetPipelineLayout().Get(); 677 desc.vertex.module = GetVertexShaderModule().Get(); 678 EXPECT_CALL(mDevice, CreateUninitializedRenderPipelineImpl) 679 .WillOnce(Return(ByMove(AcquireRef(renderPipelineMock)))); 680 DAWN_ASSERT_AND_ASSIGN(renderPipeline, mDevice.CreateRenderPipeline(&desc)); 681 EXPECT_TRUE(renderPipeline->IsAlive()); 682 EXPECT_TRUE(renderPipeline->IsCachedReference()); 683 } 684 685 Ref<SamplerBase> sampler; 686 { 687 SamplerDescriptor desc = {}; 688 EXPECT_CALL(mDevice, CreateSamplerImpl) 689 .WillOnce(Return(ByMove(AcquireRef(samplerMock)))); 690 DAWN_ASSERT_AND_ASSIGN(sampler, mDevice.CreateSampler(&desc)); 691 EXPECT_TRUE(sampler->IsAlive()); 692 EXPECT_TRUE(sampler->IsCachedReference()); 693 } 694 695 Ref<ShaderModuleBase> shaderModule; 696 { 697 ShaderModuleWGSLDescriptor wgslDesc; 698 wgslDesc.source = R"( 699 [[stage(compute), workgroup_size(1)]] fn main() { 700 } 701 )"; 702 ShaderModuleDescriptor desc = {}; 703 desc.nextInChain = &wgslDesc; 704 705 EXPECT_CALL(mDevice, CreateShaderModuleImpl) 706 .WillOnce(Return(ByMove(AcquireRef(shaderModuleMock)))); 707 DAWN_ASSERT_AND_ASSIGN(shaderModule, mDevice.CreateShaderModule(&desc)); 708 EXPECT_TRUE(shaderModule->IsAlive()); 709 EXPECT_TRUE(shaderModule->IsCachedReference()); 710 } 711 712 Ref<SwapChainBase> swapChain; 713 { 714 SwapChainDescriptor desc = {}; 715 EXPECT_CALL(mDevice, CreateSwapChainImpl(_)) 716 .WillOnce(Return(ByMove(AcquireRef(swapChainMock)))); 717 DAWN_ASSERT_AND_ASSIGN(swapChain, mDevice.CreateSwapChain(nullptr, &desc)); 718 EXPECT_TRUE(swapChain->IsAlive()); 719 } 720 721 Ref<TextureBase> texture; 722 { 723 TextureDescriptor desc = {}; 724 EXPECT_CALL(mDevice, CreateTextureImpl) 725 .WillOnce(Return(ByMove(AcquireRef(textureMock)))); 726 DAWN_ASSERT_AND_ASSIGN(texture, mDevice.CreateTexture(&desc)); 727 EXPECT_TRUE(texture->IsAlive()); 728 } 729 730 Ref<TextureViewBase> textureView; 731 { 732 TextureViewDescriptor desc = {}; 733 EXPECT_CALL(mDevice, CreateTextureViewImpl) 734 .WillOnce(Return(ByMove(AcquireRef(textureViewMock)))); 735 DAWN_ASSERT_AND_ASSIGN(textureView, 736 mDevice.CreateTextureView(GetTexture().Get(), &desc)); 737 EXPECT_TRUE(textureView->IsAlive()); 738 } 739 740 mDevice.DestroyObjects(); 741 EXPECT_FALSE(bindGroup->IsAlive()); 742 EXPECT_FALSE(bindGroupLayout->IsAlive()); 743 EXPECT_FALSE(buffer->IsAlive()); 744 EXPECT_FALSE(commandBuffer->IsAlive()); 745 EXPECT_FALSE(computePipeline->IsAlive()); 746 EXPECT_FALSE(externalTexture->IsAlive()); 747 EXPECT_FALSE(pipelineLayout->IsAlive()); 748 EXPECT_FALSE(querySet->IsAlive()); 749 EXPECT_FALSE(renderPipeline->IsAlive()); 750 EXPECT_FALSE(sampler->IsAlive()); 751 EXPECT_FALSE(shaderModule->IsAlive()); 752 EXPECT_FALSE(swapChain->IsAlive()); 753 EXPECT_FALSE(texture->IsAlive()); 754 EXPECT_FALSE(textureView->IsAlive()); 755 } 756 757 }} // namespace dawn_native:: 758