blob: 169e31fb87b58815338ce6c41b9166abd0835336 [file] [log] [blame]
/*
* Copyright 2022 Rive
*/
#include "rive/pls/gl/pls_render_context_gl_impl.hpp"
#include "rive/pls/gl/gl_utils.hpp"
#include "rive/pls/gl/pls_render_target_gl.hpp"
#include "shaders/constants.glsl"
#include "generated/shaders/glsl.exports.h"
#ifdef RIVE_WEBGL
#include <emscripten/emscripten.h>
#include <emscripten/html5.h>
EM_JS(bool,
enable_WEBGL_shader_pixel_local_storage_coherent,
(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE gl),
{
gl = GL.getContext(gl).GLctx;
gl.pls = gl["getExtension"]("WEBGL_shader_pixel_local_storage");
return Boolean(gl.pls && gl.pls["isCoherent"]());
});
EM_JS(void,
framebufferTexturePixelLocalStorageWEBGL,
(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE gl,
GLint plane,
GLuint backingtexture,
GLint level,
GLint layer),
{
const pls = GL.getContext(gl).GLctx.pls;
if (pls)
{
pls["framebufferTexturePixelLocalStorageWEBGL"](plane,
GL.textures[backingtexture],
level,
layer);
}
});
EM_JS(void,
framebufferPixelLocalClearValuefvWEBGL,
(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE gl, GLint plane, float r, float g, float b, float a),
{
const pls = GL.getContext(gl).GLctx.pls;
if (pls)
{
pls["framebufferPixelLocalClearValuefvWEBGL"](plane, [ r, g, b, a ]);
}
});
EM_JS(void,
beginPixelLocalStorageWEBGL,
(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE gl, uint32_t n, uint32_t loadopsIdx),
{
const pls = GL.getContext(gl).GLctx.pls;
if (pls)
{
pls["beginPixelLocalStorageWEBGL"](
Module["HEAPU32"].subarray(loadopsIdx, loadopsIdx + n));
}
});
EM_JS(void,
endPixelLocalStorageWEBGL,
(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE gl, uint32_t n, uint32_t storeopsIdx),
{
const pls = GL.getContext(gl).GLctx.pls;
if (pls)
{
pls["endPixelLocalStorageWEBGL"](
Module["HEAPU32"].subarray(storeopsIdx, storeopsIdx + n));
}
});
EM_JS(bool, enable_WEBGL_provoking_vertex, (EMSCRIPTEN_WEBGL_CONTEXT_HANDLE gl), {
gl = GL.getContext(gl).GLctx;
gl.pv = gl["getExtension"]("WEBGL_provoking_vertex");
return Boolean(gl.pv);
});
EM_JS(void, provokingVertexWEBGL, (EMSCRIPTEN_WEBGL_CONTEXT_HANDLE gl, GLenum provokeMode), {
const pv = GL.getContext(gl).GLctx.pv;
if (pv)
{
pv["provokingVertexWEBGL"](provokeMode);
}
});
bool webgl_enable_WEBGL_shader_pixel_local_storage_coherent()
{
return enable_WEBGL_shader_pixel_local_storage_coherent(emscripten_webgl_get_current_context());
}
bool webgl_enable_WEBGL_provoking_vertex()
{
return enable_WEBGL_provoking_vertex(emscripten_webgl_get_current_context());
}
void glFramebufferTexturePixelLocalStorageANGLE(GLint plane,
GLuint backingtexture,
GLint level,
GLint layer)
{
framebufferTexturePixelLocalStorageWEBGL(emscripten_webgl_get_current_context(),
plane,
backingtexture,
level,
layer);
}
void glFramebufferPixelLocalClearValuefvANGLE(GLint plane, const GLfloat value[4])
{
framebufferPixelLocalClearValuefvWEBGL(emscripten_webgl_get_current_context(),
plane,
value[0],
value[1],
value[2],
value[3]);
}
void glBeginPixelLocalStorageANGLE(GLsizei n, const uint32_t loadops[])
{
beginPixelLocalStorageWEBGL(emscripten_webgl_get_current_context(),
n,
reinterpret_cast<uintptr_t>(loadops) / sizeof(uint32_t));
}
void glEndPixelLocalStorageANGLE(GLsizei n, const uint32_t storeops[])
{
endPixelLocalStorageWEBGL(emscripten_webgl_get_current_context(),
n,
reinterpret_cast<uintptr_t>(storeops) / sizeof(uint32_t));
}
void glProvokingVertexANGLE(GLenum provokeMode)
{
provokingVertexWEBGL(emscripten_webgl_get_current_context(), provokeMode);
}
#endif // RIVE_WEBGL
namespace rive::pls
{
using DrawBufferMask = PLSRenderTargetGL::DrawBufferMask;
static GLenum webgl_load_op(pls::LoadAction loadAction)
{
switch (loadAction)
{
case pls::LoadAction::clear:
return GL_LOAD_OP_CLEAR_ANGLE;
case pls::LoadAction::preserveRenderTarget:
return GL_LOAD_OP_LOAD_ANGLE;
case pls::LoadAction::dontCare:
return GL_LOAD_OP_ZERO_ANGLE;
}
RIVE_UNREACHABLE();
}
class PLSRenderContextGLImpl::PLSImplWebGL : public PLSRenderContextGLImpl::PLSImpl
{
bool supportsRasterOrdering(const GLCapabilities& capabilities) const override
{
return capabilities.ANGLE_shader_pixel_local_storage_coherent;
}
void activatePixelLocalStorage(PLSRenderContextGLImpl* plsContextImpl,
const FlushDescriptor& desc) override
{
auto renderTarget = static_cast<PLSRenderTargetGL*>(desc.renderTarget);
renderTarget->allocateInternalPLSTextures(desc.interlockMode);
auto framebufferRenderTarget = lite_rtti_cast<FramebufferRenderTargetGL*>(renderTarget);
if (framebufferRenderTarget != nullptr)
{
// We're targeting an external FBO directly. Make sure to allocate and attach an
// offscreen target texture.
framebufferRenderTarget->allocateOffscreenTargetTexture();
if (desc.colorLoadAction == LoadAction::preserveRenderTarget)
{
// Copy the framebuffer's contents to our offscreen texture.
framebufferRenderTarget->bindDestinationFramebuffer(GL_READ_FRAMEBUFFER);
framebufferRenderTarget->bindInternalFramebuffer(GL_DRAW_FRAMEBUFFER,
DrawBufferMask::color);
glutils::BlitFramebuffer(desc.renderTargetUpdateBounds, renderTarget->height());
}
}
// Begin pixel local storage.
renderTarget->bindHeadlessFramebuffer(plsContextImpl->m_capabilities);
if (desc.colorLoadAction == LoadAction::clear)
{
float clearColor4f[4];
UnpackColorToRGBA32F(desc.clearColor, clearColor4f);
glFramebufferPixelLocalClearValuefvANGLE(COLOR_PLANE_IDX, clearColor4f);
}
GLenum clipLoadAction = (desc.combinedShaderFeatures & pls::ShaderFeatures::ENABLE_CLIPPING)
? GL_LOAD_OP_ZERO_ANGLE
: GL_DONT_CARE;
GLenum loadOps[4] = {webgl_load_op(desc.colorLoadAction),
clipLoadAction,
GL_DONT_CARE,
GL_LOAD_OP_ZERO_ANGLE};
static_assert(COLOR_PLANE_IDX == 0);
static_assert(CLIP_PLANE_IDX == 1);
static_assert(SCRATCH_COLOR_PLANE_IDX == 2);
static_assert(COVERAGE_PLANE_IDX == 3);
glBeginPixelLocalStorageANGLE(4, loadOps);
}
void deactivatePixelLocalStorage(PLSRenderContextGLImpl*, const FlushDescriptor& desc) override
{
constexpr static GLenum kStoreOps[4] = {GL_STORE_OP_STORE_ANGLE,
GL_DONT_CARE,
GL_DONT_CARE,
GL_DONT_CARE};
static_assert(COLOR_PLANE_IDX == 0);
static_assert(CLIP_PLANE_IDX == 1);
static_assert(SCRATCH_COLOR_PLANE_IDX == 2);
static_assert(COVERAGE_PLANE_IDX == 3);
glEndPixelLocalStorageANGLE(4, kStoreOps);
if (auto framebufferRenderTarget = lite_rtti_cast<FramebufferRenderTargetGL*>(
static_cast<PLSRenderTargetGL*>(desc.renderTarget)))
{
// We rendered to an offscreen texture. Copy back to the external target FBO.
framebufferRenderTarget->bindInternalFramebuffer(GL_READ_FRAMEBUFFER,
DrawBufferMask::color);
framebufferRenderTarget->bindDestinationFramebuffer(GL_DRAW_FRAMEBUFFER);
glutils::BlitFramebuffer(desc.renderTargetUpdateBounds,
framebufferRenderTarget->height());
}
}
void pushShaderDefines(pls::InterlockMode, std::vector<const char*>* defines) const override
{
defines->push_back(GLSL_PLS_IMPL_ANGLE);
}
};
std::unique_ptr<PLSRenderContextGLImpl::PLSImpl> PLSRenderContextGLImpl::MakePLSImplWebGL()
{
return std::make_unique<PLSImplWebGL>();
}
} // namespace rive::pls