Skip to content

Commit

Permalink
[Impeller] Cache render target texture allocations across frames. (fl…
Browse files Browse the repository at this point in the history
…utter#44527)

When allocating a non-host visible texture for a render target, the allocator will hold onto a reference to this texture descriptor. On the immediate next frame, this texture is made available to replace requested allocations for new render target textures. if this texture is not used, then at the end of the next frame it is discarded.

This removes the vast majority of allocations in most flutter gallery and wonderous. This does not attempt to use different sized textures for the cache.

There are two caveats noted in the PR contents.

Fixes flutter/flutter#131515
  • Loading branch information
jonahwilliams authored and gaaclarke committed Aug 30, 2023
1 parent 824bb0d commit cf8eaa6
Show file tree
Hide file tree
Showing 25 changed files with 432 additions and 38 deletions.
2 changes: 2 additions & 0 deletions ci/licenses_golden/excluded_files
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,14 @@
../../../flutter/impeller/compiler/README.md
../../../flutter/impeller/compiler/compiler_unittests.cc
../../../flutter/impeller/compiler/switches_unittests.cc
../../../flutter/impeller/core/allocator_unittests.cc
../../../flutter/impeller/display_list/dl_unittests.cc
../../../flutter/impeller/display_list/skia_conversions_unittests.cc
../../../flutter/impeller/docs
../../../flutter/impeller/entity/contents/filters/inputs/filter_input_unittests.cc
../../../flutter/impeller/entity/entity_unittests.cc
../../../flutter/impeller/entity/geometry/geometry_unittests.cc
../../../flutter/impeller/entity/render_target_cache_unittests.cc
../../../flutter/impeller/fixtures
../../../flutter/impeller/geometry/README.md
../../../flutter/impeller/geometry/geometry_unittests.cc
Expand Down
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1271,6 +1271,8 @@ ORIGIN: ../../../flutter/impeller/entity/geometry/vertices_geometry.cc + ../../.
ORIGIN: ../../../flutter/impeller/entity/geometry/vertices_geometry.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/inline_pass_context.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/inline_pass_context.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/render_target_cache.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/render_target_cache.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.glsl + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_color.frag + ../../../flutter/LICENSE
Expand Down Expand Up @@ -3994,6 +3996,8 @@ FILE: ../../../flutter/impeller/entity/geometry/vertices_geometry.cc
FILE: ../../../flutter/impeller/entity/geometry/vertices_geometry.h
FILE: ../../../flutter/impeller/entity/inline_pass_context.cc
FILE: ../../../flutter/impeller/entity/inline_pass_context.h
FILE: ../../../flutter/impeller/entity/render_target_cache.cc
FILE: ../../../flutter/impeller/entity/render_target_cache.h
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.glsl
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.vert
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_color.frag
Expand Down
2 changes: 2 additions & 0 deletions impeller/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ impeller_component("impeller_unittests") {
"base:base_unittests",
"blobcat:blobcat_unittests",
"compiler:compiler_unittests",
"core:allocator_unittests",
"display_list:skia_conversions_unittests",
"geometry:geometry_unittests",
"runtime_stage:runtime_stage_unittests",
Expand All @@ -89,6 +90,7 @@ impeller_component("impeller_unittests") {
"aiks:aiks_unittests",
"display_list:display_list_unittests",
"entity:entity_unittests",
"entity:render_target_cache_unittests",
"fixtures",
"geometry:geometry_unittests",
"image:image_unittests",
Expand Down
6 changes: 6 additions & 0 deletions impeller/aiks/picture.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,15 @@ std::shared_ptr<Texture> Picture::RenderToTexture(
// This texture isn't host visible, but we might want to add host visible
// features to Image someday.
auto impeller_context = context.GetContext();
// Do not use the render target cache as the lifecycle of this texture
// will outlive a particular frame.
RenderTargetAllocator render_target_allocator =
RenderTargetAllocator(impeller_context->GetResourceAllocator());
RenderTarget target;
if (impeller_context->GetCapabilities()->SupportsOffscreenMSAA()) {
target = RenderTarget::CreateOffscreenMSAA(
*impeller_context, // context
render_target_allocator, // allocator
size, // size
"Picture Snapshot MSAA", // label
RenderTarget::
Expand All @@ -70,6 +75,7 @@ std::shared_ptr<Texture> Picture::RenderToTexture(
} else {
target = RenderTarget::CreateOffscreen(
*impeller_context, // context
render_target_allocator, // allocator
size, // size
"Picture Snapshot", // label
RenderTarget::kDefaultColorAttachmentConfig // color_attachment_config
Expand Down
12 changes: 12 additions & 0 deletions impeller/core/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,15 @@ impeller_component("core") {
"//flutter/fml",
]
}

impeller_component("allocator_unittests") {
testonly = true

sources = [ "allocator_unittests.cc" ]

deps = [
":core",
"../geometry",
"//flutter/testing:testing_lib",
]
}
5 changes: 3 additions & 2 deletions impeller/core/allocator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "impeller/base/validation.h"
#include "impeller/core/device_buffer.h"
#include "impeller/core/formats.h"
#include "impeller/core/range.h"

namespace impeller {
Expand Down Expand Up @@ -57,10 +58,10 @@ std::shared_ptr<Texture> Allocator::CreateTexture(
return OnCreateTexture(desc);
}

void Allocator::DidAcquireSurfaceFrame() {}

uint16_t Allocator::MinimumBytesPerRow(PixelFormat format) const {
return BytesPerPixelForPixelFormat(format);
}

void Allocator::DidAcquireSurfaceFrame() {}

} // namespace impeller
2 changes: 2 additions & 0 deletions impeller/core/allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#include "flutter/fml/macros.h"
#include "flutter/fml/mapping.h"
#include "impeller/core/device_buffer_descriptor.h"
#include "impeller/core/texture.h"
#include "impeller/core/texture_descriptor.h"
#include "impeller/geometry/size.h"

namespace impeller {

Expand Down
83 changes: 83 additions & 0 deletions impeller/core/allocator_unittests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <memory>
#include "flutter/testing/testing.h"
#include "impeller/core/allocator.h"
#include "impeller/core/formats.h"
#include "impeller/core/texture_descriptor.h"
#include "impeller/geometry/size.h"
#include "impeller/renderer/testing/mocks.h"

namespace impeller {
namespace testing {

TEST(AllocatorTest, TextureDescriptorCompatibility) {
// Size.
{
TextureDescriptor desc_a = {.size = ISize(100, 100)};
TextureDescriptor desc_b = {.size = ISize(100, 100)};
TextureDescriptor desc_c = {.size = ISize(101, 100)};

ASSERT_EQ(desc_a, desc_b);
ASSERT_NE(desc_a, desc_c);
}
// Storage Mode.
{
TextureDescriptor desc_a = {.storage_mode = StorageMode::kDevicePrivate};
TextureDescriptor desc_b = {.storage_mode = StorageMode::kDevicePrivate};
TextureDescriptor desc_c = {.storage_mode = StorageMode::kHostVisible};

ASSERT_EQ(desc_a, desc_b);
ASSERT_NE(desc_a, desc_c);
}
// Format.
{
TextureDescriptor desc_a = {.format = PixelFormat::kR8G8B8A8UNormInt};
TextureDescriptor desc_b = {.format = PixelFormat::kR8G8B8A8UNormInt};
TextureDescriptor desc_c = {.format = PixelFormat::kB10G10R10A10XR};

ASSERT_EQ(desc_a, desc_b);
ASSERT_NE(desc_a, desc_c);
}
// Sample Count.
{
TextureDescriptor desc_a = {.sample_count = SampleCount::kCount4};
TextureDescriptor desc_b = {.sample_count = SampleCount::kCount4};
TextureDescriptor desc_c = {.sample_count = SampleCount::kCount1};

ASSERT_EQ(desc_a, desc_b);
ASSERT_NE(desc_a, desc_c);
}
// Sample Count.
{
TextureDescriptor desc_a = {.type = TextureType::kTexture2DMultisample};
TextureDescriptor desc_b = {.type = TextureType::kTexture2DMultisample};
TextureDescriptor desc_c = {.type = TextureType::kTexture2D};

ASSERT_EQ(desc_a, desc_b);
ASSERT_NE(desc_a, desc_c);
}
// Compression.
{
TextureDescriptor desc_a = {.compression_type = CompressionType::kLossless};
TextureDescriptor desc_b = {.compression_type = CompressionType::kLossless};
TextureDescriptor desc_c = {.compression_type = CompressionType::kLossy};

ASSERT_EQ(desc_a, desc_b);
ASSERT_NE(desc_a, desc_c);
}
// Mip Count.
{
TextureDescriptor desc_a = {.mip_count = 1};
TextureDescriptor desc_b = {.mip_count = 1};
TextureDescriptor desc_c = {.mip_count = 4};

ASSERT_EQ(desc_a, desc_b);
ASSERT_NE(desc_a, desc_c);
}
}

} // namespace testing
} // namespace impeller
15 changes: 15 additions & 0 deletions impeller/core/texture_descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,21 @@ struct TextureDescriptor {
return IsMultisampleCapable(type) ? count > 1 : count == 1;
}

constexpr bool operator==(const TextureDescriptor& other) const {
return size == other.size && //
storage_mode == other.storage_mode && //
format == other.format && //
usage == other.usage && //
sample_count == other.sample_count && //
type == other.type && //
compression_type == other.compression_type && //
mip_count == other.mip_count;
}

constexpr bool operator!=(const TextureDescriptor& other) const {
return !(*this == other);
}

constexpr bool IsValid() const {
return format != PixelFormat::kUnknown && //
size.IsPositive() && //
Expand Down
13 changes: 13 additions & 0 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ impeller_component("entity") {
"geometry/vertices_geometry.h",
"inline_pass_context.cc",
"inline_pass_context.h",
"render_target_cache.cc",
"render_target_cache.h",
]

if (impeller_debug) {
Expand Down Expand Up @@ -283,3 +285,14 @@ impeller_component("entity_unittests") {
"../playground:playground_test",
]
}

impeller_component("render_target_cache_unittests") {
testonly = true

sources = [ "render_target_cache_unittests.cc" ]

deps = [
":entity",
"//flutter/testing:testing_lib",
]
}
11 changes: 8 additions & 3 deletions impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "impeller/base/strings.h"
#include "impeller/core/formats.h"
#include "impeller/entity/entity.h"
#include "impeller/entity/render_target_cache.h"
#include "impeller/renderer/command_buffer.h"
#include "impeller/renderer/pipeline_library.h"
#include "impeller/renderer/render_pass.h"
Expand Down Expand Up @@ -162,7 +163,9 @@ ContentContext::ContentContext(std::shared_ptr<Context> context)
: context_(std::move(context)),
lazy_glyph_atlas_(std::make_shared<LazyGlyphAtlas>()),
tessellator_(std::make_shared<Tessellator>()),
scene_context_(std::make_shared<scene::SceneContext>(context_)) {
scene_context_(std::make_shared<scene::SceneContext>(context_)),
render_target_cache_(std::make_shared<RenderTargetCache>(
context_->GetResourceAllocator())) {
if (!context_ || !context_->IsValid()) {
return;
}
Expand Down Expand Up @@ -359,7 +362,8 @@ std::shared_ptr<Texture> ContentContext::MakeSubpass(
RenderTarget subpass_target;
if (context->GetCapabilities()->SupportsOffscreenMSAA() && msaa_enabled) {
subpass_target = RenderTarget::CreateOffscreenMSAA(
*context, texture_size, SPrintF("%s Offscreen", label.c_str()),
*context, *GetRenderTargetCache().get(), texture_size,
SPrintF("%s Offscreen", label.c_str()),
RenderTarget::kDefaultColorAttachmentConfigMSAA //
#ifndef FML_OS_ANDROID // Reduce PSO variants for Vulkan.
,
Expand All @@ -368,7 +372,8 @@ std::shared_ptr<Texture> ContentContext::MakeSubpass(
);
} else {
subpass_target = RenderTarget::CreateOffscreen(
*context, texture_size, SPrintF("%s Offscreen", label.c_str()),
*context, *GetRenderTargetCache().get(), texture_size,
SPrintF("%s Offscreen", label.c_str()),
RenderTarget::kDefaultColorAttachmentConfig //
#ifndef FML_OS_ANDROID // Reduce PSO variants for Vulkan.
,
Expand Down
7 changes: 7 additions & 0 deletions impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "impeller/entity/entity.h"
#include "impeller/renderer/capabilities.h"
#include "impeller/renderer/pipeline.h"
#include "impeller/renderer/render_target.h"
#include "impeller/scene/scene_context.h"

#ifdef IMPELLER_DEBUG
Expand Down Expand Up @@ -334,6 +335,7 @@ struct ContentContextOptions {
};

class Tessellator;
class RenderTargetCache;

class ContentContext {
public:
Expand Down Expand Up @@ -708,6 +710,10 @@ class ContentContext {
return lazy_glyph_atlas_;
}

std::shared_ptr<RenderTargetAllocator> GetRenderTargetCache() const {
return render_target_cache_;
}

private:
std::shared_ptr<Context> context_;
std::shared_ptr<LazyGlyphAtlas> lazy_glyph_atlas_;
Expand Down Expand Up @@ -865,6 +871,7 @@ class ContentContext {
bool is_valid_ = false;
std::shared_ptr<Tessellator> tessellator_;
std::shared_ptr<scene::SceneContext> scene_context_;
std::shared_ptr<RenderTargetAllocator> render_target_cache_;
bool wireframe_ = false;

FML_DISALLOW_COPY_AND_ASSIGN(ContentContext);
Expand Down
7 changes: 4 additions & 3 deletions impeller/entity/contents/scene_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ bool SceneContents::Render(const ContentContext& renderer,
}

RenderTarget subpass_target = RenderTarget::CreateOffscreenMSAA(
*renderer.GetContext(), // context
ISize(coverage.value().size), // size
"SceneContents", // label
*renderer.GetContext(), // context
*renderer.GetRenderTargetCache().get(), // allocator
ISize(coverage.value().size), // size
"SceneContents", // label
RenderTarget::AttachmentConfigMSAA{
.storage_mode = StorageMode::kDeviceTransient,
.resolve_storage_mode = StorageMode::kDevicePrivate,
Expand Down
24 changes: 15 additions & 9 deletions impeller/entity/entity_pass.cc
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,10 @@ static EntityPassTarget CreateRenderTarget(ContentContext& renderer,
RenderTarget target;
if (context->GetCapabilities()->SupportsOffscreenMSAA()) {
target = RenderTarget::CreateOffscreenMSAA(
*context, // context
size, // size
"EntityPass", // label
*context, // context
*renderer.GetRenderTargetCache().get(), // allocator
size, // size
"EntityPass", // label
RenderTarget::AttachmentConfigMSAA{
.storage_mode = StorageMode::kDeviceTransient,
.resolve_storage_mode = StorageMode::kDevicePrivate,
Expand All @@ -222,9 +223,10 @@ static EntityPassTarget CreateRenderTarget(ContentContext& renderer,
);
} else {
target = RenderTarget::CreateOffscreen(
*context, // context
size, // size
"EntityPass", // label
*context, // context
*renderer.GetRenderTargetCache().get(), // allocator
size, // size
"EntityPass", // label
RenderTarget::AttachmentConfig{
.storage_mode = StorageMode::kDevicePrivate,
.load_action = LoadAction::kDontCare,
Expand All @@ -248,6 +250,7 @@ uint32_t EntityPass::GetTotalPassReads(ContentContext& renderer) const {

bool EntityPass::Render(ContentContext& renderer,
const RenderTarget& render_target) const {
renderer.GetRenderTargetCache()->Start();
auto root_render_target = render_target;

if (root_render_target.GetColorAttachments().find(0u) ==
Expand All @@ -256,8 +259,10 @@ bool EntityPass::Render(ContentContext& renderer,
return false;
}

fml::ScopedCleanupClosure reset_lazy_glyph_atlas(
[&renderer]() { renderer.GetLazyGlyphAtlas()->ResetTextFrames(); });
fml::ScopedCleanupClosure reset_state([&renderer]() {
renderer.GetLazyGlyphAtlas()->ResetTextFrames();
renderer.GetRenderTargetCache()->End();
});

IterateAllEntities([lazy_glyph_atlas =
renderer.GetLazyGlyphAtlas()](const Entity& entity) {
Expand Down Expand Up @@ -381,7 +386,8 @@ bool EntityPass::Render(ContentContext& renderer,
// provided by the caller.
else {
root_render_target.SetupStencilAttachment(
*renderer.GetContext(), color0.texture->GetSize(),
*renderer.GetContext(), *renderer.GetRenderTargetCache().get(),
color0.texture->GetSize(),
renderer.GetContext()->GetCapabilities()->SupportsOffscreenMSAA(),
"ImpellerOnscreen",
GetDefaultStencilConfig(reads_from_onscreen_backdrop));
Expand Down
Loading

0 comments on commit cf8eaa6

Please sign in to comment.