mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00:00
LibWeb+LibCore: Use Vulkan backend for Skia on Linux
Skia now uses GPU-accelerated painting on Linux if Vulkan is available. Most of the performance gain is currently negated by reading the GPU backend back into RAM to pass it to the Browser process. In the future, this could be improved by sharing GPU-allocated memory across the Browser and WebContent processes.
This commit is contained in:
parent
70db2bff92
commit
e713de115c
Notes:
sideshowbarker
2024-07-17 04:09:56 +09:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/LadybirdBrowser/ladybird/commit/e713de115c Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/428
10 changed files with 297 additions and 2 deletions
7
Meta/CMake/vulkan.cmake
Normal file
7
Meta/CMake/vulkan.cmake
Normal file
|
@ -0,0 +1,7 @@
|
|||
if (NOT APPLE)
|
||||
find_package(Vulkan QUIET)
|
||||
if (Vulkan_FOUND)
|
||||
set(HAS_VULKAN ON CACHE BOOL "" FORCE)
|
||||
add_compile_definitions(USE_VULKAN=1)
|
||||
endif()
|
||||
endif()
|
|
@ -1,3 +1,5 @@
|
|||
include(vulkan)
|
||||
|
||||
# These are the minimal set of sources needed to build the code generators. We separate them to allow
|
||||
# LibCore to depend on generated sources.
|
||||
set(SOURCES
|
||||
|
@ -79,6 +81,11 @@ else()
|
|||
)
|
||||
endif()
|
||||
|
||||
if (HAS_VULKAN)
|
||||
include_directories(${Vulkan_INCLUDE_DIR})
|
||||
list(APPEND SOURCES VulkanContext.cpp)
|
||||
endif()
|
||||
|
||||
if (APPLE OR CMAKE_SYSTEM_NAME STREQUAL "GNU")
|
||||
list(APPEND SOURCES MachPort.cpp)
|
||||
endif()
|
||||
|
@ -103,3 +110,7 @@ endif()
|
|||
if (ANDROID)
|
||||
target_link_libraries(LibCore PRIVATE log)
|
||||
endif()
|
||||
|
||||
if (HAS_VULKAN)
|
||||
target_link_libraries(LibCore PUBLIC ${Vulkan_LIBRARIES})
|
||||
endif()
|
||||
|
|
125
Userland/Libraries/LibCore/VulkanContext.cpp
Normal file
125
Userland/Libraries/LibCore/VulkanContext.cpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/VulkanContext.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
ErrorOr<VkInstance> create_instance(uint32_t api_version)
|
||||
{
|
||||
VkInstance instance;
|
||||
|
||||
VkApplicationInfo app_info {};
|
||||
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
app_info.pApplicationName = "Ladybird";
|
||||
app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||
app_info.pEngineName = nullptr;
|
||||
app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||
app_info.apiVersion = api_version;
|
||||
|
||||
VkInstanceCreateInfo create_info {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
create_info.pApplicationInfo = &app_info;
|
||||
|
||||
if (vkCreateInstance(&create_info, nullptr, &instance) != VK_SUCCESS) {
|
||||
return Error::from_string_view("Application instance creation failed"sv);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
ErrorOr<VkPhysicalDevice> pick_physical_device(VkInstance instance)
|
||||
{
|
||||
uint32_t device_count = 0;
|
||||
vkEnumeratePhysicalDevices(instance, &device_count, nullptr);
|
||||
|
||||
if (device_count == 0)
|
||||
return Error::from_string_view("Can't find any physical devices available"sv);
|
||||
|
||||
Vector<VkPhysicalDevice> devices;
|
||||
devices.resize(device_count);
|
||||
vkEnumeratePhysicalDevices(instance, &device_count, devices.data());
|
||||
|
||||
VkPhysicalDevice picked_device = VK_NULL_HANDLE;
|
||||
// Pick discrete GPU or the first device in the list
|
||||
for (auto const& device : devices) {
|
||||
if (picked_device == VK_NULL_HANDLE)
|
||||
picked_device = device;
|
||||
|
||||
VkPhysicalDeviceProperties device_properties;
|
||||
vkGetPhysicalDeviceProperties(device, &device_properties);
|
||||
if (device_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
|
||||
picked_device = device;
|
||||
}
|
||||
|
||||
if (picked_device != VK_NULL_HANDLE)
|
||||
return picked_device;
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<VkDevice> create_logical_device(VkPhysicalDevice physical_device)
|
||||
{
|
||||
VkDevice device;
|
||||
|
||||
uint32_t queue_family_count = 0;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, nullptr);
|
||||
Vector<VkQueueFamilyProperties> queue_families;
|
||||
queue_families.resize(queue_family_count);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_families.data());
|
||||
|
||||
int graphics_queue_family_index = -1;
|
||||
for (int i = 0; i < static_cast<int>(queue_families.size()); i++) {
|
||||
if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||||
graphics_queue_family_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VkDeviceQueueCreateInfo queue_create_info {};
|
||||
queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||
queue_create_info.queueFamilyIndex = graphics_queue_family_index;
|
||||
queue_create_info.queueCount = 1;
|
||||
|
||||
float const queue_priority = 1.0f;
|
||||
queue_create_info.pQueuePriorities = &queue_priority;
|
||||
|
||||
VkPhysicalDeviceFeatures deviceFeatures {};
|
||||
|
||||
VkDeviceCreateInfo create_device_info {};
|
||||
create_device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||
create_device_info.pQueueCreateInfos = &queue_create_info;
|
||||
create_device_info.queueCreateInfoCount = 1;
|
||||
create_device_info.pEnabledFeatures = &deviceFeatures;
|
||||
|
||||
if (vkCreateDevice(physical_device, &create_device_info, nullptr, &device) != VK_SUCCESS) {
|
||||
return Error::from_string_view("Logical device creation failed"sv);
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
ErrorOr<VulkanContext> create_vulkan_context()
|
||||
{
|
||||
uint32_t const api_version = VK_API_VERSION_1_0;
|
||||
auto* instance = TRY(create_instance(api_version));
|
||||
auto* physical_device = TRY(pick_physical_device(instance));
|
||||
auto* logical_device = TRY(create_logical_device(physical_device));
|
||||
|
||||
VkQueue graphics_queue;
|
||||
vkGetDeviceQueue(logical_device, 0, 0, &graphics_queue);
|
||||
|
||||
return VulkanContext {
|
||||
.api_version = api_version,
|
||||
.instance = instance,
|
||||
.physical_device = physical_device,
|
||||
.logical_device = logical_device,
|
||||
.graphics_queue = graphics_queue,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
29
Userland/Libraries/LibCore/VulkanContext.h
Normal file
29
Userland/Libraries/LibCore/VulkanContext.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(USE_VULKAN)
|
||||
static_assert(false, "This file must only be used when Vulkan is available");
|
||||
#endif
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Function.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
struct VulkanContext {
|
||||
uint32_t api_version { VK_API_VERSION_1_0 };
|
||||
VkInstance instance { VK_NULL_HANDLE };
|
||||
VkPhysicalDevice physical_device { VK_NULL_HANDLE };
|
||||
VkDevice logical_device { VK_NULL_HANDLE };
|
||||
VkQueue graphics_queue { VK_NULL_HANDLE };
|
||||
};
|
||||
|
||||
ErrorOr<VulkanContext> create_vulkan_context();
|
||||
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
include(libweb_generators)
|
||||
include(vulkan)
|
||||
|
||||
set(SOURCES
|
||||
Animations/Animatable.cpp
|
||||
|
|
|
@ -35,6 +35,19 @@ TraversableNavigable::TraversableNavigable(JS::NonnullGCPtr<Page> page)
|
|||
m_skia_backend_context = Painting::DisplayListPlayerSkia::create_metal_context(*m_metal_context);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_VULKAN
|
||||
auto display_list_player_type = page->client().display_list_player_type();
|
||||
if (display_list_player_type == DisplayListPlayerType::Skia) {
|
||||
auto maybe_vulkan_context = Core::create_vulkan_context();
|
||||
if (!maybe_vulkan_context.is_error()) {
|
||||
auto vulkan_context = maybe_vulkan_context.release_value();
|
||||
m_skia_backend_context = Painting::DisplayListPlayerSkia::create_vulkan_context(vulkan_context);
|
||||
} else {
|
||||
dbgln("Vulkan context creation failed: {}", maybe_vulkan_context.error());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TraversableNavigable::~TraversableNavigable() = default;
|
||||
|
@ -1201,6 +1214,15 @@ void TraversableNavigable::paint(DevicePixelRect const& content_rect, Painting::
|
|||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_VULKAN
|
||||
if (m_skia_backend_context) {
|
||||
Painting::DisplayListPlayerSkia player(*m_skia_backend_context, target.bitmap());
|
||||
display_list.execute(player);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
Painting::DisplayListPlayerSkia player(target.bitmap());
|
||||
display_list.execute(player);
|
||||
} else {
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
# include <LibCore/MetalContext.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_VULKAN
|
||||
# include <LibCore/VulkanContext.h>
|
||||
#endif
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/document-sequences.html#traversable-navigable
|
||||
|
@ -130,8 +134,9 @@ private:
|
|||
|
||||
String m_window_handle;
|
||||
|
||||
#ifdef AK_OS_MACOS
|
||||
OwnPtr<Web::Painting::SkiaBackendContext> m_skia_backend_context;
|
||||
|
||||
#ifdef AK_OS_MACOS
|
||||
OwnPtr<Core::MetalContext> m_metal_context;
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -26,6 +26,13 @@
|
|||
#include <LibWeb/Painting/DisplayListPlayerSkia.h>
|
||||
#include <LibWeb/Painting/ShadowPainting.h>
|
||||
|
||||
#ifdef USE_VULKAN
|
||||
# include <gpu/ganesh/vk/GrVkDirectContext.h>
|
||||
# include <gpu/vk/GrVkBackendContext.h>
|
||||
# include <gpu/vk/VulkanBackendContext.h>
|
||||
# include <gpu/vk/VulkanExtensions.h>
|
||||
#endif
|
||||
|
||||
#ifdef AK_OS_MACOS
|
||||
# define FixedPoint FixedPointMacOS
|
||||
# define Duration DurationMacOS
|
||||
|
@ -47,10 +54,86 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
void read_into_bitmap(Gfx::Bitmap& bitmap)
|
||||
{
|
||||
auto image_info = SkImageInfo::Make(bitmap.width(), bitmap.height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType);
|
||||
SkPixmap pixmap(image_info, bitmap.begin(), bitmap.pitch());
|
||||
m_surface->readPixels(pixmap, 0, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
sk_sp<SkSurface> m_surface;
|
||||
};
|
||||
|
||||
#ifdef USE_VULKAN
|
||||
class SkiaVulkanBackendContext final : public SkiaBackendContext {
|
||||
AK_MAKE_NONCOPYABLE(SkiaVulkanBackendContext);
|
||||
AK_MAKE_NONMOVABLE(SkiaVulkanBackendContext);
|
||||
|
||||
public:
|
||||
SkiaVulkanBackendContext(sk_sp<GrDirectContext> context, NonnullOwnPtr<skgpu::VulkanExtensions> extensions)
|
||||
: m_context(move(context))
|
||||
, m_extensions(move(extensions))
|
||||
{
|
||||
}
|
||||
|
||||
~SkiaVulkanBackendContext() override {};
|
||||
|
||||
void flush_and_submit() override
|
||||
{
|
||||
m_context->flush();
|
||||
m_context->submit(GrSyncCpu::kYes);
|
||||
}
|
||||
|
||||
sk_sp<SkSurface> create_surface(int width, int height)
|
||||
{
|
||||
auto image_info = SkImageInfo::Make(width, height, kBGRA_8888_SkColorType, kPremul_SkAlphaType);
|
||||
return SkSurfaces::RenderTarget(m_context.get(), skgpu::Budgeted::kYes, image_info);
|
||||
}
|
||||
|
||||
skgpu::VulkanExtensions const* extensions() const { return m_extensions.ptr(); }
|
||||
|
||||
private:
|
||||
sk_sp<GrDirectContext> m_context;
|
||||
NonnullOwnPtr<skgpu::VulkanExtensions> m_extensions;
|
||||
};
|
||||
|
||||
OwnPtr<SkiaBackendContext> DisplayListPlayerSkia::create_vulkan_context(Core::VulkanContext& vulkan_context)
|
||||
{
|
||||
GrVkBackendContext backend_context;
|
||||
|
||||
backend_context.fInstance = vulkan_context.instance;
|
||||
backend_context.fDevice = vulkan_context.logical_device;
|
||||
backend_context.fQueue = vulkan_context.graphics_queue;
|
||||
backend_context.fPhysicalDevice = vulkan_context.physical_device;
|
||||
backend_context.fMaxAPIVersion = vulkan_context.api_version;
|
||||
backend_context.fGetProc = [](char const* proc_name, VkInstance instance, VkDevice device) {
|
||||
if (device != VK_NULL_HANDLE) {
|
||||
return vkGetDeviceProcAddr(device, proc_name);
|
||||
}
|
||||
return vkGetInstanceProcAddr(instance, proc_name);
|
||||
};
|
||||
|
||||
auto extensions = make<skgpu::VulkanExtensions>();
|
||||
backend_context.fVkExtensions = extensions.ptr();
|
||||
|
||||
sk_sp<GrDirectContext> ctx = GrDirectContexts::MakeVulkan(backend_context);
|
||||
VERIFY(ctx);
|
||||
return make<SkiaVulkanBackendContext>(ctx, move(extensions));
|
||||
}
|
||||
|
||||
DisplayListPlayerSkia::DisplayListPlayerSkia(SkiaBackendContext& context, Gfx::Bitmap& bitmap)
|
||||
{
|
||||
VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRA8888);
|
||||
auto surface = static_cast<SkiaVulkanBackendContext&>(context).create_surface(bitmap.width(), bitmap.height());
|
||||
m_surface = make<SkiaSurface>(surface);
|
||||
m_flush_context = [&bitmap, &surface = m_surface, &context] {
|
||||
context.flush_and_submit();
|
||||
surface->read_into_bitmap(bitmap);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef AK_OS_MACOS
|
||||
class SkiaMetalBackendContext final : public SkiaBackendContext {
|
||||
AK_MAKE_NONCOPYABLE(SkiaMetalBackendContext);
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
# include <LibCore/MetalContext.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_VULKAN
|
||||
# include <LibCore/VulkanContext.h>
|
||||
#endif
|
||||
|
||||
namespace Web::Painting {
|
||||
|
||||
class SkiaBackendContext {
|
||||
|
@ -70,6 +74,11 @@ public:
|
|||
|
||||
DisplayListPlayerSkia(Gfx::Bitmap&);
|
||||
|
||||
#ifdef USE_VULKAN
|
||||
static OwnPtr<SkiaBackendContext> create_vulkan_context(Core::VulkanContext&);
|
||||
DisplayListPlayerSkia(SkiaBackendContext&, Gfx::Bitmap&);
|
||||
#endif
|
||||
|
||||
#ifdef AK_OS_MACOS
|
||||
static OwnPtr<SkiaBackendContext> create_metal_context(Core::MetalContext const&);
|
||||
DisplayListPlayerSkia(SkiaBackendContext&, Core::MetalTexture&);
|
||||
|
|
|
@ -22,7 +22,10 @@
|
|||
},
|
||||
{
|
||||
"name": "skia",
|
||||
"platform": "linux | freebsd | openbsd"
|
||||
"platform": "linux | freebsd | openbsd",
|
||||
"features": [
|
||||
"vulkan"
|
||||
]
|
||||
},
|
||||
"sqlite3",
|
||||
"woff2"
|
||||
|
|
Loading…
Reference in a new issue