blob: 4844d6de3853d8642e7ccddbc73aa28554f970a1 [file] [log] [blame]
/*
* Copyright 2023 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 "shaders/out/generated/glsl.exports.h"
namespace rive::pls
{
using DrawBufferMask = PLSRenderTargetGL::DrawBufferMask;
constexpr static GLenum kPLSDrawBuffers[4] = {GL_COLOR_ATTACHMENT0,
GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2,
GL_COLOR_ATTACHMENT3};
class PLSRenderContextGLImpl::PLSImplFramebufferFetch : public PLSRenderContextGLImpl::PLSImpl
{
public:
PLSImplFramebufferFetch(const GLCapabilities& extensions) : m_capabilities(extensions) {}
bool supportsRasterOrdering(const GLCapabilities&) const override { return true; }
void activatePixelLocalStorage(PLSRenderContextGLImpl* plsContextImpl,
const FlushDescriptor& desc) override
{
assert(plsContextImpl->m_capabilities.EXT_shader_framebuffer_fetch);
auto renderTarget = static_cast<PLSRenderTargetGL*>(desc.renderTarget);
renderTarget->allocateInternalPLSTextures(desc.interlockMode);
if (auto framebufferRenderTarget = lite_rtti_cast<FramebufferRenderTargetGL*>(renderTarget))
{
// We're targeting an external FBO directly. Allocate and attach an offscreen target
// texture since we can't modify their attachments.
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,
framebufferRenderTarget->height());
}
}
GLuint coverageClear[4]{desc.coverageClearValue};
auto fbFetchBuffers = DrawBufferMask::color;
if (desc.interlockMode == pls::InterlockMode::rasterOrdering)
{
fbFetchBuffers |= DrawBufferMask::coverage | DrawBufferMask::originalDstColor;
}
else
{
// Clear and bind the coverage buffer now, since it won't be enabled on the framebuffer.
renderTarget->bindInternalFramebuffer(GL_DRAW_FRAMEBUFFER, DrawBufferMask::coverage);
glClearBufferuiv(GL_COLOR, COVERAGE_PLANE_IDX, coverageClear);
renderTarget->bindAsImageTextures(DrawBufferMask::coverage);
}
if (desc.combinedShaderFeatures & pls::ShaderFeatures::ENABLE_CLIPPING)
{
fbFetchBuffers |= DrawBufferMask::clip;
}
renderTarget->bindInternalFramebuffer(GL_DRAW_FRAMEBUFFER, fbFetchBuffers);
// Instruct the driver not to load existing PLS plane contents into tiled memory, with the
// exception of the color buffer if it's preserved.
static_assert(FRAMEBUFFER_PLANE_IDX == 0);
glInvalidateFramebuffer(GL_FRAMEBUFFER,
desc.colorLoadAction == LoadAction::preserveRenderTarget ? 3 : 4,
desc.colorLoadAction == LoadAction::preserveRenderTarget
? kPLSDrawBuffers + 1
: kPLSDrawBuffers);
// Clear the PLS planes.
if (desc.colorLoadAction == LoadAction::clear)
{
float clearColor4f[4];
UnpackColorToRGBA32F(desc.clearColor, clearColor4f);
glClearBufferfv(GL_COLOR, FRAMEBUFFER_PLANE_IDX, clearColor4f);
}
if (fbFetchBuffers & DrawBufferMask::coverage)
{
glClearBufferuiv(GL_COLOR, COVERAGE_PLANE_IDX, coverageClear);
}
if (desc.combinedShaderFeatures & pls::ShaderFeatures::ENABLE_CLIPPING)
{
constexpr static uint32_t kZero[4]{};
glClearBufferuiv(GL_COLOR, CLIP_PLANE_IDX, kZero);
}
if (pls::ShadersEmitColorToRasterPipeline(desc.interlockMode, desc.combinedShaderFeatures))
{
plsContextImpl->state()->setBlendEquation(BlendMode::srcOver);
}
if (desc.interlockMode == pls::InterlockMode::atomics)
{
glMemoryBarrierByRegion(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
}
}
void deactivatePixelLocalStorage(PLSRenderContextGLImpl*, const FlushDescriptor& desc) override
{
if (desc.interlockMode == pls::InterlockMode::atomics)
{
glMemoryBarrierByRegion(GL_ALL_BARRIER_BITS);
}
// Instruct the driver not to flush PLS contents from tiled memory, with the exception of
// the color buffer.
static_assert(FRAMEBUFFER_PLANE_IDX == 0);
glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, kPLSDrawBuffers + 1);
if (auto framebufferRenderTarget = lite_rtti_cast<FramebufferRenderTargetGL*>(
static_cast<PLSRenderTargetGL*>(desc.renderTarget)))
{
// We rendered to an offscreen texture. Copy back to the external framebuffer.
framebufferRenderTarget->bindInternalFramebuffer(GL_READ_FRAMEBUFFER,
DrawBufferMask::color);
framebufferRenderTarget->bindDestinationFramebuffer(GL_DRAW_FRAMEBUFFER);
glutils::BlitFramebuffer(desc.renderTargetUpdateBounds,
framebufferRenderTarget->height());
}
}
void pushShaderDefines(pls::InterlockMode interlockMode,
std::vector<const char*>* defines) const override
{
defines->push_back(GLSL_PLS_IMPL_FRAMEBUFFER_FETCH);
if (interlockMode == pls::InterlockMode::atomics)
{
defines->push_back(GLSL_USING_PLS_STORAGE_TEXTURES);
}
}
void onEnableRasterOrdering(bool rasterOrderingEnabled) override
{
if (!m_capabilities.QCOM_shader_framebuffer_fetch_noncoherent)
{
return;
}
if (rasterOrderingEnabled)
{
glDisable(GL_FRAMEBUFFER_FETCH_NONCOHERENT_QCOM);
}
else
{
glEnable(GL_FRAMEBUFFER_FETCH_NONCOHERENT_QCOM);
}
}
void onBarrier(const pls::FlushDescriptor& desc) override
{
if (m_capabilities.QCOM_shader_framebuffer_fetch_noncoherent)
{
glFramebufferFetchBarrierQCOM();
}
if (desc.interlockMode == pls::InterlockMode::atomics)
{
glMemoryBarrierByRegion(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
}
}
private:
const GLCapabilities m_capabilities;
};
std::unique_ptr<PLSRenderContextGLImpl::PLSImpl> PLSRenderContextGLImpl::
MakePLSImplFramebufferFetch(const GLCapabilities& extensions)
{
return std::make_unique<PLSImplFramebufferFetch>(extensions);
}
} // namespace rive::pls