diff --git a/Libraries/LibGfx/PaintingSurface.cpp b/Libraries/LibGfx/PaintingSurface.cpp index bf1528da481..20f760d6510 100644 --- a/Libraries/LibGfx/PaintingSurface.cpp +++ b/Libraries/LibGfx/PaintingSurface.cpp @@ -16,8 +16,7 @@ #ifdef AK_OS_MACOS # include -#endif -#ifdef USE_VULKAN +#elif defined(USE_VULKAN_IMAGES) # include # include #endif @@ -31,6 +30,7 @@ struct PaintingSurface::Impl { RefPtr bitmap; }; +#if defined(AK_OS_MACOS) || defined(USE_VULKAN_IMAGES) static GrSurfaceOrigin origin_to_sk_origin(PaintingSurface::Origin origin) { switch (origin) { @@ -42,8 +42,9 @@ static GrSurfaceOrigin origin_to_sk_origin(PaintingSurface::Origin origin) return kTopLeft_GrSurfaceOrigin; } } +#endif -#ifdef USE_VULKAN +#ifdef USE_VULKAN_IMAGES static SkColorType vk_format_to_sk_color_type(VkFormat format) { switch (format) { diff --git a/Libraries/LibGfx/PaintingSurface.h b/Libraries/LibGfx/PaintingSurface.h index db22cd92a5c..66b919e0746 100644 --- a/Libraries/LibGfx/PaintingSurface.h +++ b/Libraries/LibGfx/PaintingSurface.h @@ -39,7 +39,7 @@ public: static NonnullRefPtr create_from_iosurface(Core::IOSurfaceHandle&&, NonnullRefPtr, Origin = Origin::TopLeft); #endif -#ifdef USE_VULKAN +#ifdef USE_VULKAN_IMAGES static NonnullRefPtr create_from_vkimage(NonnullRefPtr context, NonnullRefPtr vulkan_image, Origin origin); #endif diff --git a/Libraries/LibGfx/VulkanContext.cpp b/Libraries/LibGfx/VulkanContext.cpp index a9c5d536ded..d1a2055fed4 100644 --- a/Libraries/LibGfx/VulkanContext.cpp +++ b/Libraries/LibGfx/VulkanContext.cpp @@ -99,17 +99,22 @@ static ErrorOr create_logical_device(VkPhysicalDevice physical_device, queue_create_info.pQueuePriorities = &queue_priority; VkPhysicalDeviceFeatures deviceFeatures {}; +#ifdef USE_VULKAN_IMAGES char const* device_extensions[] = { VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME }; - + uint32_t device_extension_count = array_size(device_extensions); +#else + const char** device_extensions = nullptr; + uint32_t device_extension_count = 0; +#endif 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; - create_device_info.enabledExtensionCount = array_size(device_extensions); + create_device_info.enabledExtensionCount = device_extension_count; create_device_info.ppEnabledExtensionNames = device_extensions; if (vkCreateDevice(physical_device, &create_device_info, nullptr, &device) != VK_SUCCESS) { @@ -119,6 +124,7 @@ static ErrorOr create_logical_device(VkPhysicalDevice physical_device, return device; } +#ifdef USE_VULKAN_IMAGES static ErrorOr create_command_pool(VkDevice logical_device, uint32_t queue_family_index) { VkCommandPoolCreateInfo command_pool_info = { @@ -153,6 +159,7 @@ static ErrorOr allocate_command_buffer(VkDevice logical_device, } return command_buffer; } +#endif ErrorOr create_vulkan_context() { @@ -165,6 +172,7 @@ ErrorOr create_vulkan_context() VkQueue graphics_queue; vkGetDeviceQueue(logical_device, graphics_queue_family, 0, &graphics_queue); +#ifdef USE_VULKAN_IMAGES VkCommandPool command_pool = TRY(create_command_pool(logical_device, graphics_queue_family)); VkCommandBuffer command_buffer = TRY(allocate_command_buffer(logical_device, command_pool)); @@ -176,6 +184,7 @@ ErrorOr create_vulkan_context() if (pfn_vk_get_image_drm_format_modifier_properties_khr == nullptr) { return Error::from_string_literal("vkGetImageDrmFormatModifierPropertiesEXT unavailable"); } +#endif return VulkanContext { .api_version = api_version, @@ -184,15 +193,18 @@ ErrorOr create_vulkan_context() .logical_device = logical_device, .graphics_queue = graphics_queue, .graphics_queue_family = graphics_queue_family, +#ifdef USE_VULKAN_IMAGES .command_pool = command_pool, .command_buffer = command_buffer, .ext_procs = { .get_memory_fd = pfn_vk_get_memory_fd_khr, .get_image_drm_format_modifier_properties = pfn_vk_get_image_drm_format_modifier_properties_khr, }, +#endif }; } +#ifdef USE_VULKAN_IMAGES VulkanImage::~VulkanImage() { if (image != VK_NULL_HANDLE) { @@ -398,5 +410,6 @@ ErrorOr> create_shared_vulkan_image(VulkanContext con }; return image; } +#endif } diff --git a/Libraries/LibGfx/VulkanContext.h b/Libraries/LibGfx/VulkanContext.h index 6dea692e3cc..fce6addb125 100644 --- a/Libraries/LibGfx/VulkanContext.h +++ b/Libraries/LibGfx/VulkanContext.h @@ -11,8 +11,12 @@ # include # include # include -# include # include +# ifdef AK_OS_LINUX +# include +// Sharable Vulkan images are currently only implemented on Linux +# define USE_VULKAN_IMAGES 1 +# endif namespace Gfx { @@ -23,6 +27,7 @@ struct VulkanContext { VkDevice logical_device { VK_NULL_HANDLE }; VkQueue graphics_queue { VK_NULL_HANDLE }; uint32_t graphics_queue_family { 0 }; +# ifdef USE_VULKAN_IMAGES VkCommandPool command_pool { VK_NULL_HANDLE }; VkCommandBuffer command_buffer { VK_NULL_HANDLE }; struct @@ -30,8 +35,12 @@ struct VulkanContext { PFN_vkGetMemoryFdKHR get_memory_fd { nullptr }; PFN_vkGetImageDrmFormatModifierPropertiesEXT get_image_drm_format_modifier_properties { nullptr }; } ext_procs; +# endif }; +ErrorOr create_vulkan_context(); + +# ifdef USE_VULKAN_IMAGES struct VulkanImage : public RefCounted { VkImage image { VK_NULL_HANDLE }; VkDeviceMemory memory { VK_NULL_HANDLE }; @@ -68,8 +77,8 @@ static inline uint32_t vk_format_to_drm_format(VkFormat format) } } -ErrorOr create_vulkan_context(); ErrorOr> create_shared_vulkan_image(VulkanContext const& context, uint32_t width, uint32_t height, VkFormat format, uint32_t num_modifiers, uint64_t const* modifiers); +# endif } diff --git a/Libraries/LibWeb/WebGL/OpenGLContext.cpp b/Libraries/LibWeb/WebGL/OpenGLContext.cpp index c5e7f5637bc..fb16795b1e2 100644 --- a/Libraries/LibWeb/WebGL/OpenGLContext.cpp +++ b/Libraries/LibWeb/WebGL/OpenGLContext.cpp @@ -23,6 +23,11 @@ extern "C" { #include } +// Enable WebGL if we're on MacOS and can use Metal or if we can use shareable Vulkan images +#if defined(AK_OS_MACOS) || defined(USE_VULKAN_IMAGES) +# define ENABLE_WEBGL 1 +#endif + namespace Web::WebGL { struct OpenGLContext::Impl { @@ -34,8 +39,9 @@ struct OpenGLContext::Impl { GLuint framebuffer { 0 }; GLuint color_buffer { 0 }; GLuint depth_buffer { 0 }; + EGLint texture_target { 0 }; -#ifdef USE_VULKAN +#ifdef USE_VULKAN_IMAGES EGLImage egl_image { EGL_NO_IMAGE }; struct { PFNEGLQUERYDMABUFFORMATSEXTPROC query_dma_buf_formats { nullptr }; @@ -53,13 +59,16 @@ OpenGLContext::OpenGLContext(NonnullRefPtr skia_backend OpenGLContext::~OpenGLContext() { +#ifdef ENABLE_WEBGL free_surface_resources(); eglMakeCurrent(m_impl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(m_impl->display, m_impl->context); +#endif } void OpenGLContext::free_surface_resources() { +#ifdef ENABLE_WEBGL if (m_impl->framebuffer) { glDeleteFramebuffers(1, &m_impl->framebuffer); m_impl->framebuffer = 0; @@ -75,22 +84,24 @@ void OpenGLContext::free_surface_resources() m_impl->depth_buffer = 0; } -#ifdef USE_VULKAN +# ifdef USE_VULKAN_IMAGES if (m_impl->egl_image != EGL_NO_IMAGE) { eglDestroyImage(m_impl->display, m_impl->egl_image); m_impl->egl_image = EGL_NO_IMAGE; } -#endif +# endif if (m_impl->surface != EGL_NO_SURFACE) { -#ifdef AK_OS_MACOS +# ifdef AK_OS_MACOS eglReleaseTexImage(m_impl->display, m_impl->surface, EGL_BACK_BUFFER); -#endif +# endif eglDestroySurface(m_impl->display, m_impl->surface); m_impl->surface = EGL_NO_SURFACE; } +#endif } +#ifdef ENABLE_WEBGL static EGLConfig get_egl_config(EGLDisplay display) { EGLint const config_attribs[] = { @@ -113,24 +124,20 @@ static EGLConfig get_egl_config(EGLDisplay display) eglChooseConfig(display, config_attribs, configs.data(), number_of_configs, &number_of_configs); return number_of_configs > 0 ? configs[0] : EGL_NO_CONFIG_KHR; } +#endif OwnPtr OpenGLContext::create(NonnullRefPtr skia_backend_context, WebGLVersion webgl_version) { -#if !defined(AK_OS_MACOS) && !defined(USE_VULKAN) - (void)skia_backend_context; - (void)webgl_version; - return {}; -#endif - +#ifdef ENABLE_WEBGL EGLAttrib display_attributes[] = { EGL_PLATFORM_ANGLE_TYPE_ANGLE, -#if defined(AK_OS_MACOS) +# if defined(AK_OS_MACOS) EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE, -#elif defined(USE_VULKAN) +# elif defined(USE_VULKAN_IMAGES) EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE, EGL_PLATFORM_SURFACELESS_MESA, -#endif +# endif EGL_NONE, }; @@ -152,6 +159,14 @@ OwnPtr OpenGLContext::create(NonnullRefPtr OpenGLContext::create(NonnullRefPtr OpenGLContext::create(NonnullRefPtr(eglGetProcAddress("eglQueryDmaBufFormatsEXT")); if (!pfn_egl_query_dma_buf_formats_ext) { dbgln("eglQueryDmaBufFormatsEXT unavailable"); @@ -187,29 +202,38 @@ OwnPtr OpenGLContext::create(NonnullRefPtr(skia_backend_context, Impl { .display = display, .config = config, .context = context, -#ifdef USE_VULKAN + .texture_target = texture_target, +# ifdef USE_VULKAN_IMAGES .ext_procs = { .query_dma_buf_formats = pfn_egl_query_dma_buf_formats_ext, .query_dma_buf_modifiers = pfn_egl_query_dma_buf_modifiers_ext, }, -#endif +# endif }, webgl_version); +#else + (void)skia_backend_context; + (void)webgl_version; + return {}; +#endif } void OpenGLContext::notify_content_will_change() { +#ifdef ENABLE_WEBGL m_painting_surface->notify_content_will_change(); +#endif } void OpenGLContext::clear_buffer_to_default_values() { +#ifdef ENABLE_WEBGL Array current_clear_color; glGetFloatv(GL_COLOR_CLEAR_VALUE, current_clear_color.data()); @@ -234,41 +258,24 @@ void OpenGLContext::clear_buffer_to_default_values() glClearColor(current_clear_color[0], current_clear_color[1], current_clear_color[2], current_clear_color[3]); glClearDepthf(current_clear_depth); glClearStencil(current_clear_stencil); +#endif } -void OpenGLContext::allocate_painting_surface_if_needed() +#ifdef AK_OS_MACOS +void OpenGLContext::allocate_iosurface_painting_surface() { - if (m_painting_surface) - return; - - free_surface_resources(); - - VERIFY(!m_size.is_empty()); - - auto width = m_size.width(); - auto height = m_size.height(); - - auto* display = m_impl->display; - - EGLint texture_target_name = 0; - -#if defined(AK_OS_MACOS) auto iosurface = Core::IOSurfaceHandle::create(m_size.width(), m_size.height()); m_painting_surface = Gfx::PaintingSurface::create_from_iosurface(move(iosurface), m_skia_backend_context, Gfx::PaintingSurface::Origin::BottomLeft); - auto* config = m_impl->config; - EGLint target = 0; - eglGetConfigAttrib(display, config, EGL_BIND_TO_TEXTURE_TARGET_ANGLE, &target); - EGLint const surface_attributes[] = { EGL_WIDTH, - width, + m_size.width(), EGL_HEIGHT, - height, + m_size.height(), EGL_IOSURFACE_PLANE_ANGLE, 0, EGL_TEXTURE_TARGET, - target, + m_impl->texture_target, EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, EGL_TEXTURE_FORMAT, @@ -278,37 +285,39 @@ void OpenGLContext::allocate_painting_surface_if_needed() EGL_NONE, EGL_NONE, }; - m_impl->surface = eglCreatePbufferFromClientBuffer(display, EGL_IOSURFACE_ANGLE, iosurface.core_foundation_pointer(), config, surface_attributes); + m_impl->surface = eglCreatePbufferFromClientBuffer(m_impl->display, EGL_IOSURFACE_ANGLE, iosurface.core_foundation_pointer(), m_impl->config, surface_attributes); eglMakeCurrent(m_impl->display, m_impl->surface, m_impl->surface, m_impl->context); - eglGetConfigAttrib(display, config, EGL_BIND_TO_TEXTURE_TARGET_ANGLE, &texture_target_name); - VERIFY(texture_target_name == EGL_TEXTURE_RECTANGLE_ANGLE || texture_target_name == EGL_TEXTURE_2D); - glGenTextures(1, &m_impl->color_buffer); - glBindTexture(texture_target_name == EGL_TEXTURE_RECTANGLE_ANGLE ? GL_TEXTURE_RECTANGLE_ANGLE : GL_TEXTURE_2D, m_impl->color_buffer); - auto result = eglBindTexImage(display, m_impl->surface, EGL_BACK_BUFFER); + glBindTexture(m_impl->texture_target == EGL_TEXTURE_RECTANGLE_ANGLE ? GL_TEXTURE_RECTANGLE_ANGLE : GL_TEXTURE_2D, m_impl->color_buffer); + auto result = eglBindTexImage(m_impl->display, m_impl->surface, EGL_BACK_BUFFER); VERIFY(result == EGL_TRUE); -#elif defined(USE_VULKAN) +} +#endif + +#ifdef USE_VULKAN_IMAGES +void OpenGLContext::allocate_vkimage_painting_surface() +{ VkFormat vulkan_format = VK_FORMAT_B8G8R8A8_UNORM; uint32_t drm_format = Gfx::vk_format_to_drm_format(vulkan_format); // Ensure that our format is supported by the implementation. // FIXME: try other formats if not? EGLint num_formats = 0; - m_impl->ext_procs.query_dma_buf_formats(display, 0, nullptr, &num_formats); + m_impl->ext_procs.query_dma_buf_formats(m_impl->display, 0, nullptr, &num_formats); Vector egl_formats; egl_formats.resize(num_formats); - m_impl->ext_procs.query_dma_buf_formats(display, num_formats, egl_formats.data(), &num_formats); + m_impl->ext_procs.query_dma_buf_formats(m_impl->display, num_formats, egl_formats.data(), &num_formats); VERIFY(egl_formats.find(drm_format) != egl_formats.end()); EGLint num_modifiers = 0; - m_impl->ext_procs.query_dma_buf_modifiers(display, drm_format, 0, nullptr, nullptr, &num_modifiers); + m_impl->ext_procs.query_dma_buf_modifiers(m_impl->display, drm_format, 0, nullptr, nullptr, &num_modifiers); Vector egl_modifiers; egl_modifiers.resize(num_modifiers); Vector external_only; external_only.resize(num_modifiers); - m_impl->ext_procs.query_dma_buf_modifiers(display, drm_format, num_modifiers, egl_modifiers.data(), external_only.data(), &num_modifiers); + m_impl->ext_procs.query_dma_buf_modifiers(m_impl->display, drm_format, num_modifiers, egl_modifiers.data(), external_only.data(), &num_modifiers); Vector renderable_modifiers; for (int i = 0; i < num_modifiers; ++i) { if (!external_only[i]) { @@ -316,14 +325,14 @@ void OpenGLContext::allocate_painting_surface_if_needed() } } - auto vulkan_image = MUST(Gfx::create_shared_vulkan_image(m_skia_backend_context->vulkan_context(), width, height, vulkan_format, renderable_modifiers.size(), renderable_modifiers.data())); + auto vulkan_image = MUST(Gfx::create_shared_vulkan_image(m_skia_backend_context->vulkan_context(), m_size.width(), m_size.height(), vulkan_format, renderable_modifiers.size(), renderable_modifiers.data())); m_painting_surface = Gfx::PaintingSurface::create_from_vkimage(m_skia_backend_context, vulkan_image, Gfx::PaintingSurface::Origin::BottomLeft); EGLAttrib attribs[] = { EGL_WIDTH, - width, + m_size.width(), EGL_HEIGHT, - height, + m_size.height(), EGL_LINUX_DRM_FOURCC_EXT, drm_format, EGL_DMA_BUF_PLANE0_FD_EXT, @@ -338,7 +347,7 @@ void OpenGLContext::allocate_painting_surface_if_needed() static_cast(vulkan_image->info.modifier >> 32), EGL_NONE, }; - m_impl->egl_image = eglCreateImage(display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs); + m_impl->egl_image = eglCreateImage(m_impl->display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs); VERIFY(m_impl->egl_image != EGL_NO_IMAGE); m_impl->surface = EGL_NO_SURFACE; @@ -347,22 +356,40 @@ void OpenGLContext::allocate_painting_surface_if_needed() glGenTextures(1, &m_impl->color_buffer); glBindTexture(GL_TEXTURE_2D, m_impl->color_buffer); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_impl->egl_image); - texture_target_name = EGL_TEXTURE_2D; - glViewport(0, 0, width, height); + glViewport(0, 0, m_size.width(), m_size.height()); +} #endif +void OpenGLContext::allocate_painting_surface_if_needed() +{ +#ifdef ENABLE_WEBGL + if (m_painting_surface) + return; + + free_surface_resources(); + + VERIFY(!m_size.is_empty()); + +# if defined(AK_OS_MACOS) + allocate_iosurface_painting_surface(); +# elif defined(USE_VULKAN_IMAGES) + allocate_vkimage_painting_surface(); +# endif + VERIFY(m_painting_surface); + glGenFramebuffers(1, &m_impl->framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, m_impl->framebuffer); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target_name == EGL_TEXTURE_RECTANGLE_ANGLE ? GL_TEXTURE_RECTANGLE_ANGLE : GL_TEXTURE_2D, m_impl->color_buffer, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_impl->texture_target == EGL_TEXTURE_RECTANGLE_ANGLE ? GL_TEXTURE_RECTANGLE_ANGLE : GL_TEXTURE_2D, m_impl->color_buffer, 0); // NOTE: ANGLE doesn't allocate depth buffer for us, so we need to do it manually // FIXME: Depth buffer only needs to be allocated if it's configured in WebGL context attributes glGenRenderbuffers(1, &m_impl->depth_buffer); glBindRenderbuffer(GL_RENDERBUFFER, m_impl->depth_buffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, m_size.width(), m_size.height()); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_impl->depth_buffer); VERIFY(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); +#endif } void OpenGLContext::set_size(Gfx::IntSize const& size) @@ -375,24 +402,27 @@ void OpenGLContext::set_size(Gfx::IntSize const& size) void OpenGLContext::make_current() { +#ifdef ENABLE_WEBGL allocate_painting_surface_if_needed(); eglMakeCurrent(m_impl->display, m_impl->surface, m_impl->surface, m_impl->context); +#endif } void OpenGLContext::present(bool preserve_drawing_buffer) { +#ifdef ENABLE_WEBGL make_current(); // "Before the drawing buffer is presented for compositing the implementation shall ensure that all rendering operations have been flushed to the drawing buffer." // With Metal, glFlush flushes the command buffer, but without waiting for it to be scheduled or completed. // eglWaitUntilWorkScheduledANGLE flushes the command buffer, and waits until it has been scheduled, hence the name. // eglWaitUntilWorkScheduledANGLE only has an effect on CGL and Metal backends, so we only use it on macOS. -#if defined(AK_OS_MACOS) +# if defined(AK_OS_MACOS) eglWaitUntilWorkScheduledANGLE(m_impl->display); -#elif defined(USE_VULKAN) +# elif defined(USE_VULKAN_IMAGES) // FIXME: CPU sync for now, but it would be better to export a fence and have Skia wait for it before reading from the surface glFinish(); -#endif +# endif // "By default, after compositing the contents of the drawing buffer shall be cleared to their default values, as shown in the table above. // This default behavior can be changed by setting the preserveDrawingBuffer attribute of the WebGLContextAttributes object. @@ -401,6 +431,9 @@ void OpenGLContext::present(bool preserve_drawing_buffer) // FIXME: we're assuming the clear operation won't actually be submitted to the GPU clear_buffer_to_default_values(); } +#else + (void)preserve_drawing_buffer; +#endif } RefPtr OpenGLContext::surface() @@ -485,6 +518,7 @@ Vector s_available_webgl_extensions { Vector OpenGLContext::get_supported_extensions() { +#ifdef ENABLE_WEBGL if (m_requestable_extensions.has_value()) return m_requestable_extensions.value(); @@ -521,12 +555,20 @@ Vector OpenGLContext::get_supported_extensions() // been requested. m_requestable_extensions = extensions; return extensions; +#else + (void)m_webgl_version; + return {}; +#endif } void OpenGLContext::request_extension(char const* extension_name) { +#ifdef ENABLE_WEBGL make_current(); glRequestExtensionANGLE(extension_name); +#else + (void)extension_name; +#endif } } diff --git a/Libraries/LibWeb/WebGL/OpenGLContext.h b/Libraries/LibWeb/WebGL/OpenGLContext.h index 03dba865ce9..7a7ac5a4d5e 100644 --- a/Libraries/LibWeb/WebGL/OpenGLContext.h +++ b/Libraries/LibWeb/WebGL/OpenGLContext.h @@ -52,6 +52,11 @@ private: WebGLVersion m_webgl_version; void free_surface_resources(); +#if defined(AK_OS_MACOS) + void allocate_iosurface_painting_surface(); +#elif defined(USE_VULKAN_IMAGES) + void allocate_vkimage_painting_surface(); +#endif }; }