Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 1bc18ff

Browse files
authored
[Impeller] Started tracking the pool with the command buffer. (#45298)
issue: flutter/flutter#133506 This fixes one of the issues seen in flutter/flutter#133506. There were multiple ones though. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 03bdb2c commit 1bc18ff

File tree

6 files changed

+144
-10
lines changed

6 files changed

+144
-10
lines changed

ci/licenses_golden/excluded_files

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@
151151
../../../flutter/impeller/image/README.md
152152
../../../flutter/impeller/playground
153153
../../../flutter/impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc
154+
../../../flutter/impeller/renderer/backend/vulkan/command_encoder_vk_unittests.cc
154155
../../../flutter/impeller/renderer/backend/vulkan/context_vk_unittests.cc
155156
../../../flutter/impeller/renderer/backend/vulkan/pass_bindings_cache_unittests.cc
156157
../../../flutter/impeller/renderer/backend/vulkan/resource_manager_vk_unittests.cc

impeller/renderer/backend/vulkan/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ impeller_component("vulkan_unittests") {
99
testonly = true
1010
sources = [
1111
"blit_command_vk_unittests.cc",
12+
"command_encoder_vk_unittests.cc",
1213
"context_vk_unittests.cc",
1314
"pass_bindings_cache_unittests.cc",
1415
"resource_manager_vk_unittests.cc",

impeller/renderer/backend/vulkan/command_encoder_vk.cc

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,7 @@ class TrackedObjectsVK {
3434
if (!buffer_) {
3535
return;
3636
}
37-
auto pool = pool_.lock();
38-
if (!pool) {
39-
// The buffer can not be freed if its command pool has been destroyed.
40-
buffer_.release();
41-
return;
42-
}
43-
pool->CollectGraphicsCommandBuffer(std::move(buffer_));
37+
pool_->CollectGraphicsCommandBuffer(std::move(buffer_));
4438
}
4539

4640
bool IsValid() const { return is_valid_; }
@@ -86,7 +80,8 @@ class TrackedObjectsVK {
8680

8781
private:
8882
DescriptorPoolVK desc_pool_;
89-
std::weak_ptr<CommandPoolVK> pool_;
83+
// `shared_ptr` since command buffers have a link to the command pool.
84+
std::shared_ptr<CommandPoolVK> pool_;
9085
vk::UniqueCommandBuffer buffer_;
9186
std::set<std::shared_ptr<SharedObjectVK>> tracked_objects_;
9287
std::set<std::shared_ptr<const Buffer>> tracked_buffers_;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include <thread>
6+
7+
#include "flutter/fml/synchronization/count_down_latch.h"
8+
#include "flutter/testing/testing.h"
9+
#include "impeller/renderer/backend/vulkan/command_encoder_vk.h"
10+
#include "impeller/renderer/backend/vulkan/fence_waiter_vk.h"
11+
#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h"
12+
13+
namespace impeller {
14+
namespace testing {
15+
16+
TEST(CommandEncoderVKTest, DeleteEncoderAfterThreadDies) {
17+
// Tests that when a CommandEncoderVK is deleted that it will clean up its
18+
// command buffers before it cleans up its command pool.
19+
std::shared_ptr<std::vector<std::string>> called_functions;
20+
{
21+
auto context = CreateMockVulkanContext();
22+
called_functions = GetMockVulkanFunctions(context->GetDevice());
23+
std::shared_ptr<CommandEncoderVK> encoder;
24+
std::thread thread([&] {
25+
CommandEncoderFactoryVK factory(context);
26+
encoder = factory.Create();
27+
});
28+
thread.join();
29+
}
30+
auto destroy_pool =
31+
std::find(called_functions->begin(), called_functions->end(),
32+
"vkDestroyCommandPool");
33+
auto free_buffers =
34+
std::find(called_functions->begin(), called_functions->end(),
35+
"vkFreeCommandBuffers");
36+
EXPECT_TRUE(destroy_pool != called_functions->end());
37+
EXPECT_TRUE(free_buffers != called_functions->end());
38+
EXPECT_TRUE(free_buffers < destroy_pool);
39+
}
40+
41+
TEST(CommandEncoderVKTest, CleanupAfterSubmit) {
42+
// This tests deleting the TrackedObjects where the thread is killed before
43+
// the fence waiter has disposed of them, making sure the command buffer and
44+
// its pools are deleted in that order.
45+
std::shared_ptr<std::vector<std::string>> called_functions;
46+
{
47+
fml::AutoResetWaitableEvent wait_for_submit;
48+
fml::AutoResetWaitableEvent wait_for_thread_join;
49+
auto context = CreateMockVulkanContext();
50+
std::thread thread([&] {
51+
CommandEncoderFactoryVK factory(context);
52+
std::shared_ptr<CommandEncoderVK> encoder = factory.Create();
53+
encoder->Submit([&](bool success) {
54+
ASSERT_TRUE(success);
55+
wait_for_thread_join.Wait();
56+
wait_for_submit.Signal();
57+
});
58+
});
59+
thread.join();
60+
wait_for_thread_join.Signal();
61+
wait_for_submit.Wait();
62+
called_functions = GetMockVulkanFunctions(context->GetDevice());
63+
}
64+
auto destroy_pool =
65+
std::find(called_functions->begin(), called_functions->end(),
66+
"vkDestroyCommandPool");
67+
auto free_buffers =
68+
std::find(called_functions->begin(), called_functions->end(),
69+
"vkFreeCommandBuffers");
70+
EXPECT_TRUE(destroy_pool != called_functions->end());
71+
EXPECT_TRUE(free_buffers != called_functions->end());
72+
EXPECT_TRUE(free_buffers < destroy_pool);
73+
}
74+
75+
} // namespace testing
76+
} // namespace impeller

impeller/renderer/backend/vulkan/resource_manager_vk.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,13 @@ class ResourceManagerVK final
7575
using Reclaimables = std::vector<std::unique_ptr<ResourceVK>>;
7676

7777
ResourceManagerVK();
78-
79-
std::thread waiter_;
8078
std::mutex reclaimables_mutex_;
8179
std::condition_variable reclaimables_cv_;
8280
Reclaimables reclaimables_;
8381
bool should_exit_ = false;
82+
// This should be initialized last since it references the other instance
83+
// variables.
84+
std::thread waiter_;
8485

8586
//----------------------------------------------------------------------------
8687
/// @brief Starts the resource manager thread.

impeller/renderer/backend/vulkan/test/mock_vulkan.cc

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,52 @@ void vkCmdSetViewport(VkCommandBuffer commandBuffer,
346346
mock_command_buffer->called_functions_->push_back("vkCmdSetViewport");
347347
}
348348

349+
void vkFreeCommandBuffers(VkDevice device,
350+
VkCommandPool commandPool,
351+
uint32_t commandBufferCount,
352+
const VkCommandBuffer* pCommandBuffers) {
353+
MockDevice* mock_device = reinterpret_cast<MockDevice*>(device);
354+
mock_device->called_functions_->push_back("vkFreeCommandBuffers");
355+
}
356+
357+
void vkDestroyCommandPool(VkDevice device,
358+
VkCommandPool commandPool,
359+
const VkAllocationCallbacks* pAllocator) {
360+
MockDevice* mock_device = reinterpret_cast<MockDevice*>(device);
361+
mock_device->called_functions_->push_back("vkDestroyCommandPool");
362+
}
363+
364+
VkResult vkEndCommandBuffer(VkCommandBuffer commandBuffer) {
365+
return VK_SUCCESS;
366+
}
367+
368+
VkResult vkCreateFence(VkDevice device,
369+
const VkFenceCreateInfo* pCreateInfo,
370+
const VkAllocationCallbacks* pAllocator,
371+
VkFence* pFence) {
372+
*pFence = reinterpret_cast<VkFence>(0xfe0ce);
373+
return VK_SUCCESS;
374+
}
375+
376+
VkResult vkQueueSubmit(VkQueue queue,
377+
uint32_t submitCount,
378+
const VkSubmitInfo* pSubmits,
379+
VkFence fence) {
380+
return VK_SUCCESS;
381+
}
382+
383+
VkResult vkWaitForFences(VkDevice device,
384+
uint32_t fenceCount,
385+
const VkFence* pFences,
386+
VkBool32 waitAll,
387+
uint64_t timeout) {
388+
return VK_SUCCESS;
389+
}
390+
391+
VkResult vkGetFenceStatus(VkDevice device, VkFence fence) {
392+
return VK_SUCCESS;
393+
}
394+
349395
PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance,
350396
const char* pName) {
351397
if (strcmp("vkEnumerateInstanceExtensionProperties", pName) == 0) {
@@ -424,6 +470,20 @@ PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance,
424470
return (PFN_vkVoidFunction)vkCmdSetScissor;
425471
} else if (strcmp("vkCmdSetViewport", pName) == 0) {
426472
return (PFN_vkVoidFunction)vkCmdSetViewport;
473+
} else if (strcmp("vkDestroyCommandPool", pName) == 0) {
474+
return (PFN_vkVoidFunction)vkDestroyCommandPool;
475+
} else if (strcmp("vkFreeCommandBuffers", pName) == 0) {
476+
return (PFN_vkVoidFunction)vkFreeCommandBuffers;
477+
} else if (strcmp("vkEndCommandBuffer", pName) == 0) {
478+
return (PFN_vkVoidFunction)vkEndCommandBuffer;
479+
} else if (strcmp("vkCreateFence", pName) == 0) {
480+
return (PFN_vkVoidFunction)vkCreateFence;
481+
} else if (strcmp("vkQueueSubmit", pName) == 0) {
482+
return (PFN_vkVoidFunction)vkQueueSubmit;
483+
} else if (strcmp("vkWaitForFences", pName) == 0) {
484+
return (PFN_vkVoidFunction)vkWaitForFences;
485+
} else if (strcmp("vkGetFenceStatus", pName) == 0) {
486+
return (PFN_vkVoidFunction)vkGetFenceStatus;
427487
}
428488
return noop;
429489
}

0 commit comments

Comments
 (0)