diff --git a/engine/src/flutter/impeller/display_list/canvas.cc b/engine/src/flutter/impeller/display_list/canvas.cc index c582ff1eed..46e55f6d0f 100644 --- a/engine/src/flutter/impeller/display_list/canvas.cc +++ b/engine/src/flutter/impeller/display_list/canvas.cc @@ -13,6 +13,7 @@ #include "display_list/effects/dl_image_filter.h" #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" +#include "impeller/base/validation.h" #include "impeller/display_list/color_filter.h" #include "impeller/display_list/image_filter.h" #include "impeller/display_list/skia_conversions.h" @@ -1672,10 +1673,8 @@ bool Canvas::BlitToOnscreen() { VALIDATION_LOG << "Failed to encode root pass blit command."; return false; } - if (!renderer_.GetContext() - ->GetCommandQueue() - ->Submit({command_buffer}) - .ok()) { + if (!renderer_.GetContext()->EnqueueCommandBuffer( + std::move(command_buffer))) { return false; } } else { @@ -1703,10 +1702,8 @@ bool Canvas::BlitToOnscreen() { VALIDATION_LOG << "Failed to encode root pass command buffer."; return false; } - if (!renderer_.GetContext() - ->GetCommandQueue() - ->Submit({command_buffer}) - .ok()) { + if (!renderer_.GetContext()->EnqueueCommandBuffer( + std::move(command_buffer))) { return false; } } @@ -1726,6 +1723,10 @@ void Canvas::EndReplay() { BlitToOnscreen(); } + if (!renderer_.GetContext()->FlushCommandBuffers()) { + // Not much we can do. + VALIDATION_LOG << "Failed to submit command buffers"; + } render_passes_.clear(); renderer_.GetRenderTargetCache()->End(); clip_geometry_.clear(); diff --git a/engine/src/flutter/impeller/entity/contents/contents.cc b/engine/src/flutter/impeller/entity/contents/contents.cc index 0946775a59..abebbfc68b 100644 --- a/engine/src/flutter/impeller/entity/contents/contents.cc +++ b/engine/src/flutter/impeller/entity/contents/contents.cc @@ -110,10 +110,7 @@ std::optional Contents::RenderToSnapshot( if (!render_target.ok()) { return std::nullopt; } - if (!renderer.GetContext() - ->GetCommandQueue() - ->Submit(/*buffers=*/{std::move(command_buffer)}) - .ok()) { + if (!renderer.GetContext()->EnqueueCommandBuffer(std::move(command_buffer))) { return std::nullopt; } diff --git a/engine/src/flutter/impeller/entity/contents/filters/blend_filter_contents.cc b/engine/src/flutter/impeller/entity/contents/filters/blend_filter_contents.cc index 08b7b0744f..ce35b710af 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/filters/blend_filter_contents.cc @@ -243,10 +243,7 @@ static std::optional AdvancedBlend( if (!render_target.ok()) { return std::nullopt; } - if (!renderer.GetContext() - ->GetCommandQueue() - ->Submit(/*buffers=*/{std::move(command_buffer)}) - .ok()) { + if (!renderer.GetContext()->EnqueueCommandBuffer(std::move(command_buffer))) { return std::nullopt; } @@ -655,11 +652,7 @@ static std::optional PipelineBlend( if (!render_target.ok()) { return std::nullopt; } - - if (!renderer.GetContext() - ->GetCommandQueue() - ->Submit(/*buffers=*/{std::move(command_buffer)}) - .ok()) { + if (!renderer.GetContext()->EnqueueCommandBuffer(std::move(command_buffer))) { return std::nullopt; } @@ -895,11 +888,7 @@ std::optional BlendFilterContents::CreateFramebufferAdvancedBlend( if (!render_target.ok()) { return std::nullopt; } - - if (!renderer.GetContext() - ->GetCommandQueue() - ->Submit(/*buffers=*/{std::move(cmd_buffer)}) - .ok()) { + if (!renderer.GetContext()->EnqueueCommandBuffer(std::move(cmd_buffer))) { return std::nullopt; } diff --git a/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index b70c717394..3e592636b8 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -817,11 +817,12 @@ std::optional GaussianBlurFilterContents::RenderFilter( return std::nullopt; } - if (!renderer.GetContext() - ->GetCommandQueue() - ->Submit(/*buffers=*/{command_buffer_1, command_buffer_2, - command_buffer_3}) - .ok()) { + if (!(renderer.GetContext()->EnqueueCommandBuffer( + std::move(command_buffer_1)) && + renderer.GetContext()->EnqueueCommandBuffer( + std::move(command_buffer_2)) && + renderer.GetContext()->EnqueueCommandBuffer( + std::move(command_buffer_3)))) { return std::nullopt; } diff --git a/engine/src/flutter/impeller/entity/contents/filters/morphology_filter_contents.cc b/engine/src/flutter/impeller/entity/contents/filters/morphology_filter_contents.cc index deeba95b9b..87c0706deb 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/morphology_filter_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/filters/morphology_filter_contents.cc @@ -144,10 +144,8 @@ std::optional DirectionalMorphologyFilterContents::RenderFilter( if (!render_target.ok()) { return std::nullopt; } - if (!renderer.GetContext() - ->GetCommandQueue() - ->Submit(/*buffers=*/{std::move(command_buffer)}) - .ok()) { + + if (!renderer.GetContext()->EnqueueCommandBuffer(std::move(command_buffer))) { return std::nullopt; } diff --git a/engine/src/flutter/impeller/entity/inline_pass_context.cc b/engine/src/flutter/impeller/entity/inline_pass_context.cc index 658b8d2f55..ac86af2139 100644 --- a/engine/src/flutter/impeller/entity/inline_pass_context.cc +++ b/engine/src/flutter/impeller/entity/inline_pass_context.cc @@ -61,17 +61,10 @@ bool InlinePassContext::EndPass() { return false; } } - if (!renderer_.GetContext() - ->GetCommandQueue() - ->Submit({std::move(command_buffer_)}) - .ok()) { - return false; - } pass_ = nullptr; - command_buffer_ = nullptr; - - return true; + return renderer_.GetContext()->EnqueueCommandBuffer( + std::move(command_buffer_)); } EntityPassTarget& InlinePassContext::GetPassTarget() const { diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc index 222646983f..cfebbf7dd0 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc @@ -445,6 +445,7 @@ void ContextVK::Setup(Settings settings) { device_name_ = std::string(physical_device_properties.deviceName); command_queue_vk_ = std::make_shared(weak_from_this()); should_disable_surface_control_ = settings.disable_surface_control; + should_batch_cmd_buffers_ = driver_info_->CanBatchSubmitCommandBuffers(); is_valid_ = true; // Create the GPU Tracer later because it depends on state from @@ -590,6 +591,26 @@ std::shared_ptr ContextVK::GetCommandQueue() const { return command_queue_vk_; } +bool ContextVK::EnqueueCommandBuffer( + std::shared_ptr command_buffer) { + if (should_batch_cmd_buffers_) { + pending_command_buffers_.push_back(std::move(command_buffer)); + return true; + } else { + return GetCommandQueue()->Submit({command_buffer}).ok(); + } +} + +bool ContextVK::FlushCommandBuffers() { + if (should_batch_cmd_buffers_) { + bool result = GetCommandQueue()->Submit(pending_command_buffers_).ok(); + pending_command_buffers_.clear(); + return result; + } else { + return true; + } +} + // Creating a render pass is observed to take an additional 6ms on a Pixel 7 // device as the driver will lazily bootstrap and compile shaders to do so. // The render pass does not need to be begun or executed. diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h index 4cb1233f5a..6fa473154c 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h @@ -22,6 +22,7 @@ #include "impeller/renderer/backend/vulkan/sampler_library_vk.h" #include "impeller/renderer/backend/vulkan/shader_library_vk.h" #include "impeller/renderer/capabilities.h" +#include "impeller/renderer/command_buffer.h" #include "impeller/renderer/command_queue.h" #include "impeller/renderer/context.h" @@ -190,6 +191,13 @@ class ContextVK final : public Context, /// disabled, even if the device is capable of supporting it. bool GetShouldDisableSurfaceControlSwapchain() const; + // | Context | + bool EnqueueCommandBuffer( + std::shared_ptr command_buffer) override; + + // | Context | + bool FlushCommandBuffers() override; + private: struct DeviceHolderImpl : public DeviceHolderVK { // |DeviceHolder| @@ -223,6 +231,8 @@ class ContextVK final : public Context, std::shared_ptr descriptor_pool_recycler_; std::shared_ptr command_queue_vk_; bool should_disable_surface_control_ = false; + bool should_batch_cmd_buffers_ = false; + std::vector> pending_command_buffers_; const uint64_t hash_; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk_unittests.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk_unittests.cc index 629f88daa2..38cc53867d 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk_unittests.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk_unittests.cc @@ -260,5 +260,56 @@ TEST(ContextVKTest, HasDefaultColorFormat) { ASSERT_NE(capabilites_vk->GetDefaultColorFormat(), PixelFormat::kUnknown); } +TEST(ContextVKTest, BatchSubmitCommandBuffersOnArm) { + std::shared_ptr context = + MockVulkanContextBuilder() + .SetPhysicalPropertiesCallback( + [](VkPhysicalDevice device, VkPhysicalDeviceProperties* prop) { + prop->vendorID = 0x13B5; // ARM + prop->deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; + }) + .Build(); + + EXPECT_TRUE(context->EnqueueCommandBuffer(context->CreateCommandBuffer())); + EXPECT_TRUE(context->EnqueueCommandBuffer(context->CreateCommandBuffer())); + + // If command buffers are batch submitted, we should have created them but not + // created the fence to track them after enqueing. + auto functions = GetMockVulkanFunctions(context->GetDevice()); + EXPECT_TRUE(std::find(functions->begin(), functions->end(), + "vkAllocateCommandBuffers") != functions->end()); + EXPECT_TRUE(std::find(functions->begin(), functions->end(), + "vkCreateFence") == functions->end()); + + context->FlushCommandBuffers(); + + // After flushing, the fence should be created. + functions = GetMockVulkanFunctions(context->GetDevice()); + EXPECT_TRUE(std::find(functions->begin(), functions->end(), + "vkCreateFence") != functions->end()); +} + +TEST(ContextVKTest, BatchSubmitCommandBuffersOnNonArm) { + std::shared_ptr context = + MockVulkanContextBuilder() + .SetPhysicalPropertiesCallback( + [](VkPhysicalDevice device, VkPhysicalDeviceProperties* prop) { + prop->vendorID = 0x8686; // Made up ID + prop->deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; + }) + .Build(); + + EXPECT_TRUE(context->EnqueueCommandBuffer(context->CreateCommandBuffer())); + EXPECT_TRUE(context->EnqueueCommandBuffer(context->CreateCommandBuffer())); + + // If command buffers are batch not submitted, we should have created them and + // a corresponding fence immediately. + auto functions = GetMockVulkanFunctions(context->GetDevice()); + EXPECT_TRUE(std::find(functions->begin(), functions->end(), + "vkAllocateCommandBuffers") != functions->end()); + EXPECT_TRUE(std::find(functions->begin(), functions->end(), + "vkCreateFence") != functions->end()); +} + } // namespace testing } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/driver_info_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/driver_info_vk.cc index e0fdb5728d..007aa47392 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/driver_info_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/driver_info_vk.cc @@ -317,6 +317,12 @@ void DriverInfoVK::DumpToLog() const { FML_LOG(IMPORTANT) << stream.str(); } +bool DriverInfoVK::CanBatchSubmitCommandBuffers() const { + return vendor_ == VendorVK::kARM || + (adreno_gpu_.has_value() && + adreno_gpu_.value() >= AdrenoGPU::kAdreno702); +} + bool DriverInfoVK::IsEmulator() const { #if FML_OS_ANDROID // Google SwiftShader on Android. diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/driver_info_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/driver_info_vk.h index 27f0e332fd..5836118ff4 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/driver_info_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/driver_info_vk.h @@ -12,100 +12,100 @@ namespace impeller { // https://en.wikipedia.org/wiki/Adreno enum class AdrenoGPU { - // Unknown GPU, likely newer model. - kUnknown, - // X - kAdrenoX185, - kAdrenoX145, - // 700s - kAdreno750, - kAdreno740, - kAdreno735, - kAdreno732, - kAdreno730, - kAdreno725, - kAdreno720, - kAdreno710, - kAdreno702, + // I don't think the 400 series will ever run Vulkan, but if some show up we + // can add them here. + // 500s + kAdreno504, + kAdreno505, + kAdreno506, + kAdreno508, + kAdreno509, + kAdreno510, + kAdreno512, + kAdreno530, + kAdreno540, // 600s - kAdreno695, - kAdreno690, - kAdreno685, - kAdreno680, - kAdreno675, - kAdreno663, - kAdreno660, - kAdreno650, - kAdreno644, - kAdreno643L, - kAdreno642, - kAdreno642L, + kAdreno605, + kAdreno608, + kAdreno610, + kAdreno612, + kAdreno613, + kAdreno615, + kAdreno616, + kAdreno618, + kAdreno619L, + kAdreno619, + kAdreno620, + kAdreno630, // The 640 is the first GPU inside an Android device with upgradable drivers. // Anything before this point exhibiting broken behavior is broken forever. kAdreno640, - kAdreno630, - kAdreno620, - kAdreno619, - kAdreno619L, - kAdreno618, - kAdreno616, - kAdreno615, - kAdreno613, - kAdreno612, - kAdreno610, - kAdreno608, - kAdreno605, - // 500s - kAdreno540, - kAdreno530, - kAdreno512, - kAdreno510, - kAdreno509, - kAdreno508, - kAdreno506, - kAdreno505, - kAdreno504, - // I don't think the 400 series will ever run Vulkan, but if some show up we - // can add them here. + kAdreno642L, + kAdreno642, + kAdreno643L, + kAdreno644, + kAdreno650, + kAdreno660, + kAdreno663, + kAdreno675, + kAdreno680, + kAdreno685, + kAdreno690, + kAdreno695, + // 700s + kAdreno702, + kAdreno710, + kAdreno720, + kAdreno725, + kAdreno730, + kAdreno732, + kAdreno735, + kAdreno740, + kAdreno750, + // X + kAdrenoX145, + kAdrenoX185, + // Unknown GPU, likely newer model. + kUnknown, }; // https://en.wikipedia.org/wiki/Mali_(processor) enum class MaliGPU { - kUnknown, - // 5th Gen - kG925, - kG725, - kG625, - kG720, - kG620, + // These might be Vulkan 1.0 Only. + kT760, + kT820, + kT830, + kT860, + kT880, + + // Bifrost + kG31, + kG51, + kG71, + kG52, + kG72, + kG76, // Valhall // Note: there is an Immortalis-G715 a Mali-G715 - kG715, - kG615, - kG710, - kG610, - kG510, - kG310, - kG78, - kG68, - kG77, kG57, + kG77, + kG68, + kG78, + kG310, + kG510, + kG610, + kG710, + kG615, + kG715, - // Bifrost - kG76, - kG72, - kG52, - kG71, - kG51, - kG31, - - // These might be Vulkan 1.0 Only. - kT880, - kT860, - kT830, - kT820, - kT760, + // 5th Gen + kG620, + kG720, + kG625, + kG725, + kG925, + kUnknown, }; enum class VendorVK { @@ -234,10 +234,23 @@ class DriverInfoVK { /// If true, context setup should fail such that the device falls /// back to OpenGLES. /// - /// @return True if non-functional device, False otherwiise. + /// @return True if non-functional device, False otherwise. /// bool IsKnownBadDriver() const; + //---------------------------------------------------------------------------- + /// @brief Determines if the driver can batch submit command buffers + /// without triggering erronious deadlock errors. + /// + /// Early 600 series Adreno drivers would deadlock if a command + /// buffer submission had too much work attached to it, this + /// requires the renderer to split up command buffers that could + /// be logically combined. + /// + /// @return True if device can batch submit command buffers. + /// + bool CanBatchSubmitCommandBuffers() const; + private: bool is_valid_ = false; Version api_version_; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/driver_info_vk_unittests.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/driver_info_vk_unittests.cc index 59f0733b8b..44f014acbc 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/driver_info_vk_unittests.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/driver_info_vk_unittests.cc @@ -54,13 +54,39 @@ bool IsBadVersionTest(std::string_view driver_name, bool qc = true) { return context->GetDriverInfo()->IsKnownBadDriver(); } +bool CanBatchSubmitTest(std::string_view driver_name, bool qc = true) { + auto const context = + MockVulkanContextBuilder() + .SetPhysicalPropertiesCallback( + [&driver_name, qc](VkPhysicalDevice device, + VkPhysicalDeviceProperties* prop) { + if (qc) { + prop->vendorID = 0x168C; // Qualcomm + } else { + prop->vendorID = 0x13B5; // ARM + } + driver_name.copy(prop->deviceName, driver_name.size()); + prop->deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; + }) + .Build(); + return context->GetDriverInfo()->CanBatchSubmitCommandBuffers(); +} + +TEST(DriverInfoVKTest, CanBatchSubmitCommandBuffers) { + // Old Adreno no batch submit! + EXPECT_FALSE(CanBatchSubmitTest("Adreno (TM) 540", true)); + + EXPECT_TRUE(CanBatchSubmitTest("Mali-G51", false)); + EXPECT_TRUE(CanBatchSubmitTest("Adreno (TM) 750", true)); +} + TEST(DriverInfoVKTest, DriverParsingMali) { EXPECT_EQ(GetMaliVersion("Mali-G51-MORE STUFF"), MaliGPU::kG51); EXPECT_EQ(GetMaliVersion("Mali-G51"), MaliGPU::kG51); EXPECT_EQ(GetMaliVersion("Mali-111111"), MaliGPU::kUnknown); } -TEST(DriverInfoVKTest, DriverParsingArm) { +TEST(DriverInfoVKTest, DriverParsingAdreno) { EXPECT_EQ(GetAdrenoVersion("Adreno (TM) 540"), AdrenoGPU::kAdreno540); EXPECT_EQ(GetAdrenoVersion("Foo Bar"), AdrenoGPU::kUnknown); } diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.cc index 912c74b03c..64f369e307 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.cc @@ -111,4 +111,13 @@ const std::shared_ptr& SurfaceContextVK::GetParent() const { return parent_; } +bool SurfaceContextVK::EnqueueCommandBuffer( + std::shared_ptr command_buffer) { + return parent_->EnqueueCommandBuffer(std::move(command_buffer)); +} + +bool SurfaceContextVK::FlushCommandBuffers() { + return parent_->FlushCommandBuffers(); +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.h index 7389d95c28..444eb91bf1 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.h @@ -90,6 +90,11 @@ class SurfaceContextVK : public Context, const std::shared_ptr& GetParent() const; + bool EnqueueCommandBuffer( + std::shared_ptr command_buffer) override; + + bool FlushCommandBuffers() override; + private: std::shared_ptr parent_; std::shared_ptr swapchain_; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/test/mock_vulkan.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/test/mock_vulkan.cc index 0a1dd6fcdf..93a6ba452f 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/test/mock_vulkan.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/test/mock_vulkan.cc @@ -494,6 +494,7 @@ VkResult vkCreateFence(VkDevice device, const VkAllocationCallbacks* pAllocator, VkFence* pFence) { MockDevice* mock_device = reinterpret_cast(device); + mock_device->AddCalledFunction("vkCreateFence"); *pFence = reinterpret_cast(new MockFence()); return VK_SUCCESS; } diff --git a/engine/src/flutter/impeller/renderer/context.cc b/engine/src/flutter/impeller/renderer/context.cc index b6d5a624cb..e23874191a 100644 --- a/engine/src/flutter/impeller/renderer/context.cc +++ b/engine/src/flutter/impeller/renderer/context.cc @@ -4,6 +4,8 @@ #include "impeller/renderer/context.h" +#include + namespace impeller { Context::~Context() = default; @@ -14,4 +16,13 @@ bool Context::UpdateOffscreenLayerPixelFormat(PixelFormat format) { return false; } +bool Context::EnqueueCommandBuffer( + std::shared_ptr command_buffer) { + return GetCommandQueue()->Submit({std::move(command_buffer)}).ok(); +} + +bool Context::FlushCommandBuffers() { + return true; +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/context.h b/engine/src/flutter/impeller/renderer/context.h index 8345e7446b..91c5ee7a77 100644 --- a/engine/src/flutter/impeller/renderer/context.h +++ b/engine/src/flutter/impeller/renderer/context.h @@ -203,6 +203,25 @@ class Context { /// operation completes in order to clear the cache. virtual void DisposeThreadLocalCachedResources() {} + /// @brief Enqueue command_buffer for submission by the end of the frame. + /// + /// Certain backends may immediately flush the command buffer if batch + /// submission is not supported. This functionality is not thread safe + /// and should only be used via the ContentContext for rendering a + /// 2D workload. + /// + /// Returns true if submission has succeeded. If the buffer is enqueued + /// then no error may be returned until FlushCommandBuffers is called. + [[nodiscard]] virtual bool EnqueueCommandBuffer( + std::shared_ptr command_buffer); + + /// @brief Flush all pending command buffers. + /// + /// Returns whether or not submission was successful. This functionality + /// is not threadsafe and should only be used via the ContentContext for + /// rendering a 2D workload. + [[nodiscard]] virtual bool FlushCommandBuffers(); + protected: Context(); diff --git a/engine/src/flutter/impeller/typographer/backends/skia/typographer_context_skia.cc b/engine/src/flutter/impeller/typographer/backends/skia/typographer_context_skia.cc index b6f7b8a646..ea8dc36b6c 100644 --- a/engine/src/flutter/impeller/typographer/backends/skia/typographer_context_skia.cc +++ b/engine/src/flutter/impeller/typographer/backends/skia/typographer_context_skia.cc @@ -14,6 +14,7 @@ #include "flutter/fml/trace_event.h" #include "fml/closure.h" +#include "impeller/base/validation.h" #include "impeller/core/allocator.h" #include "impeller/core/buffer_view.h" #include "impeller/core/formats.h" @@ -502,7 +503,9 @@ std::shared_ptr TypographerContextSkia::CreateGlyphAtlas( fml::ScopedCleanupClosure closure([&]() { blit_pass->EncodeCommands(context.GetResourceAllocator()); - context.GetCommandQueue()->Submit({std::move(cmd_buffer)}); + if (!context.EnqueueCommandBuffer(std::move(cmd_buffer))) { + VALIDATION_LOG << "Failed to submit glyph atlas command buffer"; + } }); // --------------------------------------------------------------------------- @@ -590,7 +593,9 @@ std::shared_ptr TypographerContextSkia::CreateGlyphAtlas( fml::ScopedCleanupClosure closure([&]() { blit_pass->EncodeCommands(context.GetResourceAllocator()); - context.GetCommandQueue()->Submit({std::move(cmd_buffer)}); + if (!context.EnqueueCommandBuffer(std::move(cmd_buffer))) { + VALIDATION_LOG << "Failed to submit glyph atlas command buffer"; + } }); // Now append all remaining glyphs. This should never have any missing data...