Skip to content

Commit

Permalink
[Impeller] More efficient usage of transient onscreen attachments. (f…
Browse files Browse the repository at this point in the history
…lutter#51206)

Work towards flutter/flutter#144617

> The primary trick here is making sure that the transient attachments for the onscreen (MSAA and Depth/Stencil) are effectively recycled. In fact, I think we can actually make a single Onscreen MSAA and Onscreen Depth/Stencil and share them across the swapchain images - as they should be effectively synchronized already.

Fixes flutter/flutter#141750
  • Loading branch information
jonahwilliams authored Mar 8, 2024
1 parent 6d7168c commit 05fdf94
Show file tree
Hide file tree
Showing 14 changed files with 235 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ TEST_P(RendererTest, CachesRenderPassAndFramebuffer) {
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);
EXPECT_EQ(texture_vk.GetCachedFramebuffer(), nullptr);
EXPECT_EQ(texture_vk.GetCachedRenderPass(), nullptr);

auto buffer = GetContext()->CreateCommandBuffer();
auto render_pass = buffer->CreateRenderPass(render_target);

EXPECT_NE(texture_vk.GetFramebuffer(), nullptr);
EXPECT_NE(texture_vk.GetRenderPass(), nullptr);
EXPECT_NE(texture_vk.GetCachedFramebuffer(), nullptr);
EXPECT_NE(texture_vk.GetCachedRenderPass(), nullptr);

render_pass->EncodeCommands();
GetContext()->GetCommandQueue()->Submit({buffer});
Expand Down
10 changes: 6 additions & 4 deletions impeller/renderer/backend/vulkan/render_pass_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,10 @@ RenderPassVK::RenderPassVK(const std::shared_ptr<const Context>& context,
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();
recycled_render_pass =
TextureVK::Cast(*resolve_image_vk_).GetCachedRenderPass();
recycled_framebuffer =
TextureVK::Cast(*resolve_image_vk_).GetCachedFramebuffer();
}

const auto& target_size = render_target_.GetRenderTargetSize();
Expand All @@ -192,8 +194,8 @@ RenderPassVK::RenderPassVK(const std::shared_ptr<const Context>& context,
return;
}
if (resolve_image_vk_) {
TextureVK::Cast(*resolve_image_vk_).SetFramebuffer(framebuffer);
TextureVK::Cast(*resolve_image_vk_).SetRenderPass(render_pass_);
TextureVK::Cast(*resolve_image_vk_).SetCachedFramebuffer(framebuffer);
TextureVK::Cast(*resolve_image_vk_).SetCachedRenderPass(render_pass_);
}

auto clear_values = GetVKClearValues(render_target_);
Expand Down
13 changes: 11 additions & 2 deletions impeller/renderer/backend/vulkan/surface_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,13 @@ std::unique_ptr<SurfaceVK> SurfaceVK::WrapSwapchainImage(
msaa_tex_desc.size = swapchain_image->GetSize();
msaa_tex_desc.usage = static_cast<uint64_t>(TextureUsage::kRenderTarget);

if (!swapchain_image->HasMSAATexture()) {
if (!swapchain_image->GetMSAATexture()) {
msaa_tex = context->GetResourceAllocator()->CreateTexture(msaa_tex_desc);
msaa_tex->SetLabel("ImpellerOnscreenColorMSAA");
if (!msaa_tex) {
VALIDATION_LOG << "Could not allocate MSAA color texture.";
return nullptr;
}
swapchain_image->SetMSAATexture(msaa_tex);
} else {
msaa_tex = swapchain_image->GetMSAATexture();
}
Expand Down Expand Up @@ -77,6 +76,16 @@ std::unique_ptr<SurfaceVK> SurfaceVK::WrapSwapchainImage(

RenderTarget render_target_desc;
render_target_desc.SetColorAttachment(color0, 0u);
render_target_desc.SetupDepthStencilAttachments(
/*context=*/*context, //
/*allocator=*/*context->GetResourceAllocator(), //
/*size=*/swapchain_image->GetSize(), //
/*msaa=*/enable_msaa, //
/*label=*/"Onscreen", //
/*stencil_attachment_config=*/
RenderTarget::kDefaultStencilAttachmentConfig, //
/*depth_stencil_texture=*/swapchain_image->GetDepthStencilTexture() //
);

// The constructor is private. So make_unique may not be used.
return std::unique_ptr<SurfaceVK>(
Expand Down
6 changes: 5 additions & 1 deletion impeller/renderer/backend/vulkan/surface_vk.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

#include <memory>

#include "flutter/fml/macros.h"
#include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/backend/vulkan/swapchain_image_vk.h"
#include "impeller/renderer/surface.h"
Expand All @@ -18,6 +17,11 @@ class SurfaceVK final : public Surface {
public:
using SwapCallback = std::function<bool(void)>;

/// @brief Wrap the swapchain image in a Surface, which provides the
/// additional configuration required for usage as on onscreen render
/// target by Impeller.
///
/// This creates the associated MSAA and depth+stencil texture.
static std::unique_ptr<SurfaceVK> WrapSwapchainImage(
const std::shared_ptr<Context>& context,
std::shared_ptr<SwapchainImageVK>& swapchain_image,
Expand Down
15 changes: 10 additions & 5 deletions impeller/renderer/backend/vulkan/swapchain_image_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,20 @@ bool SwapchainImageVK::IsValid() const {
}

std::shared_ptr<Texture> SwapchainImageVK::GetMSAATexture() const {
return msaa_tex_;
return msaa_texture_;
}

bool SwapchainImageVK::HasMSAATexture() const {
return msaa_tex_ != nullptr;
std::shared_ptr<Texture> SwapchainImageVK::GetDepthStencilTexture() const {
return depth_stencil_texture_;
}

void SwapchainImageVK::SetMSAATexture(std::shared_ptr<Texture> msaa_tex) {
msaa_tex_ = std::move(msaa_tex);
void SwapchainImageVK::SetMSAATexture(std::shared_ptr<Texture> texture) {
msaa_texture_ = std::move(texture);
}

void SwapchainImageVK::SetDepthStencilTexture(
std::shared_ptr<Texture> texture) {
depth_stencil_texture_ = std::move(texture);
}

PixelFormat SwapchainImageVK::GetPixelFormat() const {
Expand Down
9 changes: 6 additions & 3 deletions impeller/renderer/backend/vulkan/swapchain_image_vk.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,24 @@ class SwapchainImageVK final : public TextureSourceVK {

std::shared_ptr<Texture> GetMSAATexture() const;

bool HasMSAATexture() const;
std::shared_ptr<Texture> GetDepthStencilTexture() const;

// |TextureSourceVK|
vk::ImageView GetImageView() const override;

vk::ImageView GetRenderTargetView() const override;

void SetMSAATexture(std::shared_ptr<Texture> msaa_tex);
void SetMSAATexture(std::shared_ptr<Texture> texture);

void SetDepthStencilTexture(std::shared_ptr<Texture> texture);

bool IsSwapchainImage() const override { return true; }

private:
vk::Image image_ = VK_NULL_HANDLE;
vk::UniqueImageView image_view_ = {};
std::shared_ptr<Texture> msaa_tex_;
std::shared_ptr<Texture> msaa_texture_;
std::shared_ptr<Texture> depth_stencil_texture_;
bool is_valid_ = false;

SwapchainImageVK(const SwapchainImageVK&) = delete;
Expand Down
37 changes: 37 additions & 0 deletions impeller/renderer/backend/vulkan/swapchain_impl_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "fml/synchronization/semaphore.h"
#include "impeller/base/validation.h"
#include "impeller/core/formats.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"
Expand Down Expand Up @@ -228,6 +229,40 @@ SwapchainImplVK::SwapchainImplVK(const std::shared_ptr<Context>& context,
texture_desc.size = ISize::MakeWH(swapchain_info.imageExtent.width,
swapchain_info.imageExtent.height);

// Allocate a single onscreen MSAA texture and Depth+Stencil Texture to
// be shared by all swapchain images.
TextureDescriptor msaa_desc;
msaa_desc.storage_mode = StorageMode::kDeviceTransient;
msaa_desc.type = TextureType::kTexture2DMultisample;
msaa_desc.sample_count = SampleCount::kCount4;
msaa_desc.format = texture_desc.format;
msaa_desc.size = texture_desc.size;
msaa_desc.usage = static_cast<uint64_t>(TextureUsage::kRenderTarget);

// The depth+stencil configuration matches the configuration used by
// RenderTarget::SetupDepthStencilAttachments and matching the swapchain
// image dimensions and sample count.
TextureDescriptor depth_stencil_desc;
depth_stencil_desc.storage_mode = StorageMode::kDeviceTransient;
if (enable_msaa) {
depth_stencil_desc.type = TextureType::kTexture2DMultisample;
depth_stencil_desc.sample_count = SampleCount::kCount4;
} else {
depth_stencil_desc.type = TextureType::kTexture2D;
depth_stencil_desc.sample_count = SampleCount::kCount1;
}
depth_stencil_desc.format =
context->GetCapabilities()->GetDefaultDepthStencilFormat();
depth_stencil_desc.size = texture_desc.size;
depth_stencil_desc.usage = static_cast<uint64_t>(TextureUsage::kRenderTarget);

std::shared_ptr<Texture> msaa_texture;
if (enable_msaa) {
msaa_texture = context->GetResourceAllocator()->CreateTexture(msaa_desc);
}
std::shared_ptr<Texture> depth_stencil_texture =
context->GetResourceAllocator()->CreateTexture(depth_stencil_desc);

std::vector<std::shared_ptr<SwapchainImageVK>> swapchain_images;
for (const auto& image : images) {
auto swapchain_image =
Expand All @@ -239,6 +274,8 @@ SwapchainImplVK::SwapchainImplVK(const std::shared_ptr<Context>& context,
VALIDATION_LOG << "Could not create swapchain image.";
return;
}
swapchain_image->SetMSAATexture(msaa_texture);
swapchain_image->SetDepthStencilTexture(depth_stencil_texture);

ContextVK::SetDebugName(
vk_context.GetDevice(), swapchain_image->GetImage(),
Expand Down
26 changes: 24 additions & 2 deletions impeller/renderer/backend/vulkan/test/mock_vulkan.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <utility>
#include <vector>

#include "fml/macros.h"
#include "impeller/base/thread_safety.h"
#include "impeller/renderer/backend/vulkan/vk.h" // IWYU pragma: keep.
#include "third_party/swiftshader/include/vulkan/vulkan_core.h"
Expand Down Expand Up @@ -40,10 +39,13 @@ struct MockImage {};

struct MockSwapchainKHR {
std::array<MockImage, 3> images;
size_t current_image = 0;
};

struct MockSemaphore {};

struct MockFramebuffer {};

static ISize currentImageSize = ISize{1, 1};

class MockDevice final {
Expand Down Expand Up @@ -721,10 +723,26 @@ VkResult vkAcquireNextImageKHR(VkDevice device,
VkSemaphore semaphore,
VkFence fence,
uint32_t* pImageIndex) {
*pImageIndex = 0;
auto current_index =
reinterpret_cast<MockSwapchainKHR*>(swapchain)->current_image++;
*pImageIndex = (current_index + 1) % 3u;
return VK_SUCCESS;
}

VkResult vkCreateFramebuffer(VkDevice device,
const VkFramebufferCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkFramebuffer* pFramebuffer) {
*pFramebuffer = reinterpret_cast<VkFramebuffer>(new MockFramebuffer());
return VK_SUCCESS;
}

void vkDestroyFramebuffer(VkDevice device,
VkFramebuffer framebuffer,
const VkAllocationCallbacks* pAllocator) {
delete reinterpret_cast<MockFramebuffer*>(framebuffer);
}

PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance,
const char* pName) {
if (strcmp("vkEnumerateInstanceExtensionProperties", pName) == 0) {
Expand Down Expand Up @@ -861,6 +879,10 @@ PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance,
return (PFN_vkVoidFunction)vkDestroySurfaceKHR;
} else if (strcmp("vkAcquireNextImageKHR", pName) == 0) {
return (PFN_vkVoidFunction)vkAcquireNextImageKHR;
} else if (strcmp("vkCreateFramebuffer", pName) == 0) {
return (PFN_vkVoidFunction)vkCreateFramebuffer;
} else if (strcmp("vkDestroyFramebuffer", pName) == 0) {
return (PFN_vkVoidFunction)vkDestroyFramebuffer;
}
return noop;
}
Expand Down
73 changes: 73 additions & 0 deletions impeller/renderer/backend/vulkan/test/swapchain_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
#include "gtest/gtest.h"
#include "impeller/renderer/backend/vulkan/swapchain_vk.h"
#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h"
#include "impeller/renderer/backend/vulkan/texture_vk.h"
#include "vulkan/vulkan_enums.hpp"
#include "vulkan/vulkan_handles.hpp"

namespace impeller {
namespace testing {
Expand Down Expand Up @@ -52,5 +54,76 @@ TEST(SwapchainTest, RecreateSwapchainWhenSizeChanges) {
EXPECT_EQ(image_b->GetSize(), expected_size);
}

TEST(SwapchainTest, CachesRenderPassOnSwapchainImage) {
auto const context = MockVulkanContextBuilder().Build();

auto surface = CreateSurface(*context);
auto swapchain =
SwapchainVK::Create(context, std::move(surface), ISize{1, 1});

EXPECT_TRUE(swapchain->IsValid());

// The mock swapchain will always create 3 images, verify each one starts
// out with the same MSAA and depth+stencil texture, and no cached
// framebuffer.
std::vector<std::shared_ptr<Texture>> msaa_textures;
std::vector<std::shared_ptr<Texture>> depth_stencil_textures;
for (auto i = 0u; i < 3u; i++) {
auto drawable = swapchain->AcquireNextDrawable();
RenderTarget render_target = drawable->GetTargetRenderPassDescriptor();

auto texture = render_target.GetRenderTargetTexture();
auto& texture_vk = TextureVK::Cast(*texture);
EXPECT_EQ(texture_vk.GetCachedFramebuffer(), nullptr);
EXPECT_EQ(texture_vk.GetCachedRenderPass(), nullptr);

auto command_buffer = context->CreateCommandBuffer();
auto render_pass = command_buffer->CreateRenderPass(render_target);
render_pass->EncodeCommands();

auto& depth = render_target.GetDepthAttachment();
depth_stencil_textures.push_back(depth.has_value() ? depth->texture
: nullptr);
msaa_textures.push_back(
render_target.GetColorAttachments().find(0u)->second.texture);
}

for (auto i = 1; i < 3; i++) {
EXPECT_EQ(msaa_textures[i - 1], msaa_textures[i]);
EXPECT_EQ(depth_stencil_textures[i - 1], depth_stencil_textures[i]);
}

// After each images has been acquired once and the render pass presented,
// each should have a cached framebuffer and render pass.

std::vector<SharedHandleVK<vk::Framebuffer>> framebuffers;
std::vector<SharedHandleVK<vk::RenderPass>> render_passes;
for (auto i = 0u; i < 3u; i++) {
auto drawable = swapchain->AcquireNextDrawable();
RenderTarget render_target = drawable->GetTargetRenderPassDescriptor();

auto texture = render_target.GetRenderTargetTexture();
auto& texture_vk = TextureVK::Cast(*texture);

EXPECT_NE(texture_vk.GetCachedFramebuffer(), nullptr);
EXPECT_NE(texture_vk.GetCachedRenderPass(), nullptr);
framebuffers.push_back(texture_vk.GetCachedFramebuffer());
render_passes.push_back(texture_vk.GetCachedRenderPass());
}

// Iterate through once more to verify render passes and framebuffers are
// unchanged.
for (auto i = 0u; i < 3u; i++) {
auto drawable = swapchain->AcquireNextDrawable();
RenderTarget render_target = drawable->GetTargetRenderPassDescriptor();

auto texture = render_target.GetRenderTargetTexture();
auto& texture_vk = TextureVK::Cast(*texture);

EXPECT_EQ(texture_vk.GetCachedFramebuffer(), framebuffers[i]);
EXPECT_EQ(texture_vk.GetCachedRenderPass(), render_passes[i]);
}
}

} // namespace testing
} // namespace impeller
18 changes: 18 additions & 0 deletions impeller/renderer/backend/vulkan/texture_source_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,22 @@ fml::Status TextureSourceVK::SetLayout(const BarrierVK& barrier) const {
return {};
}

void TextureSourceVK::SetCachedFramebuffer(
const SharedHandleVK<vk::Framebuffer>& framebuffer) {
framebuffer_ = framebuffer;
}

void TextureSourceVK::SetCachedRenderPass(
const SharedHandleVK<vk::RenderPass>& render_pass) {
render_pass_ = render_pass;
}

SharedHandleVK<vk::Framebuffer> TextureSourceVK::GetCachedFramebuffer() const {
return framebuffer_;
}

SharedHandleVK<vk::RenderPass> TextureSourceVK::GetCachedRenderPass() const {
return render_pass_;
}

} // namespace impeller
Loading

0 comments on commit 05fdf94

Please sign in to comment.