flutter_flutter/vulkan/vulkan_window.cc
egdaniel 8c84737ea8 Update GrVkBackendContext to no longer own the VkInstance and VkDevice (#5457)
This changes the ownership of the VkInstance and VkDevice back to the flutter's objects. I am trying to move skia into a world where GrVkBackendContext is purely a description of the vulkan context skia should use and is just passed in during GrContext creation. It shouldn't need to be ref counted or actually do work in destructor (those changes to come later). This is just a first step towards getting there.
2018-06-04 14:46:12 -04:00

256 lines
7.5 KiB
C++

// Copyright 2016 The Chromium 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/vulkan/vulkan_window.h"
#include <memory>
#include <string>
#include "flutter/vulkan/vulkan_application.h"
#include "flutter/vulkan/vulkan_device.h"
#include "flutter/vulkan/vulkan_native_surface.h"
#include "flutter/vulkan/vulkan_surface.h"
#include "flutter/vulkan/vulkan_swapchain.h"
#include "third_party/skia/include/gpu/GrContext.h"
#include "third_party/skia/include/gpu/vk/GrVkInterface.h"
namespace vulkan {
VulkanWindow::VulkanWindow(fxl::RefPtr<VulkanProcTable> proc_table,
std::unique_ptr<VulkanNativeSurface> native_surface)
: valid_(false), vk(std::move(proc_table)) {
if (!vk || !vk->HasAcquiredMandatoryProcAddresses()) {
FXL_DLOG(INFO) << "Proc table has not acquired mandatory proc addresses.";
return;
}
if (native_surface == nullptr || !native_surface->IsValid()) {
FXL_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 setup the
// instance proc table entries.
FXL_DLOG(INFO) << "Instance proc addresses have not been setup.";
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 setup the device proc table
// entries.
FXL_DLOG(INFO) << "Device proc addresses have not been setup.";
return;
}
// Create the logical surface from the native platform surface.
surface_ = std::make_unique<VulkanSurface>(*vk, *application_,
std::move(native_surface));
if (!surface_->IsValid()) {
FXL_DLOG(INFO) << "Vulkan surface is invalid.";
return;
}
// Create the Skia GrContext.
if (!CreateSkiaGrContext()) {
FXL_DLOG(INFO) << "Could not create Skia context.";
return;
}
// Create the swapchain.
if (!RecreateSwapchain()) {
FXL_DLOG(INFO) << "Could not setup the swapchain initially.";
return;
}
valid_ = true;
}
VulkanWindow::~VulkanWindow() = default;
bool VulkanWindow::IsValid() const {
return valid_;
}
GrContext* VulkanWindow::GetSkiaGrContext() {
return skia_gr_context_.get();
}
bool VulkanWindow::CreateSkiaGrContext() {
auto backend_context = CreateSkiaBackendContext();
if (backend_context == nullptr) {
return false;
}
sk_sp<GrContext> context = GrContext::MakeVulkan(backend_context);
if (context == nullptr) {
return false;
}
context->setResourceCacheLimits(kGrCacheMaxCount, kGrCacheMaxByteSize);
skia_vk_backend_context_ = backend_context;
skia_gr_context_ = context;
return true;
}
sk_sp<GrVkBackendContext> VulkanWindow::CreateSkiaBackendContext() {
auto interface = vk->CreateSkiaInterface();
if (interface == nullptr || !interface->validate(0)) {
return nullptr;
}
uint32_t skia_features = 0;
if (!logical_device_->GetPhysicalDeviceFeaturesSkia(&skia_features)) {
return nullptr;
}
auto context = sk_make_sp<GrVkBackendContext>();
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->fInterface.reset(interface.release());
context->fOwnsInstanceAndDevice = false;
return context;
}
sk_sp<SkSurface> VulkanWindow::AcquireSurface() {
if (!IsValid()) {
FXL_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()) {
FXL_DLOG(INFO) << "Swapchain and surface sizes are out of sync. Recreating "
"swapchain.";
if (!RecreateSwapchain()) {
FXL_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.
FXL_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.
FXL_DLOG(INFO) << "Swapchain reported surface was out of date but "
"could not recreate the swapchain at the new "
"configuration.";
valid_ = false;
return nullptr;
}
}
break;
}
FXL_DCHECK(false) << "Unhandled VulkanSwapchain::AcquireResult";
return nullptr;
}
bool VulkanWindow::SwapBuffers() {
if (!IsValid()) {
FXL_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