Skip to content

Commit

Permalink
[Impeller] force bootstrap render pass. (flutter#50749)
Browse files Browse the repository at this point in the history
Part of flutter/flutter#138236

Creating the first VkRenderPass takes 6ms on a Pixel 7.

In the previous PR I attempted to use the Impeller HAL API, but this caused more problems than it solved - breaking mocks and also leading to odd behavior I think may be due to threading issues when submitting the bootstrap cmd buffer.

It seems like all we need to do to trigger the shader compilation is create the render pass, so add a ContextVK override that creates and throws it away, without beginning the render pass, creating a cmd buffer, or doing any extra work.
  • Loading branch information
jonahwilliams authored Feb 21, 2024
1 parent 4128895 commit 52ffcaa
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 1 deletion.
5 changes: 5 additions & 0 deletions impeller/aiks/testing/context_mock.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ class ContextMock : public Context {
(const override));

MOCK_METHOD(void, Shutdown, (), (override));

MOCK_METHOD(void,
InitializeCommonlyUsedShadersIfNeeded,
(),
(const, override));
};

} // namespace testing
Expand Down
4 changes: 3 additions & 1 deletion impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -612,12 +612,14 @@ void ContentContext::FlushCommandBuffers() const {
}

void ContentContext::InitializeCommonlyUsedShadersIfNeeded() const {
TRACE_EVENT0("flutter", "InitializeCommonlyUsedShadersIfNeeded");
GetContext()->InitializeCommonlyUsedShadersIfNeeded();

if (GetContext()->GetBackendType() == Context::BackendType::kOpenGLES) {
// TODO(jonahwilliams): The OpenGL Embedder Unittests hang if this code
// runs.
return;
}
TRACE_EVENT0("flutter", "InitializeCommonlyUsedShadersIfNeeded");

// Initialize commonly used shaders that aren't defaults. These settings were
// chosen based on the knowledge that we mix and match triangle and
Expand Down
43 changes: 43 additions & 0 deletions impeller/renderer/backend/vulkan/context_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#include "fml/concurrent_message_loop.h"
#include "impeller/renderer/backend/vulkan/command_queue_vk.h"
#include "impeller/renderer/backend/vulkan/render_pass_builder_vk.h"
#include "impeller/renderer/render_target.h"

#ifdef FML_OS_ANDROID
#include <pthread.h>
Expand Down Expand Up @@ -572,4 +574,45 @@ std::shared_ptr<CommandQueue> ContextVK::GetCommandQueue() const {
return command_queue_vk_;
}

// 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.
void ContextVK::InitializeCommonlyUsedShadersIfNeeded() const {
RenderTargetAllocator rt_allocator(GetResourceAllocator());
RenderTarget render_target =
RenderTarget::CreateOffscreenMSAA(*this, rt_allocator, {1, 1}, 1);

RenderPassBuilderVK builder;
for (const auto& [bind_point, color] : render_target.GetColorAttachments()) {
builder.SetColorAttachment(
bind_point, //
color.texture->GetTextureDescriptor().format, //
color.texture->GetTextureDescriptor().sample_count, //
color.load_action, //
color.store_action //
);
}

if (auto depth = render_target.GetDepthAttachment(); depth.has_value()) {
builder.SetDepthStencilAttachment(
depth->texture->GetTextureDescriptor().format, //
depth->texture->GetTextureDescriptor().sample_count, //
depth->load_action, //
depth->store_action //
);
}

if (auto stencil = render_target.GetStencilAttachment();
stencil.has_value()) {
builder.SetStencilAttachment(
stencil->texture->GetTextureDescriptor().format, //
stencil->texture->GetTextureDescriptor().sample_count, //
stencil->load_action, //
stencil->store_action //
);
}

auto pass = builder.Build(GetDevice());
}

} // namespace impeller
2 changes: 2 additions & 0 deletions impeller/renderer/backend/vulkan/context_vk.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ class ContextVK final : public Context,

void RecordFrameEndTime() const;

void InitializeCommonlyUsedShadersIfNeeded() const override;

private:
struct DeviceHolderImpl : public DeviceHolder {
// |DeviceHolder|
Expand Down
12 changes: 12 additions & 0 deletions impeller/renderer/backend/vulkan/context_vk_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/testing/testing.h" // IWYU pragma: keep
#include "impeller/base/validation.h"
#include "impeller/core/formats.h"
#include "impeller/renderer/backend/vulkan/command_pool_vk.h"
#include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h"
Expand Down Expand Up @@ -210,5 +211,16 @@ TEST(CapabilitiesVKTest,
ASSERT_EQ(context, nullptr);
}

TEST(ContextVKTest, WarmUpFunctionCreatesRenderPass) {
const std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();

context->SetOffscreenFormat(PixelFormat::kR8G8B8A8UNormInt);
context->InitializeCommonlyUsedShadersIfNeeded();

auto functions = GetMockVulkanFunctions(context->GetDevice());
ASSERT_TRUE(std::find(functions->begin(), functions->end(),
"vkCreateRenderPass") != functions->end());
}

} // namespace testing
} // namespace impeller
4 changes: 4 additions & 0 deletions impeller/renderer/backend/vulkan/surface_context_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,8 @@ const vk::Device& SurfaceContextVK::GetDevice() const {
return parent_->GetDevice();
}

void SurfaceContextVK::InitializeCommonlyUsedShadersIfNeeded() const {
parent_->InitializeCommonlyUsedShadersIfNeeded();
}

} // namespace impeller
2 changes: 2 additions & 0 deletions impeller/renderer/backend/vulkan/surface_context_vk.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ class SurfaceContextVK : public Context,
/// recreated on the next frame.
void UpdateSurfaceSize(const ISize& size) const;

void InitializeCommonlyUsedShadersIfNeeded() const override;

#ifdef FML_OS_ANDROID
vk::UniqueSurfaceKHR CreateAndroidSurface(ANativeWindow* window) const;
#endif // FML_OS_ANDROID
Expand Down
2 changes: 2 additions & 0 deletions impeller/renderer/backend/vulkan/test/mock_vulkan.cc
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,8 @@ VkResult vkCreateRenderPass(VkDevice device,
const VkAllocationCallbacks* pAllocator,
VkRenderPass* pRenderPass) {
*pRenderPass = reinterpret_cast<VkRenderPass>(0x12341234);
MockDevice* mock_device = reinterpret_cast<MockDevice*>(device);
mock_device->AddCalledFunction("vkCreateRenderPass");
return VK_SUCCESS;
}

Expand Down
8 changes: 8 additions & 0 deletions impeller/renderer/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,14 @@ class Context {
FML_CHECK(false && "not supported in this context");
}

/// Run backend specific additional setup and create common shader variants.
///
/// This bootstrap is intended to improve the performance of several
/// first frame benchmarks that are tracked in the flutter device lab.
/// The workload includes initializing commonly used but not default
/// shader variants, as well as forcing driver initialization.
virtual void InitializeCommonlyUsedShadersIfNeeded() const {}

protected:
Context();

Expand Down

0 comments on commit 52ffcaa

Please sign in to comment.