forked from firka/flutter
[Impeller] Cache RenderPass/Framebuffer objects on the resolve texture sources. (flutter/engine#50142)
Cache vk::RenderPass and vk::Framebuffer objects on the resolve texture of any render target attachments. Use these on the next frame unconditionally. Fixes https://github.com/flutter/flutter/issues/141750
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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<RenderTargetAllocator>(
|
||||
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
|
||||
@@ -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<vk::ClearValue> GetVKClearValues(
|
||||
|
||||
SharedHandleVK<vk::RenderPass> RenderPassVK::CreateVKRenderPass(
|
||||
const ContextVK& context,
|
||||
const SharedHandleVK<vk::RenderPass>& recycled_renderpass,
|
||||
const std::shared_ptr<CommandBufferVK>& command_buffer) const {
|
||||
BarrierVK barrier;
|
||||
barrier.new_layout = vk::ImageLayout::eGeneral;
|
||||
@@ -127,6 +129,15 @@ SharedHandleVK<vk::RenderPass> 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<const Context>& context,
|
||||
const RenderTarget& target,
|
||||
std::shared_ptr<CommandBufferVK> 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<CommandEncoderVK>& encoder =
|
||||
command_buffer_->GetEncoder();
|
||||
@@ -154,16 +170,26 @@ RenderPassVK::RenderPassVK(const std::shared_ptr<const Context>& context,
|
||||
return true;
|
||||
});
|
||||
|
||||
SharedHandleVK<vk::RenderPass> recycled_render_pass;
|
||||
SharedHandleVK<vk::Framebuffer> 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<const Context>& 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<const Context>& 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<vk::RenderPass> CreateVKRenderPass(
|
||||
const ContextVK& context,
|
||||
const SharedHandleVK<vk::RenderPass>& recycled_renderpass,
|
||||
const std::shared_ptr<CommandBufferVK>& command_buffer) const;
|
||||
|
||||
SharedHandleVK<vk::Framebuffer> CreateVKFramebuffer(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -173,4 +173,22 @@ vk::ImageView TextureVK::GetRenderTargetView() const {
|
||||
return source_->GetRenderTargetView();
|
||||
}
|
||||
|
||||
void TextureVK::SetFramebuffer(
|
||||
const SharedHandleVK<vk::Framebuffer>& framebuffer) {
|
||||
framebuffer_ = framebuffer;
|
||||
}
|
||||
|
||||
void TextureVK::SetRenderPass(
|
||||
const SharedHandleVK<vk::RenderPass>& render_pass) {
|
||||
render_pass_ = render_pass;
|
||||
}
|
||||
|
||||
SharedHandleVK<vk::Framebuffer> TextureVK::GetFramebuffer() const {
|
||||
return framebuffer_;
|
||||
}
|
||||
|
||||
SharedHandleVK<vk::RenderPass> TextureVK::GetRenderPass() const {
|
||||
return render_pass_;
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@@ -44,9 +44,37 @@ class TextureVK final : public Texture, public BackendCast<TextureVK, Texture> {
|
||||
|
||||
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<vk::Framebuffer>& 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<vk::RenderPass>& render_pass);
|
||||
|
||||
/// Retrieve the last framebuffer object used with this texture.
|
||||
///
|
||||
/// May be nullptr if no previous framebuffer existed.
|
||||
SharedHandleVK<vk::Framebuffer> GetFramebuffer() const;
|
||||
|
||||
/// Retrieve the last render pass object used with this texture.
|
||||
///
|
||||
/// May be nullptr if no previous render pass existed.
|
||||
SharedHandleVK<vk::RenderPass> GetRenderPass() const;
|
||||
|
||||
private:
|
||||
std::weak_ptr<Context> context_;
|
||||
std::shared_ptr<TextureSourceVK> source_;
|
||||
SharedHandleVK<vk::Framebuffer> framebuffer_ = nullptr;
|
||||
SharedHandleVK<vk::RenderPass> render_pass_ = nullptr;
|
||||
|
||||
// |Texture|
|
||||
void SetLabel(std::string_view label) override;
|
||||
|
||||
Reference in New Issue
Block a user