mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
In http://review.skia.org/892736 and http://review.skia.org/893856, Skia moved its Ganesh headers to align with the Graphite ones. This updates Flutter to use those moved files. All changes are mechanical and there is no API difference between the headers (indeed, the old ones simply `#include` the new ones). ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [ ] I listed at least one issue that this PR fixes in the description above. - [x] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [ ] I updated/added relevant documentation (doc comments with `///`). - [x] I signed the [CLA]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
311 lines
9.4 KiB
C++
311 lines
9.4 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 <utility>
|
|
|
|
#include "flutter/flutter_vma/flutter_skia_vma.h"
|
|
#include "flutter/vulkan/vulkan_skia_proc_table.h"
|
|
#include "vulkan_application.h"
|
|
#include "vulkan_device.h"
|
|
#include "vulkan_native_surface.h"
|
|
#include "vulkan_surface.h"
|
|
#include "vulkan_swapchain.h"
|
|
|
|
#include "third_party/skia/include/core/SkSurface.h"
|
|
#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
|
|
#include "third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h"
|
|
#include "third_party/skia/include/gpu/vk/VulkanExtensions.h"
|
|
|
|
namespace vulkan {
|
|
|
|
VulkanWindow::VulkanWindow(fml::RefPtr<VulkanProcTable> proc_table,
|
|
std::unique_ptr<VulkanNativeSurface> native_surface)
|
|
: VulkanWindow(/*context/*/ nullptr,
|
|
std::move(proc_table),
|
|
std::move(native_surface)) {}
|
|
|
|
VulkanWindow::VulkanWindow(const sk_sp<GrDirectContext>& context,
|
|
fml::RefPtr<VulkanProcTable> proc_table,
|
|
std::unique_ptr<VulkanNativeSurface> native_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 && !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;
|
|
}
|
|
|
|
if (!native_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;
|
|
}
|
|
|
|
// Needs to happen before GrDirectContext is created.
|
|
memory_allocator_ = flutter::FlutterSkiaVulkanMemoryAllocator::Make(
|
|
application_->GetAPIVersion(), application_->GetInstance(),
|
|
logical_device_->GetPhysicalDeviceHandle(), logical_device_->GetHandle(),
|
|
vk_, true);
|
|
|
|
// 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() {
|
|
#ifdef SK_VULKAN
|
|
skgpu::VulkanBackendContext backend_context;
|
|
VkPhysicalDeviceFeatures features;
|
|
skgpu::VulkanExtensions extensions;
|
|
|
|
if (!this->CreateSkiaBackendContext(&backend_context, &features,
|
|
&extensions)) {
|
|
return false;
|
|
}
|
|
|
|
GrContextOptions options;
|
|
options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo;
|
|
sk_sp<GrDirectContext> context =
|
|
GrDirectContexts::MakeVulkan(backend_context, options);
|
|
|
|
if (context == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
context->setResourceCacheLimit(kGrCacheMaxByteSize);
|
|
|
|
skia_gr_context_ = context;
|
|
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif // SK_VULKAN
|
|
}
|
|
|
|
bool VulkanWindow::CreateSkiaBackendContext(
|
|
skgpu::VulkanBackendContext* context,
|
|
VkPhysicalDeviceFeatures* features,
|
|
skgpu::VulkanExtensions* extensions) {
|
|
#ifdef SK_VULKAN
|
|
FML_CHECK(context);
|
|
FML_CHECK(features);
|
|
FML_CHECK(extensions);
|
|
auto getProc = CreateSkiaGetProc(vk_);
|
|
|
|
if (getProc == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
if (!logical_device_->GetPhysicalDeviceFeatures(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->fMaxAPIVersion = application_->GetAPIVersion();
|
|
context->fDeviceFeatures = features;
|
|
context->fGetProc = std::move(getProc);
|
|
context->fMemoryAllocator = memory_allocator_;
|
|
|
|
constexpr uint32_t instance_extension_count = 2;
|
|
const char* instance_extensions[instance_extension_count] = {
|
|
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_surface.html
|
|
VK_KHR_SURFACE_EXTENSION_NAME,
|
|
surface_->GetNativeSurface().GetExtensionName(),
|
|
};
|
|
constexpr uint32_t device_extension_count = 1;
|
|
const char* device_extensions[device_extension_count] = {
|
|
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_swapchain.html
|
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
|
};
|
|
extensions->init(context->fGetProc, context->fInstance,
|
|
context->fPhysicalDevice, instance_extension_count,
|
|
instance_extensions, device_extension_count,
|
|
device_extensions);
|
|
context->fVkExtensions = extensions;
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
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
|