Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to go and add a comment to SurfaceVK::WrapSwapchainImage (in the .h).

Based on what you told me, maybe something like:

Wraps a swapchain image for rendering with MSAA and depth/stencil support.

This function takes an device(?)-provided swapchain image (which is the resolve target
of a potential MSAA render process) and prepares it for use by creating necessary
supporting textures. This includes the MSAA texture (if enabled) and the depth/stencil
texture for rendering.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc comment added

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope of this PR, but why do we have this code:

if (!context || !swapchain_image || !swap_callback) {
  return nullptr;
}

When is it valid not to have these things? I'm in particular confused about swap_callback, because below:

bool SurfaceVK::Present() const {
  return swap_callback_ ? swap_callback_() : false;
}

It's impossible for this to be null, right?

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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we expand this with /*=param*/ comments?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

/*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_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for spelling out texture.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one was my fault initially

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are all these values picked/known?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment, but mostly it matches the swapchain + the convention we use.

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 @@ -715,10 +717,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 @@ -855,6 +873,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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the test directly verify the "primary trick" outlined in the change context (recycling a single MSAA and Depth/Stencil attachment)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does now

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