From 271c9d1ae94cc5cf19ad376817f6b340a69cd580 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Thu, 18 Jan 2024 20:29:09 +0100 Subject: [PATCH] LibWeb: Use platform's OpenGL in WebGL when it is available This change makes WebGL to use LibGL only in SerenityOS, and the platform's OpenGL driver in Ladybird if it is available. This is implemented by introducing wrapper class between WebGL and OpenGL calls. This way it will also be possible to provide more complete support in Ladybird even if we don't yet have all needed calls implemented in LibGL. For now, the wrapper class makes all GL calls virtual. However, we can get rid of this and implement it at compile time in case of performance problems. --- Userland/Libraries/LibWeb/CMakeLists.txt | 9 +- .../LibWeb/HTML/Canvas/CanvasPath.cpp | 1 + .../Libraries/LibWeb/WebGL/OpenGLContext.cpp | 360 ++++++++++++++++++ .../Libraries/LibWeb/WebGL/OpenGLContext.h | 47 +++ Userland/Libraries/LibWeb/WebGL/Types.h | 9 +- .../LibWeb/WebGL/WebGLRenderingContext.cpp | 11 +- .../LibWeb/WebGL/WebGLRenderingContext.h | 4 +- .../WebGL/WebGLRenderingContextBase.cpp | 36 +- .../LibWeb/WebGL/WebGLRenderingContextBase.h | 9 +- 9 files changed, 449 insertions(+), 37 deletions(-) create mode 100644 Userland/Libraries/LibWeb/WebGL/OpenGLContext.cpp create mode 100644 Userland/Libraries/LibWeb/WebGL/OpenGLContext.h diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 1d15e7c90d..5e27306da1 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -621,6 +621,7 @@ set(SOURCES WebDriver/Screenshot.cpp WebDriver/TimeoutsConfiguration.cpp WebGL/EventNames.cpp + WebGL/OpenGLContext.cpp WebGL/WebGLContextAttributes.cpp WebGL/WebGLContextEvent.cpp WebGL/WebGLRenderingContext.cpp @@ -679,12 +680,18 @@ set(GENERATED_SOURCES serenity_lib(LibWeb web) # NOTE: We link with LibSoftGPU here instead of lazy loading it via dlopen() so that we do not have to unveil the library and pledge prot_exec. -target_link_libraries(LibWeb PRIVATE LibCore LibCrypto LibJS LibMarkdown LibHTTP LibGemini LibGL LibGUI LibGfx LibIPC LibLocale LibRegex LibSoftGPU LibSyntax LibTextCodec LibUnicode LibAudio LibVideo LibWasm LibXML LibIDL) +target_link_libraries(LibWeb PRIVATE LibCore LibCrypto LibJS LibMarkdown LibHTTP LibGemini LibGUI LibGfx LibIPC LibLocale LibRegex LibSoftGPU LibSyntax LibTextCodec LibUnicode LibAudio LibVideo LibWasm LibXML LibIDL) link_with_locale_data(LibWeb) if (HAS_ACCELERATED_GRAPHICS) + target_link_libraries(LibWeb PRIVATE ${ACCEL_GFX_LIBS}) target_sources(LibWeb PRIVATE Painting/PaintingCommandExecutorGPU.cpp) target_link_libraries(LibWeb PRIVATE LibAccelGfx) + target_compile_definitions(LibWeb PRIVATE HAS_ACCELERATED_GRAPHICS) +endif() + +if (SERENITYOS) + target_link_libraries(LibWeb PRIVATE LibGL) endif() generate_js_bindings(LibWeb) diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.cpp b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.cpp index 2e587d2f05..283570ff5c 100644 --- a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.cpp +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.cpp @@ -5,6 +5,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include namespace Web::HTML { diff --git a/Userland/Libraries/LibWeb/WebGL/OpenGLContext.cpp b/Userland/Libraries/LibWeb/WebGL/OpenGLContext.cpp new file mode 100644 index 0000000000..1b458f2f8f --- /dev/null +++ b/Userland/Libraries/LibWeb/WebGL/OpenGLContext.cpp @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +#ifdef HAS_ACCELERATED_GRAPHICS +# include +# include +#elif defined(AK_OS_SERENITY) +# include +#endif + +namespace Web::WebGL { + +#ifdef HAS_ACCELERATED_GRAPHICS +class AccelGfxContext : public OpenGLContext { +public: + virtual void activate() override + { + m_context->activate(); + } + + virtual void present(Gfx::Bitmap& bitmap) override + { + VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRA8888); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glReadPixels(0, 0, bitmap.width(), bitmap.height(), GL_BGRA, GL_UNSIGNED_BYTE, bitmap.scanline(0)); + } + + virtual GLenum gl_get_error() override + { + activate(); + return glGetError(); + } + + virtual void gl_get_doublev(GLenum pname, GLdouble* params) override + { + activate(); + glGetDoublev(pname, params); + } + + virtual void gl_get_integerv(GLenum pname, GLint* params) override + { + activate(); + glGetIntegerv(pname, params); + } + + virtual void gl_clear(GLbitfield mask) override + { + activate(); + glClear(mask); + } + + virtual void gl_clear_color(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) override + { + activate(); + glClearColor(red, green, blue, alpha); + } + + virtual void gl_clear_depth(GLdouble depth) override + { + activate(); + glClearDepth(depth); + } + + virtual void gl_clear_stencil(GLint s) override + { + activate(); + glClearStencil(s); + } + + virtual void gl_active_texture(GLenum texture) override + { + activate(); + glActiveTexture(texture); + } + + virtual void gl_viewport(GLint x, GLint y, GLsizei width, GLsizei height) override + { + activate(); + glViewport(x, y, width, height); + } + + virtual void gl_line_width(GLfloat width) override + { + activate(); + glLineWidth(width); + } + + virtual void gl_polygon_offset(GLfloat factor, GLfloat units) override + { + activate(); + glPolygonOffset(factor, units); + } + + virtual void gl_scissor(GLint x, GLint y, GLsizei width, GLsizei height) override + { + activate(); + glScissor(x, y, width, height); + } + + virtual void gl_depth_mask(GLboolean mask) override + { + activate(); + glDepthMask(mask); + } + + virtual void gl_depth_func(GLenum func) override + { + activate(); + glDepthFunc(func); + } + + virtual void gl_depth_range(GLdouble z_near, GLdouble z_far) override + { + activate(); + glDepthRange(z_near, z_far); + } + + virtual void gl_cull_face(GLenum mode) override + { + activate(); + glCullFace(mode); + } + + virtual void gl_color_mask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) override + { + activate(); + glColorMask(red, green, blue, alpha); + } + + virtual void gl_front_face(GLenum mode) override + { + activate(); + glFrontFace(mode); + } + + virtual void gl_finish() override + { + activate(); + glFinish(); + } + + virtual void gl_flush() override + { + activate(); + glFlush(); + } + + virtual void gl_stencil_op_separate(GLenum, GLenum, GLenum, GLenum) override + { + TODO(); + } + + AccelGfxContext(OwnPtr context, NonnullRefPtr canvas) + : m_context(move(context)) + , m_canvas(move(canvas)) + { + } + +private: + OwnPtr m_context; + NonnullRefPtr m_canvas; +}; +#endif + +#ifdef AK_OS_SERENITY +class LibGLContext : public OpenGLContext { +public: + virtual void activate() override + { + GL::make_context_current(m_context); + } + + virtual void present(Gfx::Bitmap&) override + { + m_context->present(); + } + + virtual GLenum gl_get_error() override + { + return m_context->gl_get_error(); + } + + virtual void gl_get_doublev(GLenum pname, GLdouble* params) override + { + m_context->gl_get_doublev(pname, params); + } + + virtual void gl_get_integerv(GLenum pname, GLint* params) override + { + m_context->gl_get_integerv(pname, params); + } + + virtual void gl_clear(GLbitfield mask) override + { + m_context->gl_clear(mask); + } + + virtual void gl_clear_color(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) override + { + m_context->gl_clear_color(red, green, blue, alpha); + } + + virtual void gl_clear_depth(GLdouble depth) override + { + m_context->gl_clear_depth(depth); + } + + virtual void gl_clear_stencil(GLint s) override + { + m_context->gl_clear_stencil(s); + } + + virtual void gl_active_texture(GLenum texture) override + { + m_context->gl_active_texture(texture); + } + + virtual void gl_viewport(GLint x, GLint y, GLsizei width, GLsizei height) override + { + m_context->gl_viewport(x, y, width, height); + } + + virtual void gl_line_width(GLfloat width) override + { + m_context->gl_line_width(width); + } + + virtual void gl_polygon_offset(GLfloat factor, GLfloat units) override + { + m_context->gl_polygon_offset(factor, units); + } + + virtual void gl_scissor(GLint x, GLint y, GLsizei width, GLsizei height) override + { + m_context->gl_scissor(x, y, width, height); + } + + virtual void gl_depth_mask(GLboolean flag) override + { + m_context->gl_depth_mask(flag); + } + + virtual void gl_depth_func(GLenum func) override + { + m_context->gl_depth_func(func); + } + + virtual void gl_depth_range(GLdouble z_near, GLdouble z_far) override + { + m_context->gl_depth_range(z_near, z_far); + } + + virtual void gl_cull_face(GLenum mode) override + { + m_context->gl_cull_face(mode); + } + + virtual void gl_color_mask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) override + { + m_context->gl_color_mask(red, green, blue, alpha); + } + + virtual void gl_front_face(GLenum mode) override + { + m_context->gl_front_face(mode); + } + + virtual void gl_finish() override + { + m_context->gl_finish(); + } + + virtual void gl_flush() override + { + m_context->gl_flush(); + } + + virtual void gl_stencil_op_separate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) override + { + m_context->gl_stencil_op_separate(face, sfail, dpfail, dppass); + } + + LibGLContext(OwnPtr context) + : m_context(move(context)) + { + } + +private: + OwnPtr m_context; +}; +#endif + +#ifdef HAS_ACCELERATED_GRAPHICS +static OwnPtr make_accelgfx_context(Gfx::Bitmap& bitmap) +{ + auto context = AccelGfx::Context::create(); + auto canvas = AccelGfx::Canvas::create(bitmap.size()); + canvas->bind(); + return make(move(context), move(canvas)); +} +#endif + +#ifdef AK_OS_SERENITY +static OwnPtr make_libgl_context(Gfx::Bitmap& bitmap) +{ + auto context_or_error = GL::create_context(bitmap); + return make(move(context_or_error.value())); +} +#endif + +OwnPtr OpenGLContext::create(Gfx::Bitmap& bitmap) +{ +#ifdef HAS_ACCELERATED_GRAPHICS + return make_accelgfx_context(bitmap); +#elif defined(AK_OS_SERENITY) + return make_libgl_context(bitmap); +#endif + + (void)bitmap; + return {}; +} + +void OpenGLContext::clear_buffer_to_default_values() +{ +#if defined(HAS_ACCELERATED_GRAPHICS) || defined(AK_OS_SERENITY) + Array current_clear_color; + gl_get_doublev(GL_COLOR_CLEAR_VALUE, current_clear_color.data()); + + GLdouble current_clear_depth; + gl_get_doublev(GL_DEPTH_CLEAR_VALUE, ¤t_clear_depth); + + GLint current_clear_stencil; + gl_get_integerv(GL_STENCIL_CLEAR_VALUE, ¤t_clear_stencil); + + // The implicit clear value for the color buffer is (0, 0, 0, 0) + gl_clear_color(0, 0, 0, 0); + + // The implicit clear value for the depth buffer is 1.0. + gl_clear_depth(1.0); + + // The implicit clear value for the stencil buffer is 0. + gl_clear_stencil(0); + + gl_clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + // Restore the clear values. + gl_clear_color(current_clear_color[0], current_clear_color[1], current_clear_color[2], current_clear_color[3]); + gl_clear_depth(current_clear_depth); + gl_clear_stencil(current_clear_stencil); +#endif +} + +} diff --git a/Userland/Libraries/LibWeb/WebGL/OpenGLContext.h b/Userland/Libraries/LibWeb/WebGL/OpenGLContext.h new file mode 100644 index 0000000000..e319657600 --- /dev/null +++ b/Userland/Libraries/LibWeb/WebGL/OpenGLContext.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::WebGL { + +class OpenGLContext { +public: + static OwnPtr create(Gfx::Bitmap&); + + virtual void activate() = 0; + virtual void present(Gfx::Bitmap&) = 0; + void clear_buffer_to_default_values(); + + virtual GLenum gl_get_error() = 0; + virtual void gl_get_doublev(GLenum, GLdouble*) = 0; + virtual void gl_get_integerv(GLenum, GLint*) = 0; + virtual void gl_clear(GLbitfield) = 0; + virtual void gl_clear_color(GLfloat, GLfloat, GLfloat, GLfloat) = 0; + virtual void gl_clear_depth(GLdouble) = 0; + virtual void gl_clear_stencil(GLint) = 0; + virtual void gl_active_texture(GLenum) = 0; + virtual void gl_viewport(GLint, GLint, GLsizei, GLsizei) = 0; + virtual void gl_line_width(GLfloat) = 0; + virtual void gl_polygon_offset(GLfloat, GLfloat) = 0; + virtual void gl_scissor(GLint, GLint, GLsizei, GLsizei) = 0; + virtual void gl_depth_mask(GLboolean) = 0; + virtual void gl_depth_func(GLenum) = 0; + virtual void gl_depth_range(GLdouble, GLdouble) = 0; + virtual void gl_cull_face(GLenum) = 0; + virtual void gl_color_mask(GLboolean, GLboolean, GLboolean, GLboolean) = 0; + virtual void gl_front_face(GLenum) = 0; + virtual void gl_finish() = 0; + virtual void gl_flush() = 0; + virtual void gl_stencil_op_separate(GLenum, GLenum, GLenum, GLenum) = 0; + + virtual ~OpenGLContext() { } +}; + +} diff --git a/Userland/Libraries/LibWeb/WebGL/Types.h b/Userland/Libraries/LibWeb/WebGL/Types.h index 0d4df0e70f..36c8ae2f44 100644 --- a/Userland/Libraries/LibWeb/WebGL/Types.h +++ b/Userland/Libraries/LibWeb/WebGL/Types.h @@ -6,4 +6,11 @@ #pragma once -// FIXME: This header is here just to satisfy the IDL code generator. +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef int GLint; +typedef int GLsizei; +typedef float GLfloat; +typedef double GLdouble; +typedef GLfloat GLclampf; +typedef unsigned int GLbitfield; diff --git a/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp b/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp index afeef838dd..a8d92f8741 100644 --- a/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp +++ b/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp @@ -44,15 +44,18 @@ JS::ThrowCompletionOr> WebGLRenderingContext::c return JS::GCPtr { nullptr }; } - auto context_or_error = GL::create_context(*canvas_element.bitmap()); - if (context_or_error.is_error()) { + VERIFY(canvas_element.bitmap()); + auto context = OpenGLContext::create(*canvas_element.bitmap()); + + if (!context) { fire_webgl_context_creation_error(canvas_element); return JS::GCPtr { nullptr }; } - return realm.heap().allocate(realm, realm, canvas_element, context_or_error.release_value(), context_attributes, context_attributes); + + return realm.heap().allocate(realm, realm, canvas_element, context.release_nonnull(), context_attributes, context_attributes); } -WebGLRenderingContext::WebGLRenderingContext(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters) +WebGLRenderingContext::WebGLRenderingContext(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters) : WebGLRenderingContextBase(realm, canvas_element, move(context), move(context_creation_parameters), move(actual_context_parameters)) { } diff --git a/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContext.h b/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContext.h index 3312e453cc..d504382ada 100644 --- a/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContext.h +++ b/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContext.h @@ -1,11 +1,13 @@ /* * Copyright (c) 2022, Luke Wilde + * Copyright (c) 2024, Aliaksandr Kalenik * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include #include #include #include @@ -24,7 +26,7 @@ public: private: virtual void initialize(JS::Realm&) override; - WebGLRenderingContext(JS::Realm&, HTML::HTMLCanvasElement&, NonnullOwnPtr context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters); + WebGLRenderingContext(JS::Realm&, HTML::HTMLCanvasElement&, NonnullOwnPtr context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters); }; } diff --git a/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp b/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp index ea898634de..a2e59ad990 100644 --- a/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp +++ b/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp @@ -1,11 +1,11 @@ /* * Copyright (c) 2022, Luke Wilde + * Copyright (c) 2024, Aliaksandr Kalenik * * SPDX-License-Identifier: BSD-2-Clause */ #include -#include #include #include #include @@ -13,7 +13,12 @@ namespace Web::WebGL { -WebGLRenderingContextBase::WebGLRenderingContextBase(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters) +// FIXME: Replace with constants defined in WebGL spec. +#define GL_INVALID_OPERATION 0x0502 +#define GL_INVALID_VALUE 0x0501 +#define GL_FRONT_AND_BACK 0x0408 + +WebGLRenderingContextBase::WebGLRenderingContextBase(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters) : PlatformObject(realm) , m_canvas_element(canvas_element) , m_context(move(context)) @@ -48,36 +53,13 @@ void WebGLRenderingContextBase::present() // FIXME: Is this the operation it means? m_context->gl_flush(); - m_context->present(); + m_context->present(*canvas_element().bitmap()); // "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 (!m_context_creation_parameters.preserve_drawing_buffer) { - Array current_clear_color; - m_context->gl_get_doublev(GL_COLOR_CLEAR_VALUE, current_clear_color.data()); - - GLdouble current_clear_depth; - m_context->gl_get_doublev(GL_DEPTH_CLEAR_VALUE, ¤t_clear_depth); - - GLint current_clear_stencil; - m_context->gl_get_integerv(GL_STENCIL_CLEAR_VALUE, ¤t_clear_stencil); - - // The implicit clear value for the color buffer is (0, 0, 0, 0) - m_context->gl_clear_color(0, 0, 0, 0); - - // The implicit clear value for the depth buffer is 1.0. - m_context->gl_clear_depth(1.0); - - // The implicit clear value for the stencil buffer is 0. - m_context->gl_clear_stencil(0); - - m_context->gl_clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - - // Restore the clear values. - m_context->gl_clear_color(current_clear_color[0], current_clear_color[1], current_clear_color[2], current_clear_color[3]); - m_context->gl_clear_depth(current_clear_depth); - m_context->gl_clear_stencil(current_clear_stencil); + m_context->clear_buffer_to_default_values(); } } diff --git a/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h b/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h index 1027923fd4..14e9b20d65 100644 --- a/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h +++ b/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, Luke Wilde + * Copyright (c) 2024, Aliaksandr Kalenik * * SPDX-License-Identifier: BSD-2-Clause */ @@ -9,14 +10,16 @@ #include #include #include -#include #include #include #include +#include #include namespace Web::WebGL { +#define GL_NO_ERROR 0 + class WebGLRenderingContextBase : public Bindings::PlatformObject { WEB_PLATFORM_OBJECT(WebGLRenderingContextBase, Bindings::PlatformObject); @@ -64,14 +67,14 @@ public: void viewport(GLint x, GLint y, GLsizei width, GLsizei height); protected: - WebGLRenderingContextBase(JS::Realm&, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters); + WebGLRenderingContextBase(JS::Realm&, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters); private: virtual void visit_edges(Cell::Visitor&) override; JS::NonnullGCPtr m_canvas_element; - NonnullOwnPtr m_context; + NonnullOwnPtr m_context; // https://www.khronos.org/registry/webgl/specs/latest/1.0/#context-creation-parameters // Each WebGLRenderingContext has context creation parameters, set upon creation, in a WebGLContextAttributes object.