diff --git a/impeller/playground/backend/vulkan/playground_impl_vk.cc b/impeller/playground/backend/vulkan/playground_impl_vk.cc index 068cf2e56ad6d..c6e4511ecded3 100644 --- a/impeller/playground/backend/vulkan/playground_impl_vk.cc +++ b/impeller/playground/backend/vulkan/playground_impl_vk.cc @@ -85,6 +85,11 @@ PlaygroundImplVK::PlaygroundImplVK(PlaygroundSwitches switches) return; } + int width = 0; + int height = 0; + ::glfwGetWindowSize(window, &width, &height); + size_ = ISize{width, height}; + handle_.reset(window); ContextVK::Settings context_settings; @@ -125,7 +130,7 @@ PlaygroundImplVK::PlaygroundImplVK(PlaygroundSwitches switches) vk::UniqueSurfaceKHR surface{vk_surface, context_vk->GetInstance()}; auto context = context_vk->CreateSurfaceContext(); - if (!context->SetWindowSurface(std::move(surface))) { + if (!context->SetWindowSurface(std::move(surface), size_)) { VALIDATION_LOG << "Could not set up surface for context."; return; } @@ -150,6 +155,14 @@ std::unique_ptr PlaygroundImplVK::AcquireSurfaceFrame( std::shared_ptr context) { SurfaceContextVK* surface_context_vk = reinterpret_cast(context_.get()); + + int width = 0; + int height = 0; + ::glfwGetWindowSize(reinterpret_cast(handle_.get()), &width, + &height); + size_ = ISize{width, height}; + surface_context_vk->UpdateSurfaceSize(ISize{width, height}); + return surface_context_vk->AcquireNextSurface(); } diff --git a/impeller/playground/backend/vulkan/playground_impl_vk.h b/impeller/playground/backend/vulkan/playground_impl_vk.h index 78edc7f06d46c..83237b34da64a 100644 --- a/impeller/playground/backend/vulkan/playground_impl_vk.h +++ b/impeller/playground/backend/vulkan/playground_impl_vk.h @@ -27,6 +27,7 @@ class PlaygroundImplVK final : public PlaygroundImpl { static void DestroyWindowHandle(WindowHandle handle); using UniqueHandle = std::unique_ptr; UniqueHandle handle_; + ISize size_ = {1, 1}; // A global Vulkan instance which ensures that the Vulkan library will remain // loaded throughout the lifetime of the process. diff --git a/impeller/renderer/backend/vulkan/BUILD.gn b/impeller/renderer/backend/vulkan/BUILD.gn index f759ea269b24e..b80b9abde342c 100644 --- a/impeller/renderer/backend/vulkan/BUILD.gn +++ b/impeller/renderer/backend/vulkan/BUILD.gn @@ -19,6 +19,7 @@ impeller_component("vulkan_unittests") { "test/mock_vulkan.cc", "test/mock_vulkan.h", "test/mock_vulkan_unittests.cc", + "test/swapchain_unittests.cc", ] deps = [ ":vulkan", diff --git a/impeller/renderer/backend/vulkan/gpu_tracer_vk.cc b/impeller/renderer/backend/vulkan/gpu_tracer_vk.cc index ea26279a206e8..348ede016d73c 100644 --- a/impeller/renderer/backend/vulkan/gpu_tracer_vk.cc +++ b/impeller/renderer/backend/vulkan/gpu_tracer_vk.cc @@ -76,6 +76,9 @@ bool GPUTracerVK::IsEnabled() const { } void GPUTracerVK::MarkFrameStart() { + if (!enabled_) { + return; + } FML_DCHECK(!in_frame_); in_frame_ = true; raster_thread_id_ = std::this_thread::get_id(); diff --git a/impeller/renderer/backend/vulkan/surface_context_vk.cc b/impeller/renderer/backend/vulkan/surface_context_vk.cc index 8abea86d3c419..d4b410ee7a628 100644 --- a/impeller/renderer/backend/vulkan/surface_context_vk.cc +++ b/impeller/renderer/backend/vulkan/surface_context_vk.cc @@ -8,6 +8,7 @@ #include "impeller/renderer/backend/vulkan/command_pool_vk.h" #include "impeller/renderer/backend/vulkan/context_vk.h" #include "impeller/renderer/backend/vulkan/swapchain_vk.h" +#include "impeller/renderer/surface.h" namespace impeller { @@ -61,8 +62,9 @@ void SurfaceContextVK::Shutdown() { parent_->Shutdown(); } -bool SurfaceContextVK::SetWindowSurface(vk::UniqueSurfaceKHR surface) { - auto swapchain = SwapchainVK::Create(parent_, std::move(surface)); +bool SurfaceContextVK::SetWindowSurface(vk::UniqueSurfaceKHR surface, + const ISize& size) { + auto swapchain = SwapchainVK::Create(parent_, std::move(surface), size); if (!swapchain) { VALIDATION_LOG << "Could not create swapchain."; return false; @@ -93,6 +95,10 @@ void SurfaceContextVK::SetSyncPresentation(bool value) { parent_->SetSyncPresentation(value); } +void SurfaceContextVK::UpdateSurfaceSize(const ISize& size) const { + swapchain_->UpdateSurfaceSize(size); +} + #ifdef FML_OS_ANDROID vk::UniqueSurfaceKHR SurfaceContextVK::CreateAndroidSurface( diff --git a/impeller/renderer/backend/vulkan/surface_context_vk.h b/impeller/renderer/backend/vulkan/surface_context_vk.h index 56d6b0275dbb5..d9d889d68ef45 100644 --- a/impeller/renderer/backend/vulkan/surface_context_vk.h +++ b/impeller/renderer/backend/vulkan/surface_context_vk.h @@ -72,10 +72,15 @@ class SurfaceContextVK : public Context, // |Context| void SetSyncPresentation(bool value) override; - [[nodiscard]] bool SetWindowSurface(vk::UniqueSurfaceKHR surface); + [[nodiscard]] bool SetWindowSurface(vk::UniqueSurfaceKHR surface, + const ISize& size); std::unique_ptr AcquireNextSurface(); + /// @brief Mark the current swapchain configuration as dirty, forcing it to be + /// recreated on the next frame. + void UpdateSurfaceSize(const ISize& size) const; + #ifdef FML_OS_ANDROID vk::UniqueSurfaceKHR CreateAndroidSurface(ANativeWindow* window) const; #endif // FML_OS_ANDROID diff --git a/impeller/renderer/backend/vulkan/surface_vk.cc b/impeller/renderer/backend/vulkan/surface_vk.cc index 95846affdc1c3..c5700a7783949 100644 --- a/impeller/renderer/backend/vulkan/surface_vk.cc +++ b/impeller/renderer/backend/vulkan/surface_vk.cc @@ -4,6 +4,7 @@ #include "impeller/renderer/backend/vulkan/surface_vk.h" +#include "impeller/core/formats.h" #include "impeller/renderer/backend/vulkan/swapchain_image_vk.h" #include "impeller/renderer/backend/vulkan/texture_vk.h" #include "impeller/renderer/surface.h" @@ -13,30 +14,33 @@ namespace impeller { std::unique_ptr SurfaceVK::WrapSwapchainImage( const std::shared_ptr& context, std::shared_ptr& swapchain_image, - SwapCallback swap_callback) { + SwapCallback swap_callback, + bool enable_msaa) { if (!context || !swapchain_image || !swap_callback) { return nullptr; } - TextureDescriptor msaa_tex_desc; - msaa_tex_desc.storage_mode = StorageMode::kDeviceTransient; - msaa_tex_desc.type = TextureType::kTexture2DMultisample; - msaa_tex_desc.sample_count = SampleCount::kCount4; - msaa_tex_desc.format = swapchain_image->GetPixelFormat(); - msaa_tex_desc.size = swapchain_image->GetSize(); - msaa_tex_desc.usage = static_cast(TextureUsage::kRenderTarget); - std::shared_ptr msaa_tex; - if (!swapchain_image->HasMSAATexture()) { - 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; + if (enable_msaa) { + TextureDescriptor msaa_tex_desc; + msaa_tex_desc.storage_mode = StorageMode::kDeviceTransient; + msaa_tex_desc.type = TextureType::kTexture2DMultisample; + msaa_tex_desc.sample_count = SampleCount::kCount4; + msaa_tex_desc.format = swapchain_image->GetPixelFormat(); + msaa_tex_desc.size = swapchain_image->GetSize(); + msaa_tex_desc.usage = static_cast(TextureUsage::kRenderTarget); + + if (!swapchain_image->HasMSAATexture()) { + 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(); } - swapchain_image->SetMSAATexture(msaa_tex); - } else { - msaa_tex = swapchain_image->GetMSAATexture(); } TextureDescriptor resolve_tex_desc; @@ -60,11 +64,16 @@ std::unique_ptr SurfaceVK::WrapSwapchainImage( resolve_tex->SetLabel("ImpellerOnscreenResolve"); ColorAttachment color0; - color0.texture = msaa_tex; color0.clear_color = Color::DarkSlateGray(); color0.load_action = LoadAction::kClear; - color0.store_action = StoreAction::kMultisampleResolve; - color0.resolve_texture = resolve_tex; + if (enable_msaa) { + color0.texture = msaa_tex; + color0.store_action = StoreAction::kMultisampleResolve; + color0.resolve_texture = resolve_tex; + } else { + color0.texture = resolve_tex; + color0.store_action = StoreAction::kStore; + } RenderTarget render_target_desc; render_target_desc.SetColorAttachment(color0, 0u); diff --git a/impeller/renderer/backend/vulkan/surface_vk.h b/impeller/renderer/backend/vulkan/surface_vk.h index fef4187f3e696..ea4203135324c 100644 --- a/impeller/renderer/backend/vulkan/surface_vk.h +++ b/impeller/renderer/backend/vulkan/surface_vk.h @@ -21,7 +21,8 @@ class SurfaceVK final : public Surface { static std::unique_ptr WrapSwapchainImage( const std::shared_ptr& context, std::shared_ptr& swapchain_image, - SwapCallback swap_callback); + SwapCallback swap_callback, + bool enable_msaa = true); // |Surface| ~SurfaceVK() override; diff --git a/impeller/renderer/backend/vulkan/swapchain_image_vk.h b/impeller/renderer/backend/vulkan/swapchain_image_vk.h index ec426883d2975..1c0b9c2e911e8 100644 --- a/impeller/renderer/backend/vulkan/swapchain_image_vk.h +++ b/impeller/renderer/backend/vulkan/swapchain_image_vk.h @@ -5,7 +5,6 @@ #ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_IMAGE_VK_H_ #define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_IMAGE_VK_H_ -#include "flutter/fml/macros.h" #include "impeller/geometry/size.h" #include "impeller/renderer/backend/vulkan/formats_vk.h" #include "impeller/renderer/backend/vulkan/texture_source_vk.h" diff --git a/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc b/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc index 81a623a59a7d2..bca50c0f68fdc 100644 --- a/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc @@ -145,17 +145,18 @@ static std::optional ChoosePresentQueue( std::shared_ptr SwapchainImplVK::Create( const std::shared_ptr& context, vk::UniqueSurfaceKHR surface, - vk::SwapchainKHR old_swapchain, - vk::SurfaceTransformFlagBitsKHR last_transform) { + const ISize& size, + bool enable_msaa, + vk::SwapchainKHR old_swapchain) { return std::shared_ptr(new SwapchainImplVK( - context, std::move(surface), old_swapchain, last_transform)); + context, std::move(surface), size, enable_msaa, old_swapchain)); } -SwapchainImplVK::SwapchainImplVK( - const std::shared_ptr& context, - vk::UniqueSurfaceKHR surface, - vk::SwapchainKHR old_swapchain, - vk::SurfaceTransformFlagBitsKHR last_transform) { +SwapchainImplVK::SwapchainImplVK(const std::shared_ptr& context, + vk::UniqueSurfaceKHR surface, + const ISize& size, + bool enable_msaa, + vk::SwapchainKHR old_swapchain) { if (!context) { VALIDATION_LOG << "Cannot create a swapchain without a context."; return; @@ -209,10 +210,10 @@ SwapchainImplVK::SwapchainImplVK( swapchain_info.imageColorSpace = format.value().colorSpace; swapchain_info.presentMode = vk::PresentModeKHR::eFifo; swapchain_info.imageExtent = vk::Extent2D{ - std::clamp(surface_caps.currentExtent.width, + std::clamp(static_cast(size.width), surface_caps.minImageExtent.width, surface_caps.maxImageExtent.width), - std::clamp(surface_caps.currentExtent.height, + std::clamp(static_cast(size.height), surface_caps.minImageExtent.height, surface_caps.maxImageExtent.height), }; @@ -304,14 +305,19 @@ SwapchainImplVK::SwapchainImplVK( images_ = std::move(swapchain_images); synchronizers_ = std::move(synchronizers); current_frame_ = synchronizers_.size() - 1u; + size_ = size; + enable_msaa_ = enable_msaa; is_valid_ = true; - transform_if_changed_discard_swapchain_ = last_transform; } SwapchainImplVK::~SwapchainImplVK() { DestroySwapchain(); } +const ISize& SwapchainImplVK::GetSize() const { + return size_; +} + bool SwapchainImplVK::IsValid() const { return is_valid_; } @@ -337,10 +343,6 @@ vk::Format SwapchainImplVK::GetSurfaceFormat() const { return surface_format_; } -vk::SurfaceTransformFlagBitsKHR SwapchainImplVK::GetLastTransform() const { - return transform_if_changed_discard_swapchain_; -} - std::shared_ptr SwapchainImplVK::GetContext() const { return context_.lock(); } @@ -365,26 +367,6 @@ SwapchainImplVK::AcquireResult SwapchainImplVK::AcquireNextDrawable() { return SwapchainImplVK::AcquireResult{}; } - //---------------------------------------------------------------------------- - /// Poll to see if the orientation has changed. - /// - /// https://developer.android.com/games/optimize/vulkan-prerotation#using_polling - current_transform_poll_count_++; - if (current_transform_poll_count_ >= kPollFramesForOrientation) { - current_transform_poll_count_ = 0u; - auto [caps_result, caps] = - context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(*surface_); - if (caps_result != vk::Result::eSuccess) { - VALIDATION_LOG << "Could not get surface capabilities: " - << vk::to_string(caps_result); - return SwapchainImplVK::AcquireResult{}; - } - if (caps.currentTransform != transform_if_changed_discard_swapchain_) { - transform_if_changed_discard_swapchain_ = caps.currentTransform; - return AcquireResult{true /* out of date */}; - } - } - //---------------------------------------------------------------------------- /// Get the next image index. /// @@ -430,7 +412,8 @@ SwapchainImplVK::AcquireResult SwapchainImplVK::AcquireNextDrawable() { return false; } return swapchain->Present(image, image_index); - } // swap callback + }, // swap callback + enable_msaa_ // )}; } diff --git a/impeller/renderer/backend/vulkan/swapchain_impl_vk.h b/impeller/renderer/backend/vulkan/swapchain_impl_vk.h index 87ed948b215f2..b4126526fcbb6 100644 --- a/impeller/renderer/backend/vulkan/swapchain_impl_vk.h +++ b/impeller/renderer/backend/vulkan/swapchain_impl_vk.h @@ -9,7 +9,7 @@ #include #include -#include "flutter/fml/macros.h" +#include "impeller/geometry/size.h" #include "impeller/renderer/backend/vulkan/vk.h" #include "vulkan/vulkan_enums.hpp" @@ -34,9 +34,9 @@ class SwapchainImplVK final static std::shared_ptr Create( const std::shared_ptr& context, vk::UniqueSurfaceKHR surface, - vk::SwapchainKHR old_swapchain = VK_NULL_HANDLE, - vk::SurfaceTransformFlagBitsKHR last_transform = - vk::SurfaceTransformFlagBitsKHR::eIdentity); + const ISize& size, + bool enable_msaa = true, + vk::SwapchainKHR old_swapchain = VK_NULL_HANDLE); ~SwapchainImplVK(); @@ -57,12 +57,12 @@ class SwapchainImplVK final vk::Format GetSurfaceFormat() const; - vk::SurfaceTransformFlagBitsKHR GetLastTransform() const; - std::shared_ptr GetContext() const; std::pair DestroySwapchain(); + const ISize& GetSize() const; + private: std::weak_ptr context_; vk::UniqueSurfaceKHR surface_; @@ -72,14 +72,15 @@ class SwapchainImplVK final std::vector> images_; std::vector> synchronizers_; size_t current_frame_ = 0u; + ISize size_; + bool enable_msaa_ = true; bool is_valid_ = false; - size_t current_transform_poll_count_ = 0u; - vk::SurfaceTransformFlagBitsKHR transform_if_changed_discard_swapchain_; SwapchainImplVK(const std::shared_ptr& context, vk::UniqueSurfaceKHR surface, - vk::SwapchainKHR old_swapchain, - vk::SurfaceTransformFlagBitsKHR last_transform); + const ISize& size, + bool enable_msaa, + vk::SwapchainKHR old_swapchain); bool Present(const std::shared_ptr& image, uint32_t index); diff --git a/impeller/renderer/backend/vulkan/swapchain_vk.cc b/impeller/renderer/backend/vulkan/swapchain_vk.cc index c5399ce4835e7..22dc195ebfdb8 100644 --- a/impeller/renderer/backend/vulkan/swapchain_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain_vk.cc @@ -12,17 +12,23 @@ namespace impeller { std::shared_ptr SwapchainVK::Create( const std::shared_ptr& context, - vk::UniqueSurfaceKHR surface) { - auto impl = SwapchainImplVK::Create(context, std::move(surface)); + vk::UniqueSurfaceKHR surface, + const ISize& size, + bool enable_msaa) { + auto impl = + SwapchainImplVK::Create(context, std::move(surface), size, enable_msaa); if (!impl || !impl->IsValid()) { VALIDATION_LOG << "Failed to create SwapchainVK implementation."; return nullptr; } - return std::shared_ptr(new SwapchainVK(std::move(impl))); + return std::shared_ptr( + new SwapchainVK(std::move(impl), size, enable_msaa)); } -SwapchainVK::SwapchainVK(std::shared_ptr impl) - : impl_(std::move(impl)) {} +SwapchainVK::SwapchainVK(std::shared_ptr impl, + const ISize& size, + bool enable_msaa) + : impl_(std::move(impl)), size_(size), enable_msaa_(enable_msaa) {} SwapchainVK::~SwapchainVK() = default; @@ -30,6 +36,12 @@ bool SwapchainVK::IsValid() const { return impl_ ? impl_->IsValid() : false; } +void SwapchainVK::UpdateSurfaceSize(const ISize& size) { + // Update the size of the swapchain. On the next acquired drawable, + // the sizes may no longer match, forcing the swapchain to be recreated. + size_ = size; +} + std::unique_ptr SwapchainVK::AcquireNextDrawable() { if (!IsValid()) { return nullptr; @@ -38,7 +50,7 @@ std::unique_ptr SwapchainVK::AcquireNextDrawable() { TRACE_EVENT0("impeller", __FUNCTION__); auto result = impl_->AcquireNextDrawable(); - if (!result.out_of_date) { + if (!result.out_of_date && size_ == impl_->GetSize()) { return std::move(result.surface); } @@ -49,10 +61,11 @@ std::unique_ptr SwapchainVK::AcquireNextDrawable() { auto context = impl_->GetContext(); auto [surface, old_swapchain] = impl_->DestroySwapchain(); - auto new_impl = SwapchainImplVK::Create(context, // - std::move(surface), // - *old_swapchain, // - impl_->GetLastTransform() // + auto new_impl = SwapchainImplVK::Create(context, // + std::move(surface), // + size_, // + enable_msaa_, // + *old_swapchain // ); if (!new_impl || !new_impl->IsValid()) { VALIDATION_LOG << "Could not update swapchain."; diff --git a/impeller/renderer/backend/vulkan/swapchain_vk.h b/impeller/renderer/backend/vulkan/swapchain_vk.h index 2e724bae8ff29..1f02f1cd606dd 100644 --- a/impeller/renderer/backend/vulkan/swapchain_vk.h +++ b/impeller/renderer/backend/vulkan/swapchain_vk.h @@ -7,7 +7,7 @@ #include -#include "flutter/fml/macros.h" +#include "impeller/geometry/size.h" #include "impeller/renderer/backend/vulkan/vk.h" #include "impeller/renderer/context.h" #include "impeller/renderer/surface.h" @@ -26,7 +26,9 @@ class SwapchainVK { public: static std::shared_ptr Create( const std::shared_ptr& context, - vk::UniqueSurfaceKHR surface); + vk::UniqueSurfaceKHR surface, + const ISize& size, + bool enable_msaa = true); ~SwapchainVK(); @@ -36,10 +38,18 @@ class SwapchainVK { vk::Format GetSurfaceFormat() const; + /// @brief Mark the current swapchain configuration as dirty, forcing it to be + /// recreated on the next frame. + void UpdateSurfaceSize(const ISize& size); + private: std::shared_ptr impl_; + ISize size_; + const bool enable_msaa_; - explicit SwapchainVK(std::shared_ptr impl); + SwapchainVK(std::shared_ptr impl, + const ISize& size, + bool enable_msaa); SwapchainVK(const SwapchainVK&) = delete; diff --git a/impeller/renderer/backend/vulkan/test/mock_vulkan.cc b/impeller/renderer/backend/vulkan/test/mock_vulkan.cc index 017f1fbcbcd7c..0b42aa6d9c99a 100644 --- a/impeller/renderer/backend/vulkan/test/mock_vulkan.cc +++ b/impeller/renderer/backend/vulkan/test/mock_vulkan.cc @@ -14,6 +14,7 @@ #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" +#include "vulkan/vulkan.hpp" #include "vulkan/vulkan_core.h" namespace impeller { @@ -34,6 +35,16 @@ struct MockCommandPool {}; struct MockDescriptorPool {}; +struct MockSurfaceKHR {}; + +struct MockSwapchainKHR {}; + +struct MockImage {}; + +struct MockSemaphore {}; + +static ISize currentImageSize = ISize{1, 1}; + class MockDevice final { public: explicit MockDevice() : called_functions_(new std::vector()) {} @@ -80,16 +91,16 @@ class MockDevice final { MockDevice& operator=(const MockDevice&) = delete; Mutex called_functions_mutex_; - std::shared_ptr> called_functions_ - IPLR_GUARDED_BY(called_functions_mutex_); + std::shared_ptr> called_functions_ IPLR_GUARDED_BY( + called_functions_mutex_); Mutex command_buffers_mutex_; std::vector> command_buffers_ IPLR_GUARDED_BY(command_buffers_mutex_); Mutex commmand_pools_mutex_; - std::vector> command_pools_ - IPLR_GUARDED_BY(commmand_pools_mutex_); + std::vector> command_pools_ IPLR_GUARDED_BY( + commmand_pools_mutex_); }; void noop() {} @@ -401,6 +412,12 @@ void vkDestroyPipelineCache(VkDevice device, mock_device->AddCalledFunction("vkDestroyPipelineCache"); } +void vkDestroySurfaceKHR(VkInstance instance, + VkSurfaceKHR surface, + const VkAllocationCallbacks* pAllocator) { + return; +} + void vkCmdBindPipeline(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline) { @@ -569,6 +586,101 @@ VkResult vkAllocateDescriptorSets( return VK_SUCCESS; } +VkResult vkGetPhysicalDeviceSurfaceFormatsKHR( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + uint32_t* pSurfaceFormatCount, + VkSurfaceFormatKHR* pSurfaceFormats) { + *pSurfaceFormatCount = 1u; + if (pSurfaceFormats != nullptr) { + pSurfaceFormats[0] = + VkSurfaceFormatKHR{.format = VK_FORMAT_R8G8B8A8_UNORM, + .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}; + } + return VK_SUCCESS; +} + +VkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) { + *pSurfaceCapabilities = VkSurfaceCapabilitiesKHR{ + .minImageCount = 3, + .maxImageCount = 6, + .currentExtent = + VkExtent2D{ + .width = static_cast(currentImageSize.width), + .height = static_cast(currentImageSize.height), + }, + .minImageExtent = + VkExtent2D{ + .width = 0, + .height = 0, + }, + .maxImageExtent = + VkExtent2D{ + .width = static_cast(currentImageSize.width), + .height = static_cast(currentImageSize.height), + }, + .maxImageArrayLayers = 1, + .supportedTransforms = + VkSurfaceTransformFlagBitsKHR::VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, + .currentTransform = + VkSurfaceTransformFlagBitsKHR::VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, + .supportedCompositeAlpha = VkCompositeAlphaFlagBitsKHR:: + VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, + .supportedUsageFlags = + VkImageUsageFlagBits::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT}; + return VK_SUCCESS; +} + +VkResult vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + VkSurfaceKHR surface, + VkBool32* pSupported) { + *pSupported = VK_TRUE; + return VK_SUCCESS; +} + +VkResult vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSwapchainKHR* pSwapchain) { + *pSwapchain = reinterpret_cast(new MockSwapchainKHR()); + return VK_SUCCESS; +} + +VkResult vkGetSwapchainImagesKHR(VkDevice device, + VkSwapchainKHR swapchain, + uint32_t* pSwapchainImageCount, + VkImage* pSwapchainImages) { + *pSwapchainImageCount = 3; + if (pSwapchainImages != nullptr) { + pSwapchainImages[0] = reinterpret_cast(new MockImage()); + pSwapchainImages[1] = reinterpret_cast(new MockImage()); + pSwapchainImages[2] = reinterpret_cast(new MockImage()); + } + return VK_SUCCESS; +} + +VkResult vkCreateSemaphore(VkDevice device, + const VkSemaphoreCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSemaphore* pSemaphore) { + *pSemaphore = reinterpret_cast(new MockSemaphore()); + return VK_SUCCESS; +} + +VkResult vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, + uint64_t timeout, + VkSemaphore semaphore, + VkFence fence, + uint32_t* pImageIndex) { + *pImageIndex = 0; + return VK_SUCCESS; +} + PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance, const char* pName) { if (strcmp("vkEnumerateInstanceExtensionProperties", pName) == 0) { @@ -679,6 +791,22 @@ PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance, return (PFN_vkVoidFunction)vkResetDescriptorPool; } else if (strcmp("vkAllocateDescriptorSets", pName) == 0) { return (PFN_vkVoidFunction)vkAllocateDescriptorSets; + } else if (strcmp("vkGetPhysicalDeviceSurfaceFormatsKHR", pName) == 0) { + return (PFN_vkVoidFunction)vkGetPhysicalDeviceSurfaceFormatsKHR; + } else if (strcmp("vkGetPhysicalDeviceSurfaceCapabilitiesKHR", pName) == 0) { + return (PFN_vkVoidFunction)vkGetPhysicalDeviceSurfaceCapabilitiesKHR; + } else if (strcmp("vkGetPhysicalDeviceSurfaceSupportKHR", pName) == 0) { + return (PFN_vkVoidFunction)vkGetPhysicalDeviceSurfaceSupportKHR; + } else if (strcmp("vkCreateSwapchainKHR", pName) == 0) { + return (PFN_vkVoidFunction)vkCreateSwapchainKHR; + } else if (strcmp("vkGetSwapchainImagesKHR", pName) == 0) { + return (PFN_vkVoidFunction)vkGetSwapchainImagesKHR; + } else if (strcmp("vkCreateSemaphore", pName) == 0) { + return (PFN_vkVoidFunction)vkCreateSemaphore; + } else if (strcmp("vkDestroySurfaceKHR", pName) == 0) { + return (PFN_vkVoidFunction)vkDestroySurfaceKHR; + } else if (strcmp("vkAcquireNextImageKHR", pName) == 0) { + return (PFN_vkVoidFunction)vkAcquireNextImageKHR; } return noop; } @@ -707,5 +835,9 @@ std::shared_ptr> GetMockVulkanFunctions( return mock_device->GetCalledFunctions(); } +void SetSwapchainImageSize(ISize size) { + currentImageSize = size; +} + } // namespace testing } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/test/mock_vulkan.h b/impeller/renderer/backend/vulkan/test/mock_vulkan.h index 4c68492c5cb00..78edcd4338e2b 100644 --- a/impeller/renderer/backend/vulkan/test/mock_vulkan.h +++ b/impeller/renderer/backend/vulkan/test/mock_vulkan.h @@ -94,6 +94,9 @@ class MockVulkanContextBuilder { std::vector instance_layers_; }; +/// @brief Override the image size returned by all swapchain images. +void SetSwapchainImageSize(ISize size); + } // namespace testing } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/test/swapchain_unittests.cc b/impeller/renderer/backend/vulkan/test/swapchain_unittests.cc new file mode 100644 index 0000000000000..a78d3187b58df --- /dev/null +++ b/impeller/renderer/backend/vulkan/test/swapchain_unittests.cc @@ -0,0 +1,56 @@ +// 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" // IWYU pragma: keep +#include "gtest/gtest.h" +#include "impeller/renderer/backend/vulkan/swapchain_vk.h" +#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h" +#include "vulkan/vulkan_enums.hpp" + +namespace impeller { +namespace testing { + +vk::UniqueSurfaceKHR CreateSurface(const ContextVK& context) { +#if FML_OS_DARWIN + impeller::vk::MetalSurfaceCreateInfoEXT createInfo = {}; + auto [result, surface] = + context.GetInstance().createMetalSurfaceEXTUnique(createInfo); + FML_DCHECK(result == vk::Result::eSuccess); + return std::move(surface); +#else + return {}; +#endif // FML_OS_DARWIN +} + +TEST(SwapchainTest, CanCreateSwapchain) { + auto const context = MockVulkanContextBuilder().Build(); + + auto surface = CreateSurface(*context); + auto swapchain = + SwapchainVK::Create(context, std::move(surface), ISize{1, 1}); + + EXPECT_TRUE(swapchain->IsValid()); +} + +TEST(SwapchainTest, RecreateSwapchainWhenSizeChanges) { + auto const context = MockVulkanContextBuilder().Build(); + + auto surface = CreateSurface(*context); + SetSwapchainImageSize(ISize{1, 1}); + auto swapchain = SwapchainVK::Create(context, std::move(surface), ISize{1, 1}, + /*enable_msaa=*/false); + auto image = swapchain->AcquireNextDrawable(); + auto expected_size = ISize{1, 1}; + EXPECT_EQ(image->GetSize(), expected_size); + + SetSwapchainImageSize(ISize{100, 100}); + swapchain->UpdateSurfaceSize(ISize{100, 100}); + + auto image_b = swapchain->AcquireNextDrawable(); + expected_size = ISize{100, 100}; + EXPECT_EQ(image_b->GetSize(), expected_size); +} + +} // namespace testing +} // namespace impeller diff --git a/shell/platform/android/android_surface_vulkan_impeller.cc b/shell/platform/android/android_surface_vulkan_impeller.cc index dff8ab73fde73..2082d7fef8489 100644 --- a/shell/platform/android/android_surface_vulkan_impeller.cc +++ b/shell/platform/android/android_surface_vulkan_impeller.cc @@ -56,6 +56,8 @@ std::unique_ptr AndroidSurfaceVulkanImpeller::CreateGPUSurface( } bool AndroidSurfaceVulkanImpeller::OnScreenSurfaceResize(const SkISize& size) { + surface_context_vk_->UpdateSurfaceSize( + impeller::ISize{size.width(), size.height()}); return true; } @@ -79,8 +81,9 @@ bool AndroidSurfaceVulkanImpeller::SetNativeWindow( FML_LOG(ERROR) << "Could not create a vulkan surface."; return false; } - - return surface_context_vk_->SetWindowSurface(std::move(surface)); + auto size = native_window_->GetSize(); + return surface_context_vk_->SetWindowSurface( + std::move(surface), impeller::ISize{size.width(), size.height()}); } native_window_ = nullptr; diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index 2f0992ebbcf5c..c1d42a16bbaa6 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -111,7 +111,8 @@ bool ImpellerVulkanContextHolder::Initialize(bool enable_validation) { impeller::vk::UniqueSurfaceKHR surface{vk_surface, context->GetInstance()}; surface_context = context->CreateSurfaceContext(); - if (!surface_context->SetWindowSurface(std::move(surface))) { + if (!surface_context->SetWindowSurface(std::move(surface), + impeller::ISize{1, 1})) { VALIDATION_LOG << "Could not set up surface for context."; return false; }