blob: 875f770637bea24c51cc9c8032f230190b30e053 [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 "impeller/renderer/backend/vulkan/context_vk.h"
#ifdef FML_OS_ANDROID
#include <pthread.h>
#include <sys/resource.h>
#include <sys/time.h>
#endif // FML_OS_ANDROID
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <vector>
#include "flutter/fml/build_config.h"
#include "flutter/fml/trace_event.h"
#include "impeller/base/validation.h"
#include "impeller/renderer/backend/vulkan/allocator_vk.h"
#include "impeller/renderer/backend/vulkan/capabilities_vk.h"
#include "impeller/renderer/backend/vulkan/command_buffer_vk.h"
#include "impeller/renderer/backend/vulkan/command_encoder_vk.h"
#include "impeller/renderer/backend/vulkan/command_pool_vk.h"
#include "impeller/renderer/backend/vulkan/debug_report_vk.h"
#include "impeller/renderer/backend/vulkan/fence_waiter_vk.h"
#include "impeller/renderer/backend/vulkan/resource_manager_vk.h"
#include "impeller/renderer/backend/vulkan/surface_context_vk.h"
#include "impeller/renderer/capabilities.h"
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
namespace impeller {
// TODO(csg): Fix this after caps are reworked.
static bool gHasValidationLayers = false;
bool HasValidationLayers() {
return gHasValidationLayers;
}
static std::optional<vk::PhysicalDevice> PickPhysicalDevice(
const CapabilitiesVK& caps,
const vk::Instance& instance) {
for (const auto& device : instance.enumeratePhysicalDevices().value) {
if (caps.GetEnabledDeviceFeatures(device).has_value()) {
return device;
}
}
return std::nullopt;
}
static std::vector<vk::DeviceQueueCreateInfo> GetQueueCreateInfos(
std::initializer_list<QueueIndexVK> queues) {
std::map<size_t /* family */, size_t /* index */> family_index_map;
for (const auto& queue : queues) {
family_index_map[queue.family] = 0;
}
for (const auto& queue : queues) {
auto value = family_index_map[queue.family];
family_index_map[queue.family] = std::max(value, queue.index);
}
static float kQueuePriority = 1.0f;
std::vector<vk::DeviceQueueCreateInfo> infos;
for (const auto& item : family_index_map) {
vk::DeviceQueueCreateInfo info;
info.setQueueFamilyIndex(item.first);
info.setQueueCount(item.second + 1);
info.setQueuePriorities(kQueuePriority);
infos.push_back(info);
}
return infos;
}
static std::optional<QueueIndexVK> PickQueue(const vk::PhysicalDevice& device,
vk::QueueFlagBits flags) {
// This can be modified to ensure that dedicated queues are returned for each
// queue type depending on support.
const auto families = device.getQueueFamilyProperties();
for (size_t i = 0u; i < families.size(); i++) {
if (!(families[i].queueFlags & flags)) {
continue;
}
return QueueIndexVK{.family = i, .index = 0};
}
return std::nullopt;
}
std::shared_ptr<ContextVK> ContextVK::Create(Settings settings) {
auto context = std::shared_ptr<ContextVK>(new ContextVK());
context->Setup(std::move(settings));
if (!context->IsValid()) {
return nullptr;
}
return context;
}
namespace {
thread_local uint64_t tls_context_count = 0;
uint64_t CalculateHash(void* ptr) {
// You could make a context once per nanosecond for 584 years on one thread
// before this overflows.
return ++tls_context_count;
}
} // namespace
ContextVK::ContextVK() : hash_(CalculateHash(this)) {}
ContextVK::~ContextVK() {
if (device_holder_ && device_holder_->device) {
[[maybe_unused]] auto result = device_holder_->device->waitIdle();
}
CommandPoolVK::ClearAllPools(this);
}
Context::BackendType ContextVK::GetBackendType() const {
return Context::BackendType::kVulkan;
}
void ContextVK::Setup(Settings settings) {
TRACE_EVENT0("impeller", "ContextVK::Setup");
if (!settings.proc_address_callback) {
return;
}
raster_message_loop_ = fml::ConcurrentMessageLoop::Create(
std::min(4u, std::thread::hardware_concurrency()));
#ifdef FML_OS_ANDROID
raster_message_loop_->PostTaskToAllWorkers([]() {
if (::setpriority(PRIO_PROCESS, gettid(), -5) != 0) {
FML_LOG(ERROR) << "Failed to set Workers task runner priority";
}
});
#endif // FML_OS_ANDROID
auto& dispatcher = VULKAN_HPP_DEFAULT_DISPATCHER;
dispatcher.init(settings.proc_address_callback);
// Enable Vulkan validation if either:
// 1. The user has explicitly enabled it.
// 2. We are in a combination of debug mode, and running on Android.
// (It's possible 2 is overly conservative and we can simplify this)
auto enable_validation = settings.enable_validation;
#if defined(FML_OS_ANDROID) && !defined(NDEBUG)
enable_validation = true;
#endif
auto caps =
std::shared_ptr<CapabilitiesVK>(new CapabilitiesVK(enable_validation));
if (!caps->IsValid()) {
VALIDATION_LOG << "Could not determine device capabilities.";
return;
}
gHasValidationLayers = caps->AreValidationsEnabled();
auto enabled_layers = caps->GetEnabledLayers();
auto enabled_extensions = caps->GetEnabledInstanceExtensions();
if (!enabled_layers.has_value() || !enabled_extensions.has_value()) {
VALIDATION_LOG << "Device has insufficient capabilities.";
return;
}
vk::InstanceCreateFlags instance_flags = {};
if (std::find(enabled_extensions.value().begin(),
enabled_extensions.value().end(),
"VK_KHR_portability_enumeration") !=
enabled_extensions.value().end()) {
instance_flags |= vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR;
}
std::vector<const char*> enabled_layers_c;
std::vector<const char*> enabled_extensions_c;
for (const auto& layer : enabled_layers.value()) {
enabled_layers_c.push_back(layer.c_str());
}
for (const auto& ext : enabled_extensions.value()) {
enabled_extensions_c.push_back(ext.c_str());
}
vk::ApplicationInfo application_info;
application_info.setApplicationVersion(VK_API_VERSION_1_0);
application_info.setApiVersion(VK_API_VERSION_1_1);
application_info.setEngineVersion(VK_API_VERSION_1_0);
application_info.setPEngineName("Impeller");
application_info.setPApplicationName("Impeller");
vk::StructureChain<vk::InstanceCreateInfo, vk::ValidationFeaturesEXT>
instance_chain;
if (!caps->AreValidationsEnabled()) {
instance_chain.unlink<vk::ValidationFeaturesEXT>();
}
std::vector<vk::ValidationFeatureEnableEXT> enabled_validations = {
vk::ValidationFeatureEnableEXT::eSynchronizationValidation,
};
auto validation = instance_chain.get<vk::ValidationFeaturesEXT>();
validation.setEnabledValidationFeatures(enabled_validations);
auto instance_info = instance_chain.get<vk::InstanceCreateInfo>();
instance_info.setPEnabledLayerNames(enabled_layers_c);
instance_info.setPEnabledExtensionNames(enabled_extensions_c);
instance_info.setPApplicationInfo(&application_info);
instance_info.setFlags(instance_flags);
auto device_holder = std::make_shared<DeviceHolderImpl>();
{
auto instance = vk::createInstanceUnique(instance_info);
if (instance.result != vk::Result::eSuccess) {
VALIDATION_LOG << "Could not create Vulkan instance: "
<< vk::to_string(instance.result);
return;
}
device_holder->instance = std::move(instance.value);
}
dispatcher.init(device_holder->instance.get());
//----------------------------------------------------------------------------
/// Setup the debug report.
///
/// Do this as early as possible since we could use the debug report from
/// initialization issues.
///
auto debug_report =
std::make_unique<DebugReportVK>(*caps, device_holder->instance.get());
if (!debug_report->IsValid()) {
VALIDATION_LOG << "Could not set up debug report.";
return;
}
//----------------------------------------------------------------------------
/// Pick the physical device.
///
{
auto physical_device =
PickPhysicalDevice(*caps, device_holder->instance.get());
if (!physical_device.has_value()) {
VALIDATION_LOG << "No valid Vulkan device found.";
return;
}
device_holder->physical_device = physical_device.value();
}
//----------------------------------------------------------------------------
/// Pick device queues.
///
auto graphics_queue =
PickQueue(device_holder->physical_device, vk::QueueFlagBits::eGraphics);
auto transfer_queue =
PickQueue(device_holder->physical_device, vk::QueueFlagBits::eTransfer);
auto compute_queue =
PickQueue(device_holder->physical_device, vk::QueueFlagBits::eCompute);
if (!graphics_queue.has_value()) {
VALIDATION_LOG << "Could not pick graphics queue.";
return;
}
if (!transfer_queue.has_value()) {
FML_LOG(INFO) << "Dedicated transfer queue not avialable.";
transfer_queue = graphics_queue.value();
}
if (!compute_queue.has_value()) {
VALIDATION_LOG << "Could not pick compute queue.";
return;
}
//----------------------------------------------------------------------------
/// Create the logical device.
///
auto enabled_device_extensions =
caps->GetEnabledDeviceExtensions(device_holder->physical_device);
if (!enabled_device_extensions.has_value()) {
// This shouldn't happen since we already did device selection. But
// doesn't hurt to check again.
return;
}
std::vector<const char*> enabled_device_extensions_c;
for (const auto& ext : enabled_device_extensions.value()) {
enabled_device_extensions_c.push_back(ext.c_str());
}
const auto queue_create_infos = GetQueueCreateInfos(
{graphics_queue.value(), compute_queue.value(), transfer_queue.value()});
const auto enabled_features =
caps->GetEnabledDeviceFeatures(device_holder->physical_device);
if (!enabled_features.has_value()) {
// This shouldn't happen since the device can't be picked if this was not
// true. But doesn't hurt to check.
return;
}
vk::DeviceCreateInfo device_info;
device_info.setQueueCreateInfos(queue_create_infos);
device_info.setPEnabledExtensionNames(enabled_device_extensions_c);
device_info.setPEnabledFeatures(&enabled_features.value());
// Device layers are deprecated and ignored.
{
auto device_result =
device_holder->physical_device.createDeviceUnique(device_info);
if (device_result.result != vk::Result::eSuccess) {
VALIDATION_LOG << "Could not create logical device.";
return;
}
device_holder->device = std::move(device_result.value);
}
if (!caps->SetPhysicalDevice(device_holder->physical_device)) {
VALIDATION_LOG << "Capabilities could not be updated.";
return;
}
//----------------------------------------------------------------------------
/// Create the allocator.
///
auto allocator = std::shared_ptr<AllocatorVK>(new AllocatorVK(
weak_from_this(), //
application_info.apiVersion, //
device_holder->physical_device, //
device_holder, //
device_holder->instance.get(), //
dispatcher.vkGetInstanceProcAddr, //
dispatcher.vkGetDeviceProcAddr, //
*caps //
));
if (!allocator->IsValid()) {
VALIDATION_LOG << "Could not create memory allocator.";
return;
}
//----------------------------------------------------------------------------
/// Setup the pipeline library.
///
auto pipeline_library = std::shared_ptr<PipelineLibraryVK>(
new PipelineLibraryVK(device_holder, //
caps, //
std::move(settings.cache_directory), //
raster_message_loop_->GetTaskRunner() //
));
if (!pipeline_library->IsValid()) {
VALIDATION_LOG << "Could not create pipeline library.";
return;
}
auto sampler_library =
std::shared_ptr<SamplerLibraryVK>(new SamplerLibraryVK(device_holder));
auto shader_library = std::shared_ptr<ShaderLibraryVK>(
new ShaderLibraryVK(device_holder, //
settings.shader_libraries_data) //
);
if (!shader_library->IsValid()) {
VALIDATION_LOG << "Could not create shader library.";
return;
}
//----------------------------------------------------------------------------
/// Create the fence waiter.
///
auto fence_waiter =
std::shared_ptr<FenceWaiterVK>(new FenceWaiterVK(device_holder));
if (!fence_waiter->IsValid()) {
VALIDATION_LOG << "Could not create fence waiter.";
return;
}
//----------------------------------------------------------------------------
/// Create the resource manager.
///
auto resource_manager = ResourceManagerVK::Create();
if (!resource_manager) {
VALIDATION_LOG << "Could not create resource manager.";
return;
}
//----------------------------------------------------------------------------
/// Fetch the queues.
///
QueuesVK queues(device_holder->device.get(), //
graphics_queue.value(), //
compute_queue.value(), //
transfer_queue.value() //
);
if (!queues.IsValid()) {
VALIDATION_LOG << "Could not fetch device queues.";
return;
}
VkPhysicalDeviceProperties physical_device_properties;
dispatcher.vkGetPhysicalDeviceProperties(device_holder->physical_device,
&physical_device_properties);
//----------------------------------------------------------------------------
/// All done!
///
device_holder_ = std::move(device_holder);
debug_report_ = std::move(debug_report);
allocator_ = std::move(allocator);
shader_library_ = std::move(shader_library);
sampler_library_ = std::move(sampler_library);
pipeline_library_ = std::move(pipeline_library);
queues_ = std::move(queues);
device_capabilities_ = std::move(caps);
fence_waiter_ = std::move(fence_waiter);
resource_manager_ = std::move(resource_manager);
device_name_ = std::string(physical_device_properties.deviceName);
is_valid_ = true;
//----------------------------------------------------------------------------
/// Label all the relevant objects. This happens after setup so that the
/// debug messengers have had a chance to be set up.
///
SetDebugName(GetDevice(), device_holder_->device.get(), "ImpellerDevice");
}
void ContextVK::SetOffscreenFormat(PixelFormat pixel_format) {
CapabilitiesVK::Cast(*device_capabilities_).SetOffscreenFormat(pixel_format);
}
// |Context|
std::string ContextVK::DescribeGpuModel() const {
return device_name_;
}
bool ContextVK::IsValid() const {
return is_valid_;
}
std::shared_ptr<Allocator> ContextVK::GetResourceAllocator() const {
return allocator_;
}
std::shared_ptr<ShaderLibrary> ContextVK::GetShaderLibrary() const {
return shader_library_;
}
std::shared_ptr<SamplerLibrary> ContextVK::GetSamplerLibrary() const {
return sampler_library_;
}
std::shared_ptr<PipelineLibrary> ContextVK::GetPipelineLibrary() const {
return pipeline_library_;
}
std::shared_ptr<CommandBuffer> ContextVK::CreateCommandBuffer() const {
return std::shared_ptr<CommandBufferVK>(
new CommandBufferVK(shared_from_this(), //
CreateGraphicsCommandEncoderFactory()) //
);
}
vk::Instance ContextVK::GetInstance() const {
return *device_holder_->instance;
}
const vk::Device& ContextVK::GetDevice() const {
return device_holder_->device.get();
}
const std::shared_ptr<fml::ConcurrentTaskRunner>
ContextVK::GetConcurrentWorkerTaskRunner() const {
return raster_message_loop_->GetTaskRunner();
}
void ContextVK::Shutdown() {
raster_message_loop_->Terminate();
}
std::shared_ptr<SurfaceContextVK> ContextVK::CreateSurfaceContext() {
return std::make_shared<SurfaceContextVK>(shared_from_this());
}
const std::shared_ptr<const Capabilities>& ContextVK::GetCapabilities() const {
return device_capabilities_;
}
const std::shared_ptr<QueueVK>& ContextVK::GetGraphicsQueue() const {
return queues_.graphics_queue;
}
vk::PhysicalDevice ContextVK::GetPhysicalDevice() const {
return device_holder_->physical_device;
}
std::shared_ptr<FenceWaiterVK> ContextVK::GetFenceWaiter() const {
return fence_waiter_;
}
std::shared_ptr<ResourceManagerVK> ContextVK::GetResourceManager() const {
return resource_manager_;
}
std::unique_ptr<CommandEncoderFactoryVK>
ContextVK::CreateGraphicsCommandEncoderFactory() const {
return std::make_unique<CommandEncoderFactoryVK>(weak_from_this());
}
} // namespace impeller