| /* | |
| * 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 |