diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index e5abdae233f47..e4900a92bd7cc 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -176,6 +176,7 @@ ../../../flutter/impeller/renderer/backend/vulkan/context_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/descriptor_pool_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/fence_waiter_vk_unittests.cc +../../../flutter/impeller/renderer/backend/vulkan/render_pass_cache_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/resource_manager_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/test ../../../flutter/impeller/renderer/blit_pass_unittests.cc diff --git a/impeller/renderer/backend/vulkan/BUILD.gn b/impeller/renderer/backend/vulkan/BUILD.gn index dfd33336b921c..5d9e26df17fe3 100644 --- a/impeller/renderer/backend/vulkan/BUILD.gn +++ b/impeller/renderer/backend/vulkan/BUILD.gn @@ -14,6 +14,7 @@ impeller_component("vulkan_unittests") { "context_vk_unittests.cc", "descriptor_pool_vk_unittests.cc", "fence_waiter_vk_unittests.cc", + "render_pass_cache_unittests.cc", "resource_manager_vk_unittests.cc", "test/gpu_tracer_unittests.cc", "test/mock_vulkan.cc", @@ -23,6 +24,7 @@ impeller_component("vulkan_unittests") { ] deps = [ ":vulkan", + "../../../playground:playground_test", "//flutter/testing:testing_lib", ] } diff --git a/impeller/renderer/backend/vulkan/render_pass_cache_unittests.cc b/impeller/renderer/backend/vulkan/render_pass_cache_unittests.cc new file mode 100644 index 0000000000000..e2fc63ea5796a --- /dev/null +++ b/impeller/renderer/backend/vulkan/render_pass_cache_unittests.cc @@ -0,0 +1,51 @@ +// 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 "flutter/testing/testing.h" +#include "gtest/gtest.h" +#include "impeller/playground/playground_test.h" +#include "impeller/renderer/backend/vulkan/texture_vk.h" +#include "impeller/renderer/render_target.h" + +namespace impeller { +namespace testing { + +using RendererTest = PlaygroundTest; + +TEST_P(RendererTest, CachesRenderPassAndFramebuffer) { + if (GetBackend() != PlaygroundBackend::kVulkan) { + GTEST_SKIP() << "Test only applies to Vulkan"; + } + + auto allocator = std::make_shared( + GetContext()->GetResourceAllocator()); + + auto render_target = RenderTarget::CreateOffscreenMSAA( + *GetContext(), *allocator, {100, 100}, 1); + auto resolve_texture = + render_target.GetColorAttachments().find(0u)->second.resolve_texture; + auto& texture_vk = TextureVK::Cast(*resolve_texture); + + EXPECT_EQ(texture_vk.GetFramebuffer(), nullptr); + EXPECT_EQ(texture_vk.GetRenderPass(), nullptr); + + auto buffer = GetContext()->CreateCommandBuffer(); + auto render_pass = buffer->CreateRenderPass(render_target); + + EXPECT_NE(texture_vk.GetFramebuffer(), nullptr); + EXPECT_NE(texture_vk.GetRenderPass(), nullptr); + + render_pass->EncodeCommands(); + GetContext()->GetCommandQueue()->Submit({buffer}); + + // Can be reused without error. + auto buffer_2 = GetContext()->CreateCommandBuffer(); + auto render_pass_2 = buffer_2->CreateRenderPass(render_target); + + EXPECT_TRUE(render_pass_2->EncodeCommands()); + EXPECT_TRUE(GetContext()->GetCommandQueue()->Submit({buffer_2}).ok()); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.cc b/impeller/renderer/backend/vulkan/render_pass_vk.cc index a8ce872f6db47..f942d99ea64e2 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.cc +++ b/impeller/renderer/backend/vulkan/render_pass_vk.cc @@ -25,6 +25,7 @@ #include "impeller/renderer/backend/vulkan/shared_object_vk.h" #include "impeller/renderer/backend/vulkan/texture_vk.h" #include "impeller/renderer/backend/vulkan/vk.h" +#include "vulkan/vulkan_handles.hpp" namespace impeller { @@ -79,6 +80,7 @@ static std::vector GetVKClearValues( SharedHandleVK RenderPassVK::CreateVKRenderPass( const ContextVK& context, + const SharedHandleVK& recycled_renderpass, const std::shared_ptr& command_buffer) const { BarrierVK barrier; barrier.new_layout = vk::ImageLayout::eGeneral; @@ -127,6 +129,15 @@ SharedHandleVK RenderPassVK::CreateVKRenderPass( TextureVK::Cast(*stencil->texture).SetLayout(barrier); } + // There may exist a previous recycled render pass that we can continue using. + // This is probably compatible with the render pass we are about to construct, + // but I have not conclusively proven this. If there are scenarios that + // produce validation warnings, we could use them to determine if we need + // additional checks at this point to determine reusability. + if (recycled_renderpass != nullptr) { + return recycled_renderpass; + } + auto pass = builder.Build(context.GetDevice()); if (!pass) { @@ -143,6 +154,11 @@ RenderPassVK::RenderPassVK(const std::shared_ptr& context, const RenderTarget& target, std::shared_ptr command_buffer) : RenderPass(context, target), command_buffer_(std::move(command_buffer)) { + color_image_vk_ = + render_target_.GetColorAttachments().find(0u)->second.texture; + resolve_image_vk_ = + render_target_.GetColorAttachments().find(0u)->second.resolve_texture; + const auto& vk_context = ContextVK::Cast(*context); const std::shared_ptr& encoder = command_buffer_->GetEncoder(); @@ -154,16 +170,26 @@ RenderPassVK::RenderPassVK(const std::shared_ptr& context, return true; }); + SharedHandleVK recycled_render_pass; + SharedHandleVK recycled_framebuffer; + if (resolve_image_vk_) { + recycled_render_pass = TextureVK::Cast(*resolve_image_vk_).GetRenderPass(); + recycled_framebuffer = TextureVK::Cast(*resolve_image_vk_).GetFramebuffer(); + } + const auto& target_size = render_target_.GetRenderTargetSize(); - render_pass_ = CreateVKRenderPass(vk_context, command_buffer_); + render_pass_ = + CreateVKRenderPass(vk_context, recycled_render_pass, command_buffer_); if (!render_pass_) { VALIDATION_LOG << "Could not create renderpass."; is_valid_ = false; return; } - auto framebuffer = CreateVKFramebuffer(vk_context, *render_pass_); + auto framebuffer = (recycled_framebuffer == nullptr) + ? CreateVKFramebuffer(vk_context, *render_pass_) + : recycled_framebuffer; if (!framebuffer) { VALIDATION_LOG << "Could not create framebuffer."; is_valid_ = false; @@ -174,6 +200,10 @@ RenderPassVK::RenderPassVK(const std::shared_ptr& context, is_valid_ = false; return; } + if (resolve_image_vk_) { + TextureVK::Cast(*resolve_image_vk_).SetFramebuffer(framebuffer); + TextureVK::Cast(*resolve_image_vk_).SetRenderPass(render_pass_); + } auto clear_values = GetVKClearValues(render_target_); @@ -205,10 +235,6 @@ RenderPassVK::RenderPassVK(const std::shared_ptr& context, .setExtent(vk::Extent2D(sc.GetWidth(), sc.GetHeight())); command_buffer_vk_.setScissor(0, 1, &scissor); - color_image_vk_ = - render_target_.GetColorAttachments().find(0u)->second.texture; - resolve_image_vk_ = - render_target_.GetColorAttachments().find(0u)->second.resolve_texture; is_valid_ = true; } diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.h b/impeller/renderer/backend/vulkan/render_pass_vk.h index 4836cf4c86c81..bf05e523d7ced 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.h +++ b/impeller/renderer/backend/vulkan/render_pass_vk.h @@ -11,6 +11,7 @@ #include "impeller/renderer/backend/vulkan/shared_object_vk.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/render_target.h" +#include "vulkan/vulkan_handles.hpp" namespace impeller { @@ -123,6 +124,7 @@ class RenderPassVK final : public RenderPass { SharedHandleVK CreateVKRenderPass( const ContextVK& context, + const SharedHandleVK& recycled_renderpass, const std::shared_ptr& command_buffer) const; SharedHandleVK CreateVKFramebuffer( diff --git a/impeller/renderer/backend/vulkan/swapchain_image_vk.h b/impeller/renderer/backend/vulkan/swapchain_image_vk.h index 1c0b9c2e911e8..48673ebe4845d 100644 --- a/impeller/renderer/backend/vulkan/swapchain_image_vk.h +++ b/impeller/renderer/backend/vulkan/swapchain_image_vk.h @@ -9,6 +9,7 @@ #include "impeller/renderer/backend/vulkan/formats_vk.h" #include "impeller/renderer/backend/vulkan/texture_source_vk.h" #include "impeller/renderer/backend/vulkan/vk.h" +#include "vulkan/vulkan_handles.hpp" namespace impeller { diff --git a/impeller/renderer/backend/vulkan/texture_source_vk.h b/impeller/renderer/backend/vulkan/texture_source_vk.h index 0983bf74535d8..4f76a8067cf4f 100644 --- a/impeller/renderer/backend/vulkan/texture_source_vk.h +++ b/impeller/renderer/backend/vulkan/texture_source_vk.h @@ -10,7 +10,9 @@ #include "impeller/core/texture_descriptor.h" #include "impeller/renderer/backend/vulkan/barrier_vk.h" #include "impeller/renderer/backend/vulkan/formats_vk.h" +#include "impeller/renderer/backend/vulkan/shared_object_vk.h" #include "impeller/renderer/backend/vulkan/vk.h" +#include "vulkan/vulkan_handles.hpp" namespace impeller { diff --git a/impeller/renderer/backend/vulkan/texture_vk.cc b/impeller/renderer/backend/vulkan/texture_vk.cc index f96b4940a4d98..b105d31b96e85 100644 --- a/impeller/renderer/backend/vulkan/texture_vk.cc +++ b/impeller/renderer/backend/vulkan/texture_vk.cc @@ -173,4 +173,22 @@ vk::ImageView TextureVK::GetRenderTargetView() const { return source_->GetRenderTargetView(); } +void TextureVK::SetFramebuffer( + const SharedHandleVK& framebuffer) { + framebuffer_ = framebuffer; +} + +void TextureVK::SetRenderPass( + const SharedHandleVK& render_pass) { + render_pass_ = render_pass; +} + +SharedHandleVK TextureVK::GetFramebuffer() const { + return framebuffer_; +} + +SharedHandleVK TextureVK::GetRenderPass() const { + return render_pass_; +} + } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/texture_vk.h b/impeller/renderer/backend/vulkan/texture_vk.h index fd36213988bca..5826e3df78174 100644 --- a/impeller/renderer/backend/vulkan/texture_vk.h +++ b/impeller/renderer/backend/vulkan/texture_vk.h @@ -44,9 +44,37 @@ class TextureVK final : public Texture, public BackendCast { bool IsSwapchainImage() const { return source_->IsSwapchainImage(); } + // These methods should only be used by render_pass_vk.h + + /// Store the last framebuffer object used with this texture. + /// + /// This field is only set if this texture is used as the resolve texture + /// of a render pass. By construction, this framebuffer should be compatible + /// with any future render passes. + void SetFramebuffer(const SharedHandleVK& framebuffer); + + /// Store the last render pass object used with this texture. + /// + /// This field is only set if this texture is used as the resolve texture + /// of a render pass. By construction, this framebuffer should be compatible + /// with any future render passes. + void SetRenderPass(const SharedHandleVK& render_pass); + + /// Retrieve the last framebuffer object used with this texture. + /// + /// May be nullptr if no previous framebuffer existed. + SharedHandleVK GetFramebuffer() const; + + /// Retrieve the last render pass object used with this texture. + /// + /// May be nullptr if no previous render pass existed. + SharedHandleVK GetRenderPass() const; + private: std::weak_ptr context_; std::shared_ptr source_; + SharedHandleVK framebuffer_ = nullptr; + SharedHandleVK render_pass_ = nullptr; // |Texture| void SetLabel(std::string_view label) override;