diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index e104b088334ba..91b8b6d2c283a 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -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 diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 97ab61f6f88dd..0a19c690fc6a5 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -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 @@ -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 diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 056461e86863d..c55631be197ff 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -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", @@ -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", diff --git a/impeller/aiks/picture.cc b/impeller/aiks/picture.cc index 7ad91fb63ef0c..d7a6d4085811f 100644 --- a/impeller/aiks/picture.cc +++ b/impeller/aiks/picture.cc @@ -54,10 +54,15 @@ std::shared_ptr 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:: @@ -70,6 +75,7 @@ std::shared_ptr Picture::RenderToTexture( } else { target = RenderTarget::CreateOffscreen( *impeller_context, // context + render_target_allocator, // allocator size, // size "Picture Snapshot", // label RenderTarget::kDefaultColorAttachmentConfig // color_attachment_config diff --git a/impeller/core/BUILD.gn b/impeller/core/BUILD.gn index 68891ed145198..cc66990c909a7 100644 --- a/impeller/core/BUILD.gn +++ b/impeller/core/BUILD.gn @@ -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", + ] +} diff --git a/impeller/core/allocator.cc b/impeller/core/allocator.cc index 075a738fe5b4f..1785534a73d5b 100644 --- a/impeller/core/allocator.cc +++ b/impeller/core/allocator.cc @@ -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 { @@ -57,10 +58,10 @@ std::shared_ptr Allocator::CreateTexture( return OnCreateTexture(desc); } +void Allocator::DidAcquireSurfaceFrame() {} + uint16_t Allocator::MinimumBytesPerRow(PixelFormat format) const { return BytesPerPixelForPixelFormat(format); } -void Allocator::DidAcquireSurfaceFrame() {} - } // namespace impeller diff --git a/impeller/core/allocator.h b/impeller/core/allocator.h index 62b86b72b047f..d5f8aee10512e 100644 --- a/impeller/core/allocator.h +++ b/impeller/core/allocator.h @@ -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 { diff --git a/impeller/core/allocator_unittests.cc b/impeller/core/allocator_unittests.cc new file mode 100644 index 0000000000000..c0c2268dfb151 --- /dev/null +++ b/impeller/core/allocator_unittests.cc @@ -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 +#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 diff --git a/impeller/core/texture_descriptor.h b/impeller/core/texture_descriptor.h index 7d99d20408da5..feb012bb832eb 100644 --- a/impeller/core/texture_descriptor.h +++ b/impeller/core/texture_descriptor.h @@ -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() && // diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index bf5bbf8328580..1929514076645 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -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) { @@ -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", + ] +} diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index a727bf186e40d..809df7699b24c 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -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" @@ -162,7 +163,9 @@ ContentContext::ContentContext(std::shared_ptr context) : context_(std::move(context)), lazy_glyph_atlas_(std::make_shared()), tessellator_(std::make_shared()), - scene_context_(std::make_shared(context_)) { + scene_context_(std::make_shared(context_)), + render_target_cache_(std::make_shared( + context_->GetResourceAllocator())) { if (!context_ || !context_->IsValid()) { return; } @@ -359,7 +362,8 @@ std::shared_ptr 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. , @@ -368,7 +372,8 @@ std::shared_ptr 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. , diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 71bda2eccc3a9..86da8f98b2287 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -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 @@ -334,6 +335,7 @@ struct ContentContextOptions { }; class Tessellator; +class RenderTargetCache; class ContentContext { public: @@ -708,6 +710,10 @@ class ContentContext { return lazy_glyph_atlas_; } + std::shared_ptr GetRenderTargetCache() const { + return render_target_cache_; + } + private: std::shared_ptr context_; std::shared_ptr lazy_glyph_atlas_; @@ -865,6 +871,7 @@ class ContentContext { bool is_valid_ = false; std::shared_ptr tessellator_; std::shared_ptr scene_context_; + std::shared_ptr render_target_cache_; bool wireframe_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ContentContext); diff --git a/impeller/entity/contents/scene_contents.cc b/impeller/entity/contents/scene_contents.cc index bb00ff923e26e..bb4f014a2ebdb 100644 --- a/impeller/entity/contents/scene_contents.cc +++ b/impeller/entity/contents/scene_contents.cc @@ -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, diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 3a9537daafb24..a1a6d085f184c 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -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, @@ -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, @@ -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) == @@ -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) { @@ -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)); diff --git a/impeller/entity/render_target_cache.cc b/impeller/entity/render_target_cache.cc new file mode 100644 index 0000000000000..fc2ebf807205b --- /dev/null +++ b/impeller/entity/render_target_cache.cc @@ -0,0 +1,53 @@ +// 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 "impeller/entity/render_target_cache.h" +#include "impeller/renderer/render_target.h" + +namespace impeller { + +RenderTargetCache::RenderTargetCache(std::shared_ptr allocator) + : RenderTargetAllocator(std::move(allocator)) {} + +void RenderTargetCache::Start() { + for (auto& td : texture_data_) { + td.used_this_frame = false; + } +} + +void RenderTargetCache::End() { + std::vector retain; + + for (auto td : texture_data_) { + if (td.used_this_frame) { + retain.push_back(td); + } + } + texture_data_.swap(retain); +} + +size_t RenderTargetCache::CachedTextureCount() const { + return texture_data_.size(); +} + +std::shared_ptr RenderTargetCache::CreateTexture( + const TextureDescriptor& desc) { + FML_DCHECK(desc.storage_mode != StorageMode::kHostVisible); + FML_DCHECK(desc.usage & + static_cast(TextureUsage::kRenderTarget)); + + for (auto& td : texture_data_) { + const auto other_desc = td.texture->GetTextureDescriptor(); + if (!td.used_this_frame && desc == other_desc) { + td.used_this_frame = true; + return td.texture; + } + } + auto result = RenderTargetAllocator::CreateTexture(desc); + texture_data_.push_back( + TextureData{.used_this_frame = true, .texture = result}); + return result; +} + +} // namespace impeller diff --git a/impeller/entity/render_target_cache.h b/impeller/entity/render_target_cache.h new file mode 100644 index 0000000000000..512ad12ba9024 --- /dev/null +++ b/impeller/entity/render_target_cache.h @@ -0,0 +1,45 @@ +// 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. + +#pragma once + +#include "impeller/renderer/render_target.h" + +namespace impeller { + +/// @brief An implementation of the [RenderTargetAllocator] that caches all +/// allocated texture data for one frame. +/// +/// Any textures unused after a frame are immediately discarded. +class RenderTargetCache : public RenderTargetAllocator { + public: + explicit RenderTargetCache(std::shared_ptr allocator); + + ~RenderTargetCache() = default; + + // |RenderTargetAllocator| + void Start() override; + + // |RenderTargetAllocator| + void End() override; + + // |RenderTargetAllocator| + std::shared_ptr CreateTexture( + const TextureDescriptor& desc) override; + + // visible for testing. + size_t CachedTextureCount() const; + + private: + struct TextureData { + bool used_this_frame; + std::shared_ptr texture; + }; + + std::vector texture_data_; + + FML_DISALLOW_COPY_AND_ASSIGN(RenderTargetCache); +}; + +} // namespace impeller diff --git a/impeller/entity/render_target_cache_unittests.cc b/impeller/entity/render_target_cache_unittests.cc new file mode 100644 index 0000000000000..d8086b32aec44 --- /dev/null +++ b/impeller/entity/render_target_cache_unittests.cc @@ -0,0 +1,66 @@ +// 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 + +#include "flutter/testing/testing.h" +#include "impeller/core/allocator.h" +#include "impeller/core/texture_descriptor.h" +#include "impeller/entity/render_target_cache.h" +#include "impeller/renderer/testing/mocks.h" + +namespace impeller { +namespace testing { + +class TestAllocator : public Allocator { + public: + TestAllocator() = default; + + ~TestAllocator() = default; + + ISize GetMaxTextureSizeSupported() const override { + return ISize(1024, 1024); + }; + + std::shared_ptr OnCreateBuffer( + const DeviceBufferDescriptor& desc) override { + return std::make_shared(desc); + }; + + virtual std::shared_ptr OnCreateTexture( + const TextureDescriptor& desc) override { + return std::make_shared(desc); + }; +}; + +TEST(RenderTargetCacheTest, CachesUsedTexturesAcrossFrames) { + auto allocator = std::make_shared(); + auto render_target_cache = RenderTargetCache(allocator); + auto desc = TextureDescriptor{ + .format = PixelFormat::kR8G8B8A8UNormInt, + .size = ISize(100, 100), + .usage = static_cast(TextureUsage::kRenderTarget)}; + + render_target_cache.Start(); + // Create two textures of the same exact size/shape. Both should be marked + // as used this frame, so the cached data set will contain two. + render_target_cache.CreateTexture(desc); + render_target_cache.CreateTexture(desc); + + ASSERT_EQ(render_target_cache.CachedTextureCount(), 2u); + + render_target_cache.End(); + render_target_cache.Start(); + + // Next frame, only create one texture. The set will still contain two, + // but one will be removed at the end of the frame. + render_target_cache.CreateTexture(desc); + ASSERT_EQ(render_target_cache.CachedTextureCount(), 2u); + + render_target_cache.End(); + ASSERT_EQ(render_target_cache.CachedTextureCount(), 1u); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/gles/allocator_gles.h b/impeller/renderer/backend/gles/allocator_gles.h index 14f9dcf2dbe3a..4c3426d176cdc 100644 --- a/impeller/renderer/backend/gles/allocator_gles.h +++ b/impeller/renderer/backend/gles/allocator_gles.h @@ -21,7 +21,7 @@ class AllocatorGLES final : public Allocator { ReactorGLES::Ref reactor_; bool is_valid_ = false; - AllocatorGLES(ReactorGLES::Ref reactor); + explicit AllocatorGLES(ReactorGLES::Ref reactor); // |Allocator| bool IsValid() const; diff --git a/impeller/renderer/backend/vulkan/formats_vk.h b/impeller/renderer/backend/vulkan/formats_vk.h index e43aea7585249..529ef4da6afc5 100644 --- a/impeller/renderer/backend/vulkan/formats_vk.h +++ b/impeller/renderer/backend/vulkan/formats_vk.h @@ -466,7 +466,7 @@ constexpr vk::AttachmentDescription CreateAttachmentDescription( switch (kind) { case AttachmentKind::kColor: vk_attachment.initialLayout = current_layout; - vk_attachment.finalLayout = vk::ImageLayout::eGeneral; + vk_attachment.finalLayout = vk::ImageLayout::eColorAttachmentOptimal; break; case AttachmentKind::kDepth: case AttachmentKind::kStencil: diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.cc b/impeller/renderer/backend/vulkan/render_pass_vk.cc index 5cb38fecc9f9f..fa8f5b66cce06 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.cc +++ b/impeller/renderer/backend/vulkan/render_pass_vk.cc @@ -15,6 +15,7 @@ #include "impeller/core/formats.h" #include "impeller/core/sampler.h" #include "impeller/core/shader_types.h" +#include "impeller/renderer/backend/vulkan/barrier_vk.h" #include "impeller/renderer/backend/vulkan/command_buffer_vk.h" #include "impeller/renderer/backend/vulkan/command_encoder_vk.h" #include "impeller/renderer/backend/vulkan/context_vk.h" @@ -24,11 +25,13 @@ #include "impeller/renderer/backend/vulkan/sampler_vk.h" #include "impeller/renderer/backend/vulkan/shared_object_vk.h" #include "impeller/renderer/backend/vulkan/texture_vk.h" +#include "vulkan/vulkan_to_string.hpp" namespace impeller { static vk::AttachmentDescription CreateAttachmentDescription( const Attachment& attachment, + const std::shared_ptr& command_buffer, bool resolve_texture = false) { const auto& texture = resolve_texture ? attachment.resolve_texture : attachment.texture; @@ -37,7 +40,7 @@ static vk::AttachmentDescription CreateAttachmentDescription( } const auto& texture_vk = TextureVK::Cast(*texture); const auto& desc = texture->GetTextureDescriptor(); - const auto current_layout = texture_vk.GetLayout(); + auto current_layout = texture_vk.GetLayout(); auto load_action = attachment.load_action; auto store_action = attachment.store_action; @@ -52,6 +55,22 @@ static vk::AttachmentDescription CreateAttachmentDescription( store_action = StoreAction::kStore; } + if (current_layout != vk::ImageLayout::ePresentSrcKHR && + current_layout != vk::ImageLayout::eUndefined) { + BarrierVK barrier; + barrier.new_layout = vk::ImageLayout::eGeneral; + barrier.cmd_buffer = command_buffer->GetEncoder()->GetCommandBuffer(); + barrier.src_access = vk::AccessFlagBits::eShaderRead; + barrier.src_stage = vk::PipelineStageFlagBits::eFragmentShader; + barrier.dst_access = vk::AccessFlagBits::eColorAttachmentWrite | + vk::AccessFlagBits::eTransferWrite; + barrier.dst_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput | + vk::PipelineStageFlagBits::eTransfer; + + texture_vk.SetLayout(barrier); + current_layout = vk::ImageLayout::eGeneral; + } + const auto attachment_desc = CreateAttachmentDescription(desc.format, // desc.sample_count, // @@ -68,7 +87,8 @@ static vk::AttachmentDescription CreateAttachmentDescription( } SharedHandleVK RenderPassVK::CreateVKRenderPass( - const ContextVK& context) const { + const ContextVK& context, + const std::shared_ptr& command_buffer) const { std::vector attachments; std::vector color_refs; @@ -92,11 +112,13 @@ SharedHandleVK RenderPassVK::CreateVKRenderPass( color_refs[bind_point] = vk::AttachmentReference{static_cast(attachments.size()), vk::ImageLayout::eColorAttachmentOptimal}; - attachments.emplace_back(CreateAttachmentDescription(color)); + attachments.emplace_back( + CreateAttachmentDescription(color, command_buffer)); if (color.resolve_texture) { resolve_refs[bind_point] = vk::AttachmentReference{ static_cast(attachments.size()), vk::ImageLayout::eGeneral}; - attachments.emplace_back(CreateAttachmentDescription(color, true)); + attachments.emplace_back( + CreateAttachmentDescription(color, command_buffer, true)); } } @@ -104,7 +126,8 @@ SharedHandleVK RenderPassVK::CreateVKRenderPass( depth_stencil_ref = vk::AttachmentReference{ static_cast(attachments.size()), vk::ImageLayout::eDepthStencilAttachmentOptimal}; - attachments.emplace_back(CreateAttachmentDescription(depth.value())); + attachments.emplace_back( + CreateAttachmentDescription(depth.value(), command_buffer)); } if (auto stencil = render_target_.GetStencilAttachment(); @@ -112,7 +135,8 @@ SharedHandleVK RenderPassVK::CreateVKRenderPass( depth_stencil_ref = vk::AttachmentReference{ static_cast(attachments.size()), vk::ImageLayout::eDepthStencilAttachmentOptimal}; - attachments.emplace_back(CreateAttachmentDescription(stencil.value())); + attachments.emplace_back( + CreateAttachmentDescription(stencil.value(), command_buffer)); } vk::SubpassDescription subpass_desc; @@ -594,7 +618,7 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const { const auto& target_size = render_target_.GetRenderTargetSize(); - auto render_pass = CreateVKRenderPass(vk_context); + auto render_pass = CreateVKRenderPass(vk_context, command_buffer); if (!render_pass) { VALIDATION_LOG << "Could not create renderpass."; return false; diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.h b/impeller/renderer/backend/vulkan/render_pass_vk.h index 273c9089aad3f..818bb4c6003af 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.h +++ b/impeller/renderer/backend/vulkan/render_pass_vk.h @@ -45,7 +45,8 @@ class RenderPassVK final : public RenderPass { bool OnEncodeCommands(const Context& context) const override; SharedHandleVK CreateVKRenderPass( - const ContextVK& context) const; + const ContextVK& context, + const std::shared_ptr& command_buffer) const; SharedHandleVK CreateVKFramebuffer( const ContextVK& context, diff --git a/impeller/renderer/backend/vulkan/surface_context_vk.h b/impeller/renderer/backend/vulkan/surface_context_vk.h index 5847d49aa0d47..37a38e4dc9c2a 100644 --- a/impeller/renderer/backend/vulkan/surface_context_vk.h +++ b/impeller/renderer/backend/vulkan/surface_context_vk.h @@ -19,7 +19,7 @@ class SwapchainVK; class SurfaceContextVK : public Context, public BackendCast { public: - SurfaceContextVK(const std::shared_ptr& parent); + explicit SurfaceContextVK(const std::shared_ptr& parent); // |Context| ~SurfaceContextVK() override; diff --git a/impeller/renderer/render_target.cc b/impeller/renderer/render_target.cc index 46ca72b0a815b..636601ddd3874 100644 --- a/impeller/renderer/render_target.cc +++ b/impeller/renderer/render_target.cc @@ -14,6 +14,19 @@ namespace impeller { +RenderTargetAllocator::RenderTargetAllocator( + std::shared_ptr allocator) + : allocator_(std::move(allocator)) {} + +void RenderTargetAllocator::Start() {} + +void RenderTargetAllocator::End() {} + +std::shared_ptr RenderTargetAllocator::CreateTexture( + const TextureDescriptor& desc) { + return allocator_->CreateTexture(desc); +} + RenderTarget::RenderTarget() = default; RenderTarget::~RenderTarget() = default; @@ -209,6 +222,7 @@ const std::optional& RenderTarget::GetStencilAttachment() RenderTarget RenderTarget::CreateOffscreen( const Context& context, + RenderTargetAllocator& allocator, ISize size, const std::string& label, AttachmentConfig color_attachment_config, @@ -235,7 +249,7 @@ RenderTarget RenderTarget::CreateOffscreen( color0.clear_color = color_attachment_config.clear_color; color0.load_action = color_attachment_config.load_action; color0.store_action = color_attachment_config.store_action; - color0.texture = context.GetResourceAllocator()->CreateTexture(color_tex0); + color0.texture = allocator.CreateTexture(color_tex0); if (!color0.texture) { return {}; @@ -244,7 +258,7 @@ RenderTarget RenderTarget::CreateOffscreen( target.SetColorAttachment(color0, 0u); if (stencil_attachment_config.has_value()) { - target.SetupStencilAttachment(context, size, false, label, + target.SetupStencilAttachment(context, allocator, size, false, label, stencil_attachment_config.value()); } else { target.SetStencilAttachment(std::nullopt); @@ -255,6 +269,7 @@ RenderTarget RenderTarget::CreateOffscreen( RenderTarget RenderTarget::CreateOffscreenMSAA( const Context& context, + RenderTargetAllocator& allocator, ISize size, const std::string& label, AttachmentConfigMSAA color_attachment_config, @@ -281,8 +296,7 @@ RenderTarget RenderTarget::CreateOffscreenMSAA( color0_tex_desc.size = size; color0_tex_desc.usage = static_cast(TextureUsage::kRenderTarget); - auto color0_msaa_tex = - context.GetResourceAllocator()->CreateTexture(color0_tex_desc); + auto color0_msaa_tex = allocator.CreateTexture(color0_tex_desc); if (!color0_msaa_tex) { VALIDATION_LOG << "Could not create multisample color texture."; return {}; @@ -302,8 +316,7 @@ RenderTarget RenderTarget::CreateOffscreenMSAA( static_cast(TextureUsage::kRenderTarget) | static_cast(TextureUsage::kShaderRead); - auto color0_resolve_tex = - context.GetResourceAllocator()->CreateTexture(color0_resolve_tex_desc); + auto color0_resolve_tex = allocator.CreateTexture(color0_resolve_tex_desc); if (!color0_resolve_tex) { VALIDATION_LOG << "Could not create color texture."; return {}; @@ -324,7 +337,7 @@ RenderTarget RenderTarget::CreateOffscreenMSAA( // Create MSAA stencil texture. if (stencil_attachment_config.has_value()) { - target.SetupStencilAttachment(context, size, true, label, + target.SetupStencilAttachment(context, allocator, size, true, label, stencil_attachment_config.value()); } else { target.SetStencilAttachment(std::nullopt); @@ -335,6 +348,7 @@ RenderTarget RenderTarget::CreateOffscreenMSAA( void RenderTarget::SetupStencilAttachment( const Context& context, + RenderTargetAllocator& allocator, ISize size, bool msaa, const std::string& label, @@ -354,8 +368,7 @@ void RenderTarget::SetupStencilAttachment( stencil0.load_action = stencil_attachment_config.load_action; stencil0.store_action = stencil_attachment_config.store_action; stencil0.clear_stencil = 0u; - stencil0.texture = - context.GetResourceAllocator()->CreateTexture(stencil_tex0); + stencil0.texture = allocator.CreateTexture(stencil_tex0); if (!stencil0.texture) { return; // Error messages are handled by `Allocator::CreateTexture`. diff --git a/impeller/renderer/render_target.h b/impeller/renderer/render_target.h index bc54976a030a8..d808f9feeff1d 100644 --- a/impeller/renderer/render_target.h +++ b/impeller/renderer/render_target.h @@ -17,6 +17,34 @@ namespace impeller { class Context; +/// @brief a wrapper around the impeller [Allocator] instance that can be used +/// to provide caching of allocated render target textures. +class RenderTargetAllocator { + public: + explicit RenderTargetAllocator(std::shared_ptr allocator); + + virtual ~RenderTargetAllocator() = default; + + /// @brief Create a new render target texture, or recycle a previously + /// allocated render + /// target texture. + virtual std::shared_ptr CreateTexture(const TextureDescriptor& desc); + + /// @brief Mark the beginning of a frame workload. + /// + /// This may be used to reset any tracking state on whether or not a + /// particular texture instance is still in use. + virtual void Start(); + + /// @brief Mark the end of a frame workload. + /// + /// This may be used to deallocate any unused textures. + virtual void End(); + + private: + std::shared_ptr allocator_; +}; + class RenderTarget final { public: struct AttachmentConfig { @@ -55,6 +83,7 @@ class RenderTarget final { static RenderTarget CreateOffscreen( const Context& context, + RenderTargetAllocator& allocator, ISize size, const std::string& label = "Offscreen", AttachmentConfig color_attachment_config = kDefaultColorAttachmentConfig, @@ -63,6 +92,7 @@ class RenderTarget final { static RenderTarget CreateOffscreenMSAA( const Context& context, + RenderTargetAllocator& allocator, ISize size, const std::string& label = "Offscreen MSAA", AttachmentConfigMSAA color_attachment_config = @@ -77,6 +107,7 @@ class RenderTarget final { bool IsValid() const; void SetupStencilAttachment(const Context& context, + RenderTargetAllocator& allocator, ISize size, bool msaa, const std::string& label = "Offscreen", diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 09f0a20d92fe0..575178f229bf2 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -1149,7 +1149,9 @@ TEST_P(RendererTest, StencilMask) { stencil_config.load_action = LoadAction::kLoad; stencil_config.store_action = StoreAction::kDontCare; stencil_config.storage_mode = StorageMode::kHostVisible; - render_target.SetupStencilAttachment(*context, + auto render_target_allocator = + RenderTargetAllocator(context->GetResourceAllocator()); + render_target.SetupStencilAttachment(*context, render_target_allocator, render_target.GetRenderTargetSize(), true, "stencil", stencil_config); // Fill the stencil buffer with an checkerboard pattern.