mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This option will be the default in Skia soon. Per discussion in #26067, let’s be explicit about disabling it for the time being, and we can revisit the flag in the future if desirable.
273 lines
8.1 KiB
C++
273 lines
8.1 KiB
C++
// 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.
|
|
|
|
// FLUTTER_NOLINT: https://github.com/flutter/flutter/issues/68331
|
|
|
|
#include "vulkan_window.h"
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include "third_party/skia/include/gpu/GrDirectContext.h"
|
|
#include "vulkan_application.h"
|
|
#include "vulkan_device.h"
|
|
#include "vulkan_native_surface.h"
|
|
#include "vulkan_surface.h"
|
|
#include "vulkan_swapchain.h"
|
|
|
|
namespace vulkan {
|
|
|
|
VulkanWindow::VulkanWindow(fml::RefPtr<VulkanProcTable> proc_table,
|
|
std::unique_ptr<VulkanNativeSurface> native_surface,
|
|
bool render_to_surface)
|
|
: VulkanWindow(/*context/*/ nullptr,
|
|
proc_table,
|
|
std::move(native_surface),
|
|
render_to_surface) {}
|
|
|
|
VulkanWindow::VulkanWindow(const sk_sp<GrDirectContext>& context,
|
|
fml::RefPtr<VulkanProcTable> proc_table,
|
|
std::unique_ptr<VulkanNativeSurface> native_surface,
|
|
bool render_to_surface)
|
|
: valid_(false), vk(std::move(proc_table)), skia_gr_context_(context) {
|
|
if (!vk || !vk->HasAcquiredMandatoryProcAddresses()) {
|
|
FML_DLOG(INFO) << "Proc table has not acquired mandatory proc addresses.";
|
|
return;
|
|
}
|
|
|
|
if (native_surface == nullptr || !native_surface->IsValid()) {
|
|
FML_DLOG(INFO) << "Native surface is invalid.";
|
|
return;
|
|
}
|
|
|
|
// Create the application instance.
|
|
|
|
std::vector<std::string> extensions = {
|
|
VK_KHR_SURFACE_EXTENSION_NAME, // parent extension
|
|
native_surface->GetExtensionName() // child extension
|
|
};
|
|
|
|
application_ = std::make_unique<VulkanApplication>(*vk, "Flutter",
|
|
std::move(extensions));
|
|
|
|
if (!application_->IsValid() || !vk->AreInstanceProcsSetup()) {
|
|
// Make certain the application instance was created and it set up the
|
|
// instance proc table entries.
|
|
FML_DLOG(INFO) << "Instance proc addresses have not been set up.";
|
|
return;
|
|
}
|
|
|
|
// Create the device.
|
|
|
|
logical_device_ = application_->AcquireFirstCompatibleLogicalDevice();
|
|
|
|
if (logical_device_ == nullptr || !logical_device_->IsValid() ||
|
|
!vk->AreDeviceProcsSetup()) {
|
|
// Make certain the device was created and it set up the device proc table
|
|
// entries.
|
|
FML_DLOG(INFO) << "Device proc addresses have not been set up.";
|
|
return;
|
|
}
|
|
|
|
// TODO(38466): Refactor GPU surface APIs take into account the fact that an
|
|
// external view embedder may want to render to the root surface.
|
|
if (!render_to_surface) {
|
|
return;
|
|
}
|
|
|
|
// Create the logical surface from the native platform surface.
|
|
surface_ = std::make_unique<VulkanSurface>(*vk, *application_,
|
|
std::move(native_surface));
|
|
|
|
if (!surface_->IsValid()) {
|
|
FML_DLOG(INFO) << "Vulkan surface is invalid.";
|
|
return;
|
|
}
|
|
|
|
// Create the Skia GrDirectContext.
|
|
|
|
if (!skia_gr_context_ && !CreateSkiaGrContext()) {
|
|
FML_DLOG(INFO) << "Could not create Skia context.";
|
|
return;
|
|
}
|
|
|
|
// Create the swapchain.
|
|
|
|
if (!RecreateSwapchain()) {
|
|
FML_DLOG(INFO) << "Could not set up the swapchain initially.";
|
|
return;
|
|
}
|
|
|
|
valid_ = true;
|
|
}
|
|
|
|
VulkanWindow::~VulkanWindow() = default;
|
|
|
|
bool VulkanWindow::IsValid() const {
|
|
return valid_;
|
|
}
|
|
|
|
GrDirectContext* VulkanWindow::GetSkiaGrContext() {
|
|
return skia_gr_context_.get();
|
|
}
|
|
|
|
bool VulkanWindow::CreateSkiaGrContext() {
|
|
GrVkBackendContext backend_context;
|
|
|
|
if (!CreateSkiaBackendContext(&backend_context)) {
|
|
return false;
|
|
}
|
|
|
|
GrContextOptions options;
|
|
options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo;
|
|
sk_sp<GrDirectContext> context =
|
|
GrDirectContext::MakeVulkan(backend_context, options);
|
|
|
|
if (context == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
context->setResourceCacheLimits(kGrCacheMaxCount, kGrCacheMaxByteSize);
|
|
|
|
skia_gr_context_ = context;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VulkanWindow::CreateSkiaBackendContext(GrVkBackendContext* context) {
|
|
auto getProc = vk->CreateSkiaGetProc();
|
|
|
|
if (getProc == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t skia_features = 0;
|
|
if (!logical_device_->GetPhysicalDeviceFeaturesSkia(&skia_features)) {
|
|
return false;
|
|
}
|
|
|
|
context->fInstance = application_->GetInstance();
|
|
context->fPhysicalDevice = logical_device_->GetPhysicalDeviceHandle();
|
|
context->fDevice = logical_device_->GetHandle();
|
|
context->fQueue = logical_device_->GetQueueHandle();
|
|
context->fGraphicsQueueIndex = logical_device_->GetGraphicsQueueIndex();
|
|
context->fMinAPIVersion = application_->GetAPIVersion();
|
|
context->fExtensions = kKHR_surface_GrVkExtensionFlag |
|
|
kKHR_swapchain_GrVkExtensionFlag |
|
|
surface_->GetNativeSurface().GetSkiaExtensionName();
|
|
context->fFeatures = skia_features;
|
|
context->fGetProc = std::move(getProc);
|
|
context->fOwnsInstanceAndDevice = false;
|
|
return true;
|
|
}
|
|
|
|
sk_sp<SkSurface> VulkanWindow::AcquireSurface() {
|
|
if (!IsValid()) {
|
|
FML_DLOG(INFO) << "Surface is invalid.";
|
|
return nullptr;
|
|
}
|
|
|
|
auto surface_size = surface_->GetSize();
|
|
|
|
// This check is theoretically unnecessary as the swapchain should report that
|
|
// the surface is out-of-date and perform swapchain recreation at the new
|
|
// configuration. However, on Android, the swapchain never reports that it is
|
|
// of date. Hence this extra check. Platforms that don't have this issue, or,
|
|
// cant report this information (which is optional anyway), report a zero
|
|
// size.
|
|
if (surface_size != SkISize::Make(0, 0) &&
|
|
surface_size != swapchain_->GetSize()) {
|
|
FML_DLOG(INFO) << "Swapchain and surface sizes are out of sync. Recreating "
|
|
"swapchain.";
|
|
if (!RecreateSwapchain()) {
|
|
FML_DLOG(INFO) << "Could not recreate swapchain.";
|
|
valid_ = false;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
while (true) {
|
|
sk_sp<SkSurface> surface;
|
|
auto acquire_result = VulkanSwapchain::AcquireStatus::ErrorSurfaceLost;
|
|
|
|
std::tie(acquire_result, surface) = swapchain_->AcquireSurface();
|
|
|
|
if (acquire_result == VulkanSwapchain::AcquireStatus::Success) {
|
|
// Successfully acquired a surface from the swapchain. Nothing more to do.
|
|
return surface;
|
|
}
|
|
|
|
if (acquire_result == VulkanSwapchain::AcquireStatus::ErrorSurfaceLost) {
|
|
// Surface is lost. This is an unrecoverable error.
|
|
FML_DLOG(INFO) << "Swapchain reported surface was lost.";
|
|
return nullptr;
|
|
}
|
|
|
|
if (acquire_result ==
|
|
VulkanSwapchain::AcquireStatus::ErrorSurfaceOutOfDate) {
|
|
// Surface out of date. Recreate the swapchain at the new configuration.
|
|
if (RecreateSwapchain()) {
|
|
// Swapchain was recreated, try surface acquisition again.
|
|
continue;
|
|
} else {
|
|
// Could not recreate the swapchain at the new configuration.
|
|
FML_DLOG(INFO) << "Swapchain reported surface was out of date but "
|
|
"could not recreate the swapchain at the new "
|
|
"configuration.";
|
|
valid_ = false;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
FML_DCHECK(false) << "Unhandled VulkanSwapchain::AcquireResult";
|
|
return nullptr;
|
|
}
|
|
|
|
bool VulkanWindow::SwapBuffers() {
|
|
if (!IsValid()) {
|
|
FML_DLOG(INFO) << "Window was invalid.";
|
|
return false;
|
|
}
|
|
|
|
return swapchain_->Submit();
|
|
}
|
|
|
|
bool VulkanWindow::RecreateSwapchain() {
|
|
// This way, we always lose our reference to the old swapchain. Even if we
|
|
// cannot create a new one to replace it.
|
|
auto old_swapchain = std::move(swapchain_);
|
|
|
|
if (!vk->IsValid()) {
|
|
return false;
|
|
}
|
|
|
|
if (logical_device_ == nullptr || !logical_device_->IsValid()) {
|
|
return false;
|
|
}
|
|
|
|
if (surface_ == nullptr || !surface_->IsValid()) {
|
|
return false;
|
|
}
|
|
|
|
if (skia_gr_context_ == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
auto swapchain = std::make_unique<VulkanSwapchain>(
|
|
*vk, *logical_device_, *surface_, skia_gr_context_.get(),
|
|
std::move(old_swapchain), logical_device_->GetGraphicsQueueIndex());
|
|
|
|
if (!swapchain->IsValid()) {
|
|
return false;
|
|
}
|
|
|
|
swapchain_ = std::move(swapchain);
|
|
return true;
|
|
}
|
|
|
|
} // namespace vulkan
|