diff --git a/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp b/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp index a18dc3ca0bc..1f66256dc10 100644 --- a/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp @@ -121,8 +121,8 @@ void HTMLCanvasElement::reset_context_to_default_state() [](GC::Ref& context) { context->reset_to_default_state(); }, - [](GC::Ref&) { - TODO(); + [](GC::Ref& context) { + context->reset_to_default_state(); }, [](Empty) { // Do nothing. @@ -135,8 +135,8 @@ void HTMLCanvasElement::notify_context_about_canvas_size_change() [&](GC::Ref& context) { context->set_size(bitmap_size_for_canvas()); }, - [&](GC::Ref&) { - TODO(); + [&](GC::Ref& context) { + context->set_size(bitmap_size_for_canvas()); }, [](Empty) { // Do nothing. @@ -391,8 +391,8 @@ RefPtr HTMLCanvasElement::surface() const [&](GC::Ref const& context) { return context->surface(); }, - [&](GC::Ref const&) -> RefPtr { - TODO(); + [&](GC::Ref const& context) -> RefPtr { + return context->surface(); }, [](Empty) -> RefPtr { return {}; @@ -405,8 +405,8 @@ void HTMLCanvasElement::allocate_painting_surface_if_needed() [&](GC::Ref& context) { context->allocate_painting_surface_if_needed(); }, - [&](GC::Ref&) { - TODO(); + [&](GC::Ref& context) { + context->allocate_painting_surface_if_needed(); }, [](Empty) { // Do nothing. diff --git a/Libraries/LibWeb/WebGL/OpenGLContext.cpp b/Libraries/LibWeb/WebGL/OpenGLContext.cpp index d967293015b..44bfa62c6ed 100644 --- a/Libraries/LibWeb/WebGL/OpenGLContext.cpp +++ b/Libraries/LibWeb/WebGL/OpenGLContext.cpp @@ -5,17 +5,197 @@ */ #include +#include +#include #include +#ifdef AK_OS_MACOS +# include +# include +# include +# include +# include +# include +#endif + namespace Web::WebGL { -OwnPtr OpenGLContext::create() +struct OpenGLContext::Impl { +#ifdef AK_OS_MACOS + EGLDisplay display { nullptr }; + EGLConfig config { nullptr }; + EGLContext context { nullptr }; + EGLSurface surface { nullptr }; + + GLuint framebuffer { 0 }; + GLuint depth_buffer { 0 }; +#endif +}; + +OpenGLContext::OpenGLContext(NonnullRefPtr skia_backend_context, Impl impl) + : m_skia_backend_context(move(skia_backend_context)) + , m_impl(make(impl)) { - return {}; +} + +OpenGLContext::~OpenGLContext() +{ +#ifdef AK_OS_MACOS + eglMakeCurrent(m_impl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + glDeleteFramebuffers(1, &m_impl->framebuffer); + glDeleteRenderbuffers(1, &m_impl->depth_buffer); + eglDestroyContext(m_impl->display, m_impl->context); + eglDestroySurface(m_impl->display, m_impl->surface); +#endif +} + +#ifdef AK_OS_MACOS +static EGLConfig get_egl_config(EGLDisplay display) +{ + EGLint const config_attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + 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 configs; + configs.resize(number_of_configs); + eglChooseConfig(display, config_attribs, configs.data(), number_of_configs, &number_of_configs); + return configs[0]; +} +#endif + +OwnPtr OpenGLContext::create(NonnullRefPtr skia_backend_context) +{ +#ifdef AK_OS_MACOS + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + 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); + + EGLint context_attributes[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attributes); + if (context == EGL_NO_CONTEXT) { + dbgln("Failed to create EGL context"); + return {}; + } + + return make(skia_backend_context, Impl { .display = display, .config = config, .context = context }); +#else + (void)skia_backend_context; + return nullptr; +#endif } void OpenGLContext::clear_buffer_to_default_values() { } +void OpenGLContext::allocate_painting_surface_if_needed() +{ +#ifdef AK_OS_MACOS + if (m_painting_surface) + return; + + VERIFY(!m_size.is_empty()); + + auto iosurface = Core::IOSurfaceHandle::create(m_size.width(), m_size.height()); + m_painting_surface = Gfx::PaintingSurface::wrap_iosurface(iosurface, m_skia_backend_context); + + auto width = m_size.width(); + auto height = m_size.height(); + + auto* display = m_impl->display; + 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, + EGL_HEIGHT, + height, + EGL_IOSURFACE_PLANE_ANGLE, + 0, + EGL_TEXTURE_TARGET, + target, + 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, + }; + m_impl->surface = eglCreatePbufferFromClientBuffer(display, EGL_IOSURFACE_ANGLE, iosurface.core_foundation_pointer(), config, surface_attributes); + + eglMakeCurrent(m_impl->display, m_impl->surface, m_impl->surface, m_impl->context); + + EGLint texture_target_angle = 0; + eglGetConfigAttrib(display, config, EGL_BIND_TO_TEXTURE_TARGET_ANGLE, &texture_target_angle); + VERIFY(texture_target_angle == EGL_TEXTURE_RECTANGLE_ANGLE); + + GLuint texture = 0; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_RECTANGLE_ANGLE, texture); + auto result = eglBindTexImage(display, m_impl->surface, EGL_BACK_BUFFER); + VERIFY(result == EGL_TRUE); + + glGenFramebuffers(1, &m_impl->framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, m_impl->framebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ANGLE, texture, 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); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_impl->depth_buffer); +#endif +} + +void OpenGLContext::set_size(Gfx::IntSize const& size) +{ + if (m_size != size) { + m_painting_surface = nullptr; + } + m_size = size; +} + +void OpenGLContext::make_current() +{ +#ifdef AK_OS_MACOS + allocate_painting_surface_if_needed(); + eglMakeCurrent(m_impl->display, m_impl->surface, m_impl->surface, m_impl->context); +#endif +} + +RefPtr OpenGLContext::surface() +{ + return m_painting_surface; +} + } diff --git a/Libraries/LibWeb/WebGL/OpenGLContext.h b/Libraries/LibWeb/WebGL/OpenGLContext.h index 1c663711b1e..c5554816f4f 100644 --- a/Libraries/LibWeb/WebGL/OpenGLContext.h +++ b/Libraries/LibWeb/WebGL/OpenGLContext.h @@ -6,19 +6,34 @@ #pragma once -#include -#include +#include +#include namespace Web::WebGL { class OpenGLContext { public: - static OwnPtr create(); + static OwnPtr create(NonnullRefPtr); - virtual void present() = 0; void clear_buffer_to_default_values(); + void allocate_painting_surface_if_needed(); - virtual ~OpenGLContext() { } + struct Impl; + OpenGLContext(NonnullRefPtr, Impl); + + ~OpenGLContext(); + + void make_current(); + + void set_size(Gfx::IntSize const&); + + RefPtr surface(); + +private: + NonnullRefPtr m_skia_backend_context; + Gfx::IntSize m_size; + RefPtr m_painting_surface; + NonnullOwnPtr m_impl; }; } diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp b/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp index ed2ca686671..d75f84166f8 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp @@ -57,13 +57,16 @@ JS::ThrowCompletionOr> WebGLRenderingContext::cre // We should be coming here from getContext being called on a wrapped element. auto context_attributes = TRY(convert_value_to_context_attributes_dictionary(canvas_element.vm(), options)); - auto context = OpenGLContext::create(); + auto skia_backend_context = canvas_element.navigable()->traversable_navigable()->skia_backend_context(); + auto context = OpenGLContext::create(*skia_backend_context); if (!context) { fire_webgl_context_creation_error(canvas_element); return GC::Ptr { nullptr }; } + context->set_size(canvas_element.bitmap_size_for_canvas(1, 1)); + return realm.create(realm, canvas_element, context.release_nonnull(), context_attributes, context_attributes); } @@ -137,9 +140,9 @@ bool WebGLRenderingContext::is_context_lost() const return m_context_lost; } -void WebGLRenderingContext::set_size(Gfx::IntSize const&) +void WebGLRenderingContext::set_size(Gfx::IntSize const& size) { - TODO(); + m_context->set_size(size); } void WebGLRenderingContext::reset_to_default_state() @@ -148,7 +151,12 @@ void WebGLRenderingContext::reset_to_default_state() RefPtr WebGLRenderingContext::surface() { - TODO(); + return m_context->surface(); +} + +void WebGLRenderingContext::allocate_painting_surface_if_needed() +{ + m_context->allocate_painting_surface_if_needed(); } Optional> WebGLRenderingContext::get_supported_extensions() const diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContext.h b/Libraries/LibWeb/WebGL/WebGLRenderingContext.h index a1c48fd373f..655810fdbe2 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContext.h +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContext.h @@ -33,6 +33,7 @@ public: bool is_context_lost() const; RefPtr surface(); + void allocate_painting_surface_if_needed(); void set_size(Gfx::IntSize const&); void reset_to_default_state();