2024-01-18 20:29:09 +01:00
/*
* Copyright ( c ) 2024 , Aliaksandr Kalenik < kalenik . aliaksandr @ gmail . com >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2025-01-17 15:42:17 +00:00
# include <AK/HashMap.h>
2024-01-18 20:29:09 +01:00
# include <AK/OwnPtr.h>
2024-11-29 22:15:02 +01:00
# include <AK/String.h>
# include <LibGfx/PaintingSurface.h>
2024-01-18 20:29:09 +01:00
# include <LibWeb/WebGL/OpenGLContext.h>
2025-08-15 09:49:48 -04:00
# include <EGL/egl.h>
# include <EGL/eglext.h>
# define EGL_EGLEXT_PROTOTYPES 1
2025-03-23 11:27:39 +00:00
extern " C " {
2025-08-15 09:49:48 -04:00
# include <EGL/eglext_angle.h>
2025-03-23 11:27:39 +00:00
}
2025-08-15 09:49:48 -04:00
# define GL_GLEXT_PROTOTYPES 1
# include <GLES2/gl2.h>
# include <GLES2/gl2ext.h>
2025-01-10 12:52:47 +00:00
extern " C " {
2025-08-15 09:49:48 -04:00
# include <GLES2/gl2ext_angle.h>
2025-01-10 12:52:47 +00:00
}
2024-11-29 22:15:02 +01:00
2025-08-19 12:07:03 -04:00
// 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
2024-01-18 20:29:09 +01:00
namespace Web : : WebGL {
2024-11-29 22:15:02 +01:00
struct OpenGLContext : : Impl {
2025-08-28 11:08:37 -04:00
EGLDisplay display { EGL_NO_DISPLAY } ;
EGLConfig config { EGL_NO_CONFIG_KHR } ;
EGLContext context { EGL_NO_CONTEXT } ;
EGLSurface surface { EGL_NO_SURFACE } ;
2024-11-29 22:15:02 +01:00
GLuint framebuffer { 0 } ;
2025-08-15 09:39:39 -04:00
GLuint color_buffer { 0 } ;
2024-11-29 22:15:02 +01:00
GLuint depth_buffer { 0 } ;
2025-08-19 12:07:03 -04:00
EGLint texture_target { 0 } ;
2025-08-15 09:49:48 -04:00
2025-08-19 12:07:03 -04:00
# ifdef USE_VULKAN_IMAGES
2025-08-15 09:49:48 -04:00
EGLImage egl_image { EGL_NO_IMAGE } ;
struct {
PFNEGLQUERYDMABUFFORMATSEXTPROC query_dma_buf_formats { nullptr } ;
PFNEGLQUERYDMABUFMODIFIERSEXTPROC query_dma_buf_modifiers { nullptr } ;
} ext_procs ;
2024-11-29 22:15:02 +01:00
# endif
} ;
2025-03-23 10:38:00 +00:00
OpenGLContext : : OpenGLContext ( NonnullRefPtr < Gfx : : SkiaBackendContext > skia_backend_context , Impl impl , WebGLVersion webgl_version )
2024-11-29 22:15:02 +01:00
: m_skia_backend_context ( move ( skia_backend_context ) )
, m_impl ( make < Impl > ( impl ) )
2025-03-23 10:38:00 +00:00
, m_webgl_version ( webgl_version )
2024-11-29 22:15:02 +01:00
{
}
OpenGLContext : : ~ OpenGLContext ( )
2024-01-18 20:29:09 +01:00
{
2025-08-19 12:07:03 -04:00
# ifdef ENABLE_WEBGL
2025-08-15 09:39:39 -04:00
free_surface_resources ( ) ;
2024-11-29 22:15:02 +01:00
eglMakeCurrent ( m_impl - > display , EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT ) ;
eglDestroyContext ( m_impl - > display , m_impl - > context ) ;
2025-08-19 12:07:03 -04:00
# endif
2024-11-29 22:15:02 +01:00
}
2025-08-15 09:39:39 -04:00
void OpenGLContext : : free_surface_resources ( )
{
2025-08-19 12:07:03 -04:00
# ifdef ENABLE_WEBGL
2025-08-28 11:08:37 -04:00
eglMakeCurrent ( m_impl - > display , m_impl - > surface , m_impl - > surface , m_impl - > context ) ;
2025-08-15 09:39:39 -04:00
if ( m_impl - > framebuffer ) {
glDeleteFramebuffers ( 1 , & m_impl - > framebuffer ) ;
m_impl - > framebuffer = 0 ;
}
if ( m_impl - > color_buffer ) {
glDeleteTextures ( 1 , & m_impl - > color_buffer ) ;
m_impl - > color_buffer = 0 ;
}
if ( m_impl - > depth_buffer ) {
glDeleteRenderbuffers ( 1 , & m_impl - > depth_buffer ) ;
m_impl - > depth_buffer = 0 ;
}
2025-08-19 12:07:03 -04:00
# ifdef USE_VULKAN_IMAGES
2025-08-15 09:49:48 -04:00
if ( m_impl - > egl_image ! = EGL_NO_IMAGE ) {
eglDestroyImage ( m_impl - > display , m_impl - > egl_image ) ;
m_impl - > egl_image = EGL_NO_IMAGE ;
}
2025-08-19 12:07:03 -04:00
# endif
2025-08-15 09:49:48 -04:00
2025-08-15 09:39:39 -04:00
if ( m_impl - > surface ! = EGL_NO_SURFACE ) {
2025-08-19 12:07:03 -04:00
# ifdef AK_OS_MACOS
2025-08-15 09:39:39 -04:00
eglReleaseTexImage ( m_impl - > display , m_impl - > surface , EGL_BACK_BUFFER ) ;
2025-08-19 12:07:03 -04:00
# endif
2025-08-15 09:39:39 -04:00
eglDestroySurface ( m_impl - > display , m_impl - > surface ) ;
m_impl - > surface = EGL_NO_SURFACE ;
}
2025-08-19 12:07:03 -04:00
# endif
2025-08-15 09:39:39 -04:00
}
2025-08-19 12:07:03 -04:00
# ifdef ENABLE_WEBGL
2024-11-29 22:15:02 +01:00
static EGLConfig get_egl_config ( EGLDisplay display )
{
EGLint const config_attribs [ ] = {
2025-08-15 09:45:08 -04:00
EGL_SURFACE_TYPE , EGL_PBUFFER_BIT ,
2024-11-29 22:15:02 +01:00
EGL_RENDERABLE_TYPE , EGL_OPENGL_ES2_BIT ,
EGL_RED_SIZE , 8 ,
EGL_GREEN_SIZE , 8 ,
EGL_BLUE_SIZE , 8 ,
EGL_ALPHA_SIZE , 8 ,
EGL_DEPTH_SIZE , 24 ,
EGL_STENCIL_SIZE , 8 ,
EGL_NONE
} ;
EGLint number_of_configs ;
eglChooseConfig ( display , config_attribs , NULL , 0 , & number_of_configs ) ;
Vector < EGLConfig > configs ;
configs . resize ( number_of_configs ) ;
eglChooseConfig ( display , config_attribs , configs . data ( ) , number_of_configs , & number_of_configs ) ;
2025-08-15 09:48:47 -04:00
return number_of_configs > 0 ? configs [ 0 ] : EGL_NO_CONFIG_KHR ;
2024-11-29 22:15:02 +01:00
}
2025-08-19 12:07:03 -04:00
# endif
2024-11-29 22:15:02 +01:00
2025-03-23 10:38:00 +00:00
OwnPtr < OpenGLContext > OpenGLContext : : create ( NonnullRefPtr < Gfx : : SkiaBackendContext > skia_backend_context , WebGLVersion webgl_version )
2024-11-29 22:15:02 +01:00
{
2025-08-19 12:07:03 -04:00
# ifdef ENABLE_WEBGL
2025-03-23 10:51:05 +00:00
EGLAttrib display_attributes [ ] = {
EGL_PLATFORM_ANGLE_TYPE_ANGLE ,
2025-08-19 12:07:03 -04:00
# if defined(AK_OS_MACOS)
2025-03-23 10:51:05 +00:00
EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE ,
2025-08-19 12:07:03 -04:00
# elif defined(USE_VULKAN_IMAGES)
2025-08-15 09:49:48 -04:00
EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE ,
EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE ,
EGL_PLATFORM_SURFACELESS_MESA ,
2025-08-19 12:07:03 -04:00
# endif
2025-03-23 10:51:05 +00:00
EGL_NONE ,
} ;
2025-08-19 17:37:54 +02:00
auto display = eglGetPlatformDisplay ( EGL_PLATFORM_ANGLE_ANGLE , reinterpret_cast < void * > ( EGL_DEFAULT_DISPLAY ) , display_attributes ) ;
2024-11-29 22:15:02 +01:00
if ( display = = EGL_NO_DISPLAY ) {
dbgln ( " Failed to get EGL display " ) ;
return { } ;
}
EGLint major , minor ;
if ( ! eglInitialize ( display , & major , & minor ) ) {
dbgln ( " Failed to initialize EGL " ) ;
return { } ;
}
auto * config = get_egl_config ( display ) ;
2025-08-15 09:48:47 -04:00
if ( config = = EGL_NO_CONFIG_KHR ) {
dbgln ( " Failed to find EGLConfig " ) ;
return { } ;
}
2024-11-29 22:15:02 +01:00
2025-08-19 12:07:03 -04:00
EGLint texture_target ;
# if defined(AK_OS_MACOS)
eglGetConfigAttrib ( display , config , EGL_BIND_TO_TEXTURE_TARGET_ANGLE , & texture_target ) ;
VERIFY ( texture_target = = EGL_TEXTURE_RECTANGLE_ANGLE | | texture_target = = EGL_TEXTURE_2D ) ;
# elif defined(USE_VULKAN_IMAGES)
texture_target = EGL_TEXTURE_2D ;
# endif
2024-11-29 22:15:02 +01:00
EGLint context_attributes [ ] = {
2025-01-10 12:52:47 +00:00
EGL_CONTEXT_CLIENT_VERSION ,
2025-03-23 10:38:00 +00:00
webgl_version = = WebGLVersion : : WebGL1 ? 2 : 3 ,
2025-01-10 12:52:47 +00:00
EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE ,
EGL_TRUE ,
2025-03-23 11:20:16 +00:00
EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE ,
EGL_TRUE ,
2025-03-23 10:38:00 +00:00
EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE ,
EGL_FALSE ,
2025-08-19 12:07:03 -04:00
# ifdef USE_VULKAN_IMAGES
2025-08-15 09:49:48 -04:00
// we need GL_OES_EGL_image
EGL_EXTENSIONS_ENABLED_ANGLE ,
EGL_TRUE ,
2025-08-19 12:07:03 -04:00
# endif
2025-01-10 12:52:47 +00:00
EGL_NONE ,
EGL_NONE ,
2024-11-29 22:15:02 +01:00
} ;
2025-08-19 17:37:54 +02:00
auto context = eglCreateContext ( display , config , EGL_NO_CONTEXT , context_attributes ) ;
2024-11-29 22:15:02 +01:00
if ( context = = EGL_NO_CONTEXT ) {
dbgln ( " Failed to create EGL context " ) ;
return { } ;
}
2025-08-19 12:07:03 -04:00
# ifdef USE_VULKAN_IMAGES
2025-08-19 17:37:54 +02:00
auto pfn_egl_query_dma_buf_formats_ext = reinterpret_cast < PFNEGLQUERYDMABUFFORMATSEXTPROC > ( eglGetProcAddress ( " eglQueryDmaBufFormatsEXT " ) ) ;
2025-08-15 09:49:48 -04:00
if ( ! pfn_egl_query_dma_buf_formats_ext ) {
dbgln ( " eglQueryDmaBufFormatsEXT unavailable " ) ;
return { } ;
}
2025-08-19 17:37:54 +02:00
auto pfn_egl_query_dma_buf_modifiers_ext = reinterpret_cast < PFNEGLQUERYDMABUFMODIFIERSEXTPROC > ( eglGetProcAddress ( " eglQueryDmaBufModifiersEXT " ) ) ;
2025-08-15 09:49:48 -04:00
if ( ! pfn_egl_query_dma_buf_modifiers_ext ) {
dbgln ( " eglQueryDmaBufModifiersEXT unavailable " ) ;
return { } ;
}
2025-08-19 12:07:03 -04:00
# endif
2025-08-15 09:49:48 -04:00
return make < OpenGLContext > ( skia_backend_context , Impl {
. display = display ,
. config = config ,
. context = context ,
2025-08-19 12:07:03 -04:00
. texture_target = texture_target ,
# ifdef USE_VULKAN_IMAGES
2025-08-15 09:49:48 -04:00
. ext_procs = {
. query_dma_buf_formats = pfn_egl_query_dma_buf_formats_ext ,
. query_dma_buf_modifiers = pfn_egl_query_dma_buf_modifiers_ext ,
} ,
2025-08-19 12:07:03 -04:00
# endif
2025-08-15 09:49:48 -04:00
} ,
webgl_version ) ;
2025-08-19 12:07:03 -04:00
# else
( void ) skia_backend_context ;
( void ) webgl_version ;
return { } ;
# endif
2024-01-18 20:29:09 +01:00
}
2024-11-29 23:15:43 +01:00
void OpenGLContext : : notify_content_will_change ( )
{
2025-08-19 12:07:03 -04:00
# ifdef ENABLE_WEBGL
2024-11-29 23:15:43 +01:00
m_painting_surface - > notify_content_will_change ( ) ;
2025-08-19 12:07:03 -04:00
# endif
2024-11-29 23:15:43 +01:00
}
2024-01-18 20:29:09 +01:00
void OpenGLContext : : clear_buffer_to_default_values ( )
{
2025-08-19 12:07:03 -04:00
# ifdef ENABLE_WEBGL
2025-01-28 17:11:47 +00:00
Array < GLfloat , 4 > current_clear_color ;
glGetFloatv ( GL_COLOR_CLEAR_VALUE , current_clear_color . data ( ) ) ;
GLfloat current_clear_depth ;
glGetFloatv ( GL_DEPTH_CLEAR_VALUE , & current_clear_depth ) ;
GLint current_clear_stencil ;
glGetIntegerv ( GL_STENCIL_CLEAR_VALUE , & current_clear_stencil ) ;
// The implicit clear value for the color buffer is (0, 0, 0, 0)
glClearColor ( 0 , 0 , 0 , 0 ) ;
// The implicit clear value for the depth buffer is 1.0.
glClearDepthf ( 1.0f ) ;
// The implicit clear value for the stencil buffer is 0.
glClearStencil ( 0 ) ;
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ) ;
// Restore the clear 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 ) ;
2025-08-19 12:07:03 -04:00
# endif
2024-01-18 20:29:09 +01:00
}
2025-08-19 12:07:03 -04:00
# ifdef AK_OS_MACOS
void OpenGLContext : : allocate_iosurface_painting_surface ( )
2024-11-29 22:15:02 +01:00
{
2025-08-15 09:49:48 -04:00
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 ) ;
2024-11-29 22:15:02 +01:00
EGLint const surface_attributes [ ] = {
EGL_WIDTH ,
2025-08-19 12:07:03 -04:00
m_size . width ( ) ,
2024-11-29 22:15:02 +01:00
EGL_HEIGHT ,
2025-08-19 12:07:03 -04:00
m_size . height ( ) ,
2024-11-29 22:15:02 +01:00
EGL_IOSURFACE_PLANE_ANGLE ,
0 ,
EGL_TEXTURE_TARGET ,
2025-08-19 12:07:03 -04:00
m_impl - > texture_target ,
2024-11-29 22:15:02 +01:00
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE ,
GL_BGRA_EXT ,
EGL_TEXTURE_FORMAT ,
EGL_TEXTURE_RGBA ,
EGL_TEXTURE_TYPE_ANGLE ,
GL_UNSIGNED_BYTE ,
EGL_NONE ,
EGL_NONE ,
} ;
2025-08-19 12:07:03 -04:00
m_impl - > surface = eglCreatePbufferFromClientBuffer ( m_impl - > display , EGL_IOSURFACE_ANGLE , iosurface . core_foundation_pointer ( ) , m_impl - > config , surface_attributes ) ;
2024-11-29 22:15:02 +01:00
eglMakeCurrent ( m_impl - > display , m_impl - > surface , m_impl - > surface , m_impl - > context ) ;
2025-08-15 09:39:39 -04:00
glGenTextures ( 1 , & m_impl - > color_buffer ) ;
2025-08-19 12:07:03 -04:00
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 ) ;
2024-11-29 22:15:02 +01:00
VERIFY ( result = = EGL_TRUE ) ;
2025-08-19 12:07:03 -04:00
}
# endif
# ifdef USE_VULKAN_IMAGES
void OpenGLContext : : allocate_vkimage_painting_surface ( )
{
2025-08-15 09:49:48 -04:00
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 ;
2025-08-19 12:07:03 -04:00
m_impl - > ext_procs . query_dma_buf_formats ( m_impl - > display , 0 , nullptr , & num_formats ) ;
2025-08-15 09:49:48 -04:00
Vector < EGLint > egl_formats ;
egl_formats . resize ( num_formats ) ;
2025-08-19 12:07:03 -04:00
m_impl - > ext_procs . query_dma_buf_formats ( m_impl - > display , num_formats , egl_formats . data ( ) , & num_formats ) ;
2025-08-15 09:49:48 -04:00
VERIFY ( egl_formats . find ( drm_format ) ! = egl_formats . end ( ) ) ;
EGLint num_modifiers = 0 ;
2025-08-19 12:07:03 -04:00
m_impl - > ext_procs . query_dma_buf_modifiers ( m_impl - > display , drm_format , 0 , nullptr , nullptr , & num_modifiers ) ;
2025-08-15 09:49:48 -04:00
Vector < uint64_t > egl_modifiers ;
egl_modifiers . resize ( num_modifiers ) ;
Vector < EGLBoolean > external_only ;
external_only . resize ( num_modifiers ) ;
2025-08-19 12:07:03 -04:00
m_impl - > ext_procs . query_dma_buf_modifiers ( m_impl - > display , drm_format , num_modifiers , egl_modifiers . data ( ) , external_only . data ( ) , & num_modifiers ) ;
2025-08-15 09:49:48 -04:00
Vector < uint64_t > renderable_modifiers ;
for ( int i = 0 ; i < num_modifiers ; + + i ) {
if ( ! external_only [ i ] ) {
renderable_modifiers . append ( egl_modifiers [ i ] ) ;
}
}
2025-08-19 12:07:03 -04:00
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 ( ) ) ) ;
2025-08-15 09:49:48 -04:00
m_painting_surface = Gfx : : PaintingSurface : : create_from_vkimage ( m_skia_backend_context , vulkan_image , Gfx : : PaintingSurface : : Origin : : BottomLeft ) ;
EGLAttrib attribs [ ] = {
EGL_WIDTH ,
2025-08-19 12:07:03 -04:00
m_size . width ( ) ,
2025-08-15 09:49:48 -04:00
EGL_HEIGHT ,
2025-08-19 12:07:03 -04:00
m_size . height ( ) ,
2025-08-15 09:49:48 -04:00
EGL_LINUX_DRM_FOURCC_EXT ,
drm_format ,
EGL_DMA_BUF_PLANE0_FD_EXT ,
vulkan_image - > get_dma_buf_fd ( ) , // EGL takes ownership of the fd
EGL_DMA_BUF_PLANE0_OFFSET_EXT ,
0 ,
EGL_DMA_BUF_PLANE0_PITCH_EXT ,
static_cast < uint32_t > ( vulkan_image - > info . row_pitch ) ,
EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT ,
static_cast < uint32_t > ( vulkan_image - > info . modifier & 0xffffffff ) ,
EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT ,
static_cast < uint32_t > ( vulkan_image - > info . modifier > > 32 ) ,
EGL_NONE ,
} ;
2025-08-19 12:07:03 -04:00
m_impl - > egl_image = eglCreateImage ( m_impl - > display , EGL_NO_CONTEXT , EGL_LINUX_DMA_BUF_EXT , nullptr , attribs ) ;
2025-08-15 09:49:48 -04:00
VERIFY ( m_impl - > egl_image ! = EGL_NO_IMAGE ) ;
m_impl - > surface = EGL_NO_SURFACE ;
eglMakeCurrent ( m_impl - > display , m_impl - > surface , m_impl - > surface , m_impl - > context ) ;
glGenTextures ( 1 , & m_impl - > color_buffer ) ;
glBindTexture ( GL_TEXTURE_2D , m_impl - > color_buffer ) ;
glEGLImageTargetTexture2DOES ( GL_TEXTURE_2D , m_impl - > egl_image ) ;
2025-08-19 12:07:03 -04:00
glViewport ( 0 , 0 , m_size . width ( ) , m_size . height ( ) ) ;
}
2025-08-15 09:49:48 -04:00
# endif
2024-11-29 22:15:02 +01:00
2025-08-19 12:07:03 -04:00
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 ) ;
2025-08-28 11:08:37 -04:00
VERIFY ( eglGetCurrentContext ( ) = = m_impl - > context ) ;
2025-08-19 12:07:03 -04:00
2024-11-29 22:15:02 +01:00
glGenFramebuffers ( 1 , & m_impl - > framebuffer ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , m_impl - > framebuffer ) ;
2025-08-19 12:07:03 -04:00
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 ) ;
2024-11-29 22:15:02 +01:00
// 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 ) ;
2025-08-19 12:07:03 -04:00
glRenderbufferStorage ( GL_RENDERBUFFER , GL_DEPTH_COMPONENT16 , m_size . width ( ) , m_size . height ( ) ) ;
2024-11-29 22:15:02 +01:00
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER , m_impl - > depth_buffer ) ;
2025-08-15 09:49:48 -04:00
VERIFY ( glCheckFramebufferStatus ( GL_FRAMEBUFFER ) = = GL_FRAMEBUFFER_COMPLETE ) ;
2025-08-19 12:07:03 -04:00
# endif
2024-11-29 22:15:02 +01:00
}
void OpenGLContext : : set_size ( Gfx : : IntSize const & size )
{
if ( m_size ! = size ) {
m_painting_surface = nullptr ;
}
m_size = size ;
}
void OpenGLContext : : make_current ( )
{
2025-08-19 12:07:03 -04:00
# ifdef ENABLE_WEBGL
2024-11-29 22:15:02 +01:00
allocate_painting_surface_if_needed ( ) ;
eglMakeCurrent ( m_impl - > display , m_impl - > surface , m_impl - > surface , m_impl - > context ) ;
2025-08-19 12:07:03 -04:00
# endif
2024-11-29 22:15:02 +01:00
}
2025-03-23 11:27:39 +00:00
void OpenGLContext : : present ( bool preserve_drawing_buffer )
{
2025-08-19 12:07:03 -04:00
# ifdef ENABLE_WEBGL
2025-03-23 11:27:39 +00:00
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.
2025-08-19 12:07:03 -04:00
# if defined(AK_OS_MACOS)
2025-03-23 11:27:39 +00:00
eglWaitUntilWorkScheduledANGLE ( m_impl - > display ) ;
2025-08-19 12:07:03 -04:00
# elif defined(USE_VULKAN_IMAGES)
2025-08-15 09:49:48 -04:00
// 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 ( ) ;
2025-08-19 12:07:03 -04:00
# endif
2025-03-23 11:27:39 +00:00
// "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.
// If this flag is true, the contents of the drawing buffer shall be preserved until the author either clears or overwrites them."
if ( ! preserve_drawing_buffer ) {
2025-08-15 09:49:48 -04:00
// FIXME: we're assuming the clear operation won't actually be submitted to the GPU
2025-03-23 11:27:39 +00:00
clear_buffer_to_default_values ( ) ;
}
2025-08-19 12:07:03 -04:00
# else
( void ) preserve_drawing_buffer ;
# endif
2025-03-23 11:27:39 +00:00
}
2024-11-29 22:15:02 +01:00
RefPtr < Gfx : : PaintingSurface > OpenGLContext : : surface ( )
{
return m_painting_surface ;
}
2025-01-08 13:00:12 +00:00
u32 OpenGLContext : : default_renderbuffer ( ) const
{
return m_impl - > depth_buffer ;
}
u32 OpenGLContext : : default_framebuffer ( ) const
{
return m_impl - > framebuffer ;
}
2025-01-17 15:42:17 +00:00
struct Extension {
String webgl_extension_name ;
Vector < StringView > required_angle_extensions ;
2025-03-23 10:38:00 +00:00
Optional < OpenGLContext : : WebGLVersion > only_for_webgl_version { OptionalNone { } } ;
2025-01-17 15:42:17 +00:00
} ;
Vector < Extension > s_available_webgl_extensions {
2024-12-10 05:49:33 +01:00
// Khronos ratified WebGL Extensions
2025-03-23 10:38:00 +00:00
{ " ANGLE_instanced_arrays " _string , { " GL_ANGLE_instanced_arrays " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
{ " EXT_blend_minmax " _string , { " GL_EXT_blend_minmax " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
{ " EXT_frag_depth " _string , { " GL_EXT_frag_depth " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
{ " EXT_shader_texture_lod " _string , { " GL_EXT_shader_texture_lod " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
2025-01-17 15:42:17 +00:00
{ " EXT_texture_filter_anisotropic " _string , { " GL_EXT_texture_filter_anisotropic " sv } } ,
2025-03-23 10:38:00 +00:00
{ " OES_element_index_uint " _string , { " GL_OES_element_index_uint " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
{ " OES_standard_derivatives " _string , { " GL_OES_standard_derivatives " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
{ " OES_texture_float " _string , { " GL_OES_texture_float " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
2025-01-17 15:42:17 +00:00
{ " OES_texture_float_linear " _string , { " GL_OES_texture_float_linear " sv } } ,
2025-03-23 10:38:00 +00:00
{ " OES_texture_half_float " _string , { " GL_OES_texture_half_float " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
{ " OES_texture_half_float_linear " _string , { " GL_OES_texture_half_float_linear " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
{ " OES_vertex_array_object " _string , { " GL_OES_vertex_array_object " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
2025-01-17 15:42:17 +00:00
{ " WEBGL_compressed_texture_s3tc " _string , { " GL_EXT_texture_compression_dxt1 " sv , " GL_ANGLE_texture_compression_dxt3 " sv , " GL_ANGLE_texture_compression_dxt5 " sv } } ,
{ " WEBGL_debug_renderer_info " _string , { } } ,
{ " WEBGL_debug_shaders " _string , { } } ,
2025-03-23 10:38:00 +00:00
{ " WEBGL_depth_texture " _string , { " GL_ANGLE_depth_texture " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
{ " WEBGL_draw_buffers " _string , { " GL_EXT_draw_buffers " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
2025-01-17 15:42:17 +00:00
{ " WEBGL_lose_context " _string , { } } ,
2024-12-10 05:49:33 +01:00
// Community approved WebGL Extensions
2025-01-17 15:42:17 +00:00
{ " EXT_clip_control " _string , { " GL_EXT_clip_control " sv } } ,
2025-03-23 10:38:00 +00:00
{ " EXT_color_buffer_float " _string , { " GL_EXT_color_buffer_float " sv } , OpenGLContext : : WebGLVersion : : WebGL2 } ,
2025-01-17 15:42:17 +00:00
{ " EXT_color_buffer_half_float " _string , { " GL_EXT_color_buffer_half_float " sv } } ,
2025-03-23 10:38:00 +00:00
{ " EXT_conservative_depth " _string , { " GL_EXT_conservative_depth " sv } , OpenGLContext : : WebGLVersion : : WebGL2 } ,
2025-01-17 15:42:17 +00:00
{ " EXT_depth_clamp " _string , { " GL_EXT_depth_clamp " sv } } ,
2025-03-23 10:38:00 +00:00
{ " EXT_disjoint_timer_query " _string , { " GL_EXT_disjoint_timer_query " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
{ " EXT_disjoint_timer_query_webgl2 " _string , { " GL_EXT_disjoint_timer_query " sv } , OpenGLContext : : WebGLVersion : : WebGL2 } ,
2025-01-17 15:42:17 +00:00
{ " EXT_float_blend " _string , { " GL_EXT_float_blend " sv } } ,
{ " EXT_polygon_offset_clamp " _string , { " GL_EXT_polygon_offset_clamp " sv } } ,
2025-03-23 10:38:00 +00:00
{ " EXT_render_snorm " _string , { " GL_EXT_render_snorm " sv } , OpenGLContext : : WebGLVersion : : WebGL2 } ,
{ " EXT_sRGB " _string , { " GL_EXT_sRGB " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
2025-01-17 15:42:17 +00:00
{ " EXT_texture_compression_bptc " _string , { " GL_EXT_texture_compression_bptc " sv } } ,
{ " EXT_texture_compression_rgtc " _string , { " GL_EXT_texture_compression_rgtc " sv } } ,
{ " EXT_texture_mirror_clamp_to_edge " _string , { " GL_EXT_texture_mirror_clamp_to_edge " sv } } ,
2025-03-23 10:38:00 +00:00
{ " EXT_texture_norm16 " _string , { " GL_EXT_texture_norm16 " sv } , OpenGLContext : : WebGLVersion : : WebGL2 } ,
2025-01-17 15:42:17 +00:00
{ " KHR_parallel_shader_compile " _string , { " GL_KHR_parallel_shader_compile " sv } } ,
2025-03-23 10:38:00 +00:00
{ " NV_shader_noperspective_interpolation " _string , { " GL_NV_shader_noperspective_interpolation " sv } , OpenGLContext : : WebGLVersion : : WebGL2 } ,
2025-01-17 15:42:17 +00:00
{ " OES_draw_buffers_indexed " _string , { " GL_OES_draw_buffers_indexed " sv } } ,
2025-03-23 10:38:00 +00:00
{ " OES_fbo_render_mipmap " _string , { " GL_OES_fbo_render_mipmap " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
{ " OES_sample_variables " _string , { " GL_OES_sample_variables " sv } , OpenGLContext : : WebGLVersion : : WebGL2 } ,
{ " OES_shader_multisample_interpolation " _string , { " GL_OES_shader_multisample_interpolation " sv } , OpenGLContext : : WebGLVersion : : WebGL2 } ,
{ " OVR_multiview2 " _string , { " GL_OVR_multiview2 " sv } , OpenGLContext : : WebGLVersion : : WebGL2 } ,
2025-01-17 15:42:17 +00:00
{ " WEBGL_blend_func_extended " _string , { " GL_EXT_blend_func_extended " sv } } ,
2025-03-23 10:38:00 +00:00
{ " WEBGL_clip_cull_distance " _string , { " GL_EXT_clip_cull_distance " sv } , OpenGLContext : : WebGLVersion : : WebGL2 } ,
{ " WEBGL_color_buffer_float " _string , { " EXT_color_buffer_half_float " sv , " OES_texture_float " sv } , OpenGLContext : : WebGLVersion : : WebGL1 } ,
2025-01-17 15:42:17 +00:00
{ " WEBGL_compressed_texture_astc " _string , { " KHR_texture_compression_astc_hdr " sv , " KHR_texture_compression_astc_ldr " sv } } ,
{ " WEBGL_compressed_texture_etc " _string , { " GL_ANGLE_compressed_texture_etc " sv } } ,
{ " WEBGL_compressed_texture_etc1 " _string , { " GL_OES_compressed_ETC1_RGB8_texture " sv } } ,
{ " WEBGL_compressed_texture_pvrtc " _string , { " GL_IMG_texture_compression_pvrtc " sv } } ,
{ " WEBGL_compressed_texture_s3tc_srgb " _string , { " GL_EXT_texture_compression_s3tc_srgb " sv } } ,
{ " WEBGL_multi_draw " _string , { " GL_ANGLE_multi_draw " sv } } ,
{ " WEBGL_polygon_mode " _string , { " GL_ANGLE_polygon_mode " sv } } ,
2025-03-23 10:38:00 +00:00
{ " WEBGL_provoking_vertex " _string , { " GL_ANGLE_provoking_vertex " sv } , OpenGLContext : : WebGLVersion : : WebGL2 } ,
{ " WEBGL_render_shared_exponent " _string , { " GL_QCOM_render_shared_exponent " sv } , OpenGLContext : : WebGLVersion : : WebGL2 } ,
{ " WEBGL_stencil_texturing " _string , { " GL_ANGLE_stencil_texturing " sv } , OpenGLContext : : WebGLVersion : : WebGL2 } ,
2024-12-10 05:49:33 +01:00
} ;
Vector < String > OpenGLContext : : get_supported_extensions ( )
{
2025-08-19 12:07:03 -04:00
# ifdef ENABLE_WEBGL
2025-01-17 15:42:17 +00:00
if ( m_requestable_extensions . has_value ( ) )
return m_requestable_extensions . value ( ) ;
2024-12-10 05:49:33 +01:00
make_current ( ) ;
2025-01-17 15:42:17 +00:00
auto const * requestable_extensions_string = reinterpret_cast < char const * > ( glGetString ( GL_REQUESTABLE_EXTENSIONS_ANGLE ) ) ;
StringView requestable_extensions_view ( requestable_extensions_string , strlen ( requestable_extensions_string ) ) ;
auto requestable_extensions = requestable_extensions_view . split_view ( ' ' ) ;
2024-12-10 05:49:33 +01:00
Vector < String > extensions ;
2025-01-17 15:42:17 +00:00
for ( auto const & available_extension : s_available_webgl_extensions ) {
2025-03-23 10:38:00 +00:00
bool supported = ! available_extension . only_for_webgl_version . has_value ( )
| | m_webgl_version = = available_extension . only_for_webgl_version ;
if ( supported ) {
for ( auto const & required_extension : available_extension . required_angle_extensions ) {
auto maybe_required_extension = requestable_extensions . find_if ( [ & ] ( StringView requestable_extension ) {
return required_extension = = requestable_extension ;
} ) ;
if ( maybe_required_extension = = requestable_extensions . end ( ) ) {
supported = false ;
break ;
}
2025-01-17 15:42:17 +00:00
}
}
if ( supported )
extensions . append ( available_extension . webgl_extension_name ) ;
2024-12-10 05:49:33 +01:00
}
2025-01-17 15:42:17 +00:00
// We must cache this, because once extensions have been requested, they're no longer requestable extensions and would
// not appear in this list. However, we must always report every supported extension, regardless of what has already
// been requested.
m_requestable_extensions = extensions ;
2024-12-10 05:49:33 +01:00
return extensions ;
2025-08-19 12:07:03 -04:00
# else
( void ) m_webgl_version ;
return { } ;
# endif
2024-12-10 05:49:33 +01:00
}
2025-01-10 12:55:47 +00:00
void OpenGLContext : : request_extension ( char const * extension_name )
{
2025-08-19 12:07:03 -04:00
# ifdef ENABLE_WEBGL
2025-01-10 12:55:47 +00:00
make_current ( ) ;
glRequestExtensionANGLE ( extension_name ) ;
2025-08-19 12:07:03 -04:00
# else
( void ) extension_name ;
# endif
2025-01-10 12:55:47 +00:00
}
2024-01-18 20:29:09 +01:00
}