ladybird/Userland/Libraries/LibWeb/WebGL/OpenGLContext.cpp
Aliaksandr Kalenik 980b61470c LibAccelGfx+LibWeb+WebContent: Handle OpenGL Context init errors
Now, if OpenGL context fails, an error will be returned and a message
printed, instead of crashing.
2024-01-24 19:43:51 +01:00

365 lines
8.6 KiB
C++

/*
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/OwnPtr.h>
#include <LibGfx/Bitmap.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#ifdef HAS_ACCELERATED_GRAPHICS
# include <LibAccelGfx/Canvas.h>
# include <LibAccelGfx/Context.h>
#elif defined(AK_OS_SERENITY)
# include <LibGL/GLContext.h>
#endif
namespace Web::WebGL {
#ifdef HAS_ACCELERATED_GRAPHICS
class AccelGfxContext : public OpenGLContext {
public:
void activate()
{
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(NonnullOwnPtr<AccelGfx::Context> context, NonnullRefPtr<AccelGfx::Canvas> canvas)
: m_context(move(context))
, m_canvas(move(canvas))
{
}
~AccelGfxContext()
{
activate();
}
private:
NonnullOwnPtr<AccelGfx::Context> m_context;
NonnullRefPtr<AccelGfx::Canvas> m_canvas;
};
#endif
#ifdef AK_OS_SERENITY
class LibGLContext : public OpenGLContext {
public:
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<GL::GLContext> context)
: m_context(move(context))
{
}
private:
OwnPtr<GL::GLContext> m_context;
};
#endif
#ifdef HAS_ACCELERATED_GRAPHICS
static OwnPtr<AccelGfxContext> make_accelgfx_context(Gfx::Bitmap& bitmap)
{
auto context = AccelGfx::Context::create();
if (context.is_error()) {
dbgln("Failed to create AccelGfx context: {}", context.error().string_literal());
return {};
}
auto canvas = AccelGfx::Canvas::create(bitmap.size());
canvas->bind();
return make<AccelGfxContext>(context.release_value(), move(canvas));
}
#endif
#ifdef AK_OS_SERENITY
static OwnPtr<LibGLContext> make_libgl_context(Gfx::Bitmap& bitmap)
{
auto context_or_error = GL::create_context(bitmap);
return make<LibGLContext>(move(context_or_error.value()));
}
#endif
OwnPtr<OpenGLContext> 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<GLdouble, 4> 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, &current_clear_depth);
GLint current_clear_stencil;
gl_get_integerv(GL_STENCIL_CLEAR_VALUE, &current_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
}
}