blob: ea36581add8e27a70dd4ac01da042cdec8858a75 [file] [log] [blame]
// 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 <cassert>
#include <memory>
#include <optional>
#include "flutter/flutter_vma/flutter_skia_vma.h"
#include "flutter/fml/logging.h"
#include "flutter/shell/common/context_options.h"
#include "flutter/testing/test_vulkan_context.h"
#include "flutter/vulkan/vulkan_skia_proc_table.h"
#include "flutter/fml/memory/ref_ptr.h"
#include "flutter/fml/native_library.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "third_party/skia/include/gpu/vk/GrVkExtensions.h"
#include "vulkan/vulkan_core.h"
#ifdef FML_OS_MACOSX
#define VULKAN_SO_PATH "libvk_swiftshader.dylib"
#elif FML_OS_WIN
#define VULKAN_SO_PATH "vk_swiftshader.dll"
#else
#define VULKAN_SO_PATH "libvk_swiftshader.so"
#endif
namespace flutter {
namespace testing {
TestVulkanContext::TestVulkanContext() {
// ---------------------------------------------------------------------------
// Initialize basic Vulkan state using the Swiftshader ICD.
// ---------------------------------------------------------------------------
const char* vulkan_icd = VULKAN_SO_PATH;
// TODO(96949): Clean this up and pass a native library directly to
// VulkanProcTable.
if (!fml::NativeLibrary::Create(VULKAN_SO_PATH)) {
FML_LOG(ERROR) << "Couldn't find Vulkan ICD \"" << vulkan_icd
<< "\", trying \"libvulkan.so\" instead.";
vulkan_icd = "libvulkan.so";
}
FML_LOG(INFO) << "Using Vulkan ICD: " << vulkan_icd;
vk_ = fml::MakeRefCounted<vulkan::VulkanProcTable>(vulkan_icd);
if (!vk_ || !vk_->HasAcquiredMandatoryProcAddresses()) {
FML_LOG(ERROR) << "Proc table has not acquired mandatory proc addresses.";
return;
}
application_ = std::make_unique<vulkan::VulkanApplication>(
*vk_, "Flutter Unittests", std::vector<std::string>{},
VK_MAKE_VERSION(1, 0, 0), VK_MAKE_VERSION(1, 0, 0), true);
if (!application_->IsValid()) {
FML_LOG(ERROR) << "Failed to initialize basic Vulkan state.";
return;
}
if (!vk_->AreInstanceProcsSetup()) {
FML_LOG(ERROR) << "Failed to acquire full proc table.";
return;
}
device_ = application_->AcquireFirstCompatibleLogicalDevice();
if (!device_ || !device_->IsValid()) {
FML_LOG(ERROR) << "Failed to create compatible logical device.";
return;
}
// ---------------------------------------------------------------------------
// Create a Skia context.
// For creating SkSurfaces from VkImages and snapshotting them, etc.
// ---------------------------------------------------------------------------
uint32_t skia_features = 0;
if (!device_->GetPhysicalDeviceFeaturesSkia(&skia_features)) {
FML_LOG(ERROR) << "Failed to get physical device features.";
return;
}
auto get_proc = vulkan::CreateSkiaGetProc(vk_);
if (get_proc == nullptr) {
FML_LOG(ERROR) << "Failed to create Vulkan getProc for Skia.";
return;
}
sk_sp<skgpu::VulkanMemoryAllocator> allocator =
flutter::FlutterSkiaVulkanMemoryAllocator::Make(
VK_MAKE_VERSION(1, 0, 0), application_->GetInstance(),
device_->GetPhysicalDeviceHandle(), device_->GetHandle(), vk_, true);
GrVkExtensions extensions;
GrVkBackendContext backend_context = {};
backend_context.fInstance = application_->GetInstance();
backend_context.fPhysicalDevice = device_->GetPhysicalDeviceHandle();
backend_context.fDevice = device_->GetHandle();
backend_context.fQueue = device_->GetQueueHandle();
backend_context.fGraphicsQueueIndex = device_->GetGraphicsQueueIndex();
backend_context.fMinAPIVersion = VK_MAKE_VERSION(1, 0, 0);
backend_context.fMaxAPIVersion = VK_MAKE_VERSION(1, 0, 0);
backend_context.fFeatures = skia_features;
backend_context.fVkExtensions = &extensions;
backend_context.fGetProc = get_proc;
backend_context.fOwnsInstanceAndDevice = false;
backend_context.fMemoryAllocator = allocator;
GrContextOptions options =
MakeDefaultContextOptions(ContextType::kRender, GrBackendApi::kVulkan);
options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo;
context_ = GrDirectContext::MakeVulkan(backend_context, options);
}
TestVulkanContext::~TestVulkanContext() {
if (context_) {
context_->releaseResourcesAndAbandonContext();
}
}
std::optional<TestVulkanImage> TestVulkanContext::CreateImage(
const SkISize& size) const {
TestVulkanImage result;
VkImageCreateInfo info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.imageType = VK_IMAGE_TYPE_2D,
.format = VK_FORMAT_R8G8B8A8_UNORM,
.extent = VkExtent3D{static_cast<uint32_t>(size.width()),
static_cast<uint32_t>(size.height()), 1},
.mipLevels = 1,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
};
VkImage image;
if (VK_CALL_LOG_ERROR(VK_CALL_LOG_ERROR(
vk_->CreateImage(device_->GetHandle(), &info, nullptr, &image)))) {
return std::nullopt;
}
result.image_ = vulkan::VulkanHandle<VkImage>(
image, [&vk = vk_, &device = device_](VkImage image) {
vk->DestroyImage(device->GetHandle(), image, nullptr);
});
VkMemoryRequirements mem_req;
vk_->GetImageMemoryRequirements(device_->GetHandle(), image, &mem_req);
VkMemoryAllocateInfo alloc_info{};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.allocationSize = mem_req.size;
alloc_info.memoryTypeIndex = static_cast<uint32_t>(__builtin_ctz(
mem_req.memoryTypeBits & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
VkDeviceMemory memory;
if (VK_CALL_LOG_ERROR(vk_->AllocateMemory(device_->GetHandle(), &alloc_info,
nullptr, &memory)) != VK_SUCCESS) {
return std::nullopt;
}
result.memory_ = vulkan::VulkanHandle<VkDeviceMemory>{
memory, [&vk = vk_, &device = device_](VkDeviceMemory memory) {
vk->FreeMemory(device->GetHandle(), memory, nullptr);
}};
if (VK_CALL_LOG_ERROR(VK_CALL_LOG_ERROR(vk_->BindImageMemory(
device_->GetHandle(), result.image_, result.memory_, 0)))) {
return std::nullopt;
}
result.context_ =
fml::RefPtr<TestVulkanContext>(const_cast<TestVulkanContext*>(this));
return result;
}
sk_sp<GrDirectContext> TestVulkanContext::GetGrDirectContext() const {
return context_;
}
} // namespace testing
} // namespace flutter