| /* |
| * Copyright 2024 Rive |
| */ |
| |
| #include "gm.hpp" |
| #include "gmutils.hpp" |
| #include "common/testing_window.hpp" |
| #include "common/offscreen_render_target.hpp" |
| #include "rive/renderer/render_context.hpp" |
| #include "rive/renderer/rive_renderer.hpp" |
| |
| #ifdef RIVE_TOOLS_NO_GL |
| using GLuint = uint32_t; |
| #else |
| #include "rive/renderer/gl/render_context_gl_impl.hpp" |
| #include "rive/renderer/gl/render_target_gl.hpp" |
| #endif |
| |
| using namespace rivegm; |
| using namespace rive; |
| using namespace rive::gpu; |
| |
| // Most gms render directly to the framebuffer. This GM checks that texture |
| // targets work in GL. |
| class OffscreenRenderTarget : public GM |
| { |
| public: |
| OffscreenRenderTarget(bool riveRenderable) : |
| GM(256, 256), m_riveRenderable(riveRenderable) |
| {} |
| |
| ColorInt clearColor() const override { return 0xffff0000; } |
| |
| void onDraw(rive::Renderer* originalRenderer) override |
| { |
| if (rcp<rive_tests::OffscreenRenderTarget> renderImageTarget = |
| TestingWindow::Get() |
| ->makeOffscreenRenderTarget(256, 256, m_riveRenderable)) |
| { |
| // Intercept the current frameDescriptor and end the PLS frame. |
| auto renderContext = TestingWindow::Get()->renderContext(); |
| auto originalFrameDescriptor = renderContext->frameDescriptor(); |
| TestingWindow::Get()->flushPLSContext(); |
| |
| // Draw to the offscreen texture. |
| auto textureFrameDescriptor = originalFrameDescriptor; |
| textureFrameDescriptor.clearColor = 0xffff00ff; |
| renderContext->beginFrame(std::move(textureFrameDescriptor)); |
| RiveRenderer renderer(renderContext); |
| drawInternal(&renderer, renderImageTarget->asRenderTarget()); |
| TestingWindow::Get()->flushPLSContext( |
| renderImageTarget->asRenderTarget()); |
| |
| // Copy the offscreen texture back to the destination framebuffer. |
| RenderImage* renderImage = renderImageTarget->asRenderImage(); |
| auto copyFrameDescriptor = originalFrameDescriptor; |
| copyFrameDescriptor.loadAction = |
| gpu::LoadAction::preserveRenderTarget; |
| copyFrameDescriptor.clearColor = 0xffff0000; |
| renderContext->beginFrame(std::move(copyFrameDescriptor)); |
| renderer.save(); |
| if (renderContext->platformFeatures().framebufferBottomUp) |
| { |
| renderer.translate(0, 256); |
| renderer.scale(1, -1); |
| } |
| renderer.drawImage(renderImage, |
| {.filter = ImageFilter::nearest}, |
| BlendMode::srcOver, |
| 1); |
| renderer.restore(); |
| } |
| else |
| { |
| drawInternal(originalRenderer, nullptr); |
| } |
| } |
| |
| virtual void drawInternal(Renderer* renderer, RenderTarget*) |
| { |
| drawStar5(renderer, Paint(0x8000ffff)); |
| drawStar13(renderer, Paint(0x80ffff00)); |
| } |
| |
| void drawStar5(Renderer* renderer, RenderPaint* paint) |
| { |
| PathBuilder builder; |
| float theta = -math::PI / 7; |
| builder.moveTo(cosf(theta), sinf(theta)); |
| for (int i = 0; i <= 7; ++i) |
| { |
| theta += 2 * math::PI * 2 / 7; |
| builder.lineTo(cosf(theta), sinf(theta)); |
| } |
| renderer->save(); |
| renderer->translate(100, 100); |
| renderer->scale(80, 80); |
| renderer->drawPath(builder.detach(), paint); |
| renderer->restore(); |
| } |
| |
| void drawStar13(Renderer* renderer, RenderPaint* paint) |
| { |
| PathBuilder builder; |
| float theta = 0; |
| for (int i = 0; i <= 13; ++i) |
| { |
| theta += 2 * math::PI * 3 / 13; |
| builder.lineTo(cosf(theta), sinf(theta)); |
| } |
| builder.fillRule(FillRule::evenOdd); |
| renderer->save(); |
| renderer->translate(256 - 100, 256 - 100); |
| renderer->scale(80, 80); |
| renderer->drawPath(builder.detach(), paint); |
| renderer->restore(); |
| } |
| |
| private: |
| const bool m_riveRenderable; |
| }; |
| |
| GMREGISTER(offscreen_render_target, return new OffscreenRenderTarget(true)) |
| GMREGISTER(offscreen_render_target_nonrenderable, |
| return new OffscreenRenderTarget(false)) |
| |
| // This GM checks that texture targets (including MSAA targets) work with |
| // LoadAction::preserveRenderTarget. |
| class OffscreenRenderTargetPreserve : public OffscreenRenderTarget |
| { |
| public: |
| OffscreenRenderTargetPreserve(BlendMode blendMode, bool riveRenderable) : |
| OffscreenRenderTarget(riveRenderable), m_blendMode(blendMode) |
| {} |
| OffscreenRenderTargetPreserve(bool riveRenderable) : |
| OffscreenRenderTargetPreserve(BlendMode::srcOver, riveRenderable) |
| {} |
| |
| virtual void drawInternal(Renderer* renderer, RenderTarget* renderTarget) |
| { |
| ColorInt colors[2]; |
| float stops[2]; |
| colors[0] = 0xff000000; |
| stops[0] = 0; |
| colors[1] = 0xffff00ff; |
| stops[1] = 1; |
| Paint paint; |
| paint->shader(TestingWindow::Get() |
| ->factory() |
| ->makeLinearGradient(0, 0, 250, 0, colors, stops, 2)); |
| renderer->drawPath(PathBuilder::Rect({0, 0, 256, 256}), paint); |
| |
| colors[0] = 0x80000000; |
| stops[0] = 0; |
| colors[1] = 0x8000ffff; |
| stops[1] = 1; |
| Paint paint2; |
| paint2->shader( |
| TestingWindow::Get() |
| ->factory() |
| ->makeLinearGradient(0, 0, 0, 250, colors, stops, 2)); |
| renderer->drawPath(PathBuilder::Rect({0, 0, 256, 256}), paint2); |
| |
| if (auto renderContext = TestingWindow::Get()->renderContext()) |
| { |
| auto frameDescriptor = renderContext->frameDescriptor(); |
| #ifndef RIVE_TOOLS_NO_GL |
| if (auto plsImplGL = TestingWindow::Get()->renderContextGLImpl()) |
| { |
| assert(renderTarget); |
| TestingWindow::Get()->flushPLSContext(renderTarget); |
| if (int sampleCount = frameDescriptor.msaaSampleCount) |
| { |
| // If the MSAA framebuffer target is not the target texture, |
| // wipe it to red behind the scenes in order to make sure |
| // our preservation codepath works. (It shouldn't appear red |
| // in the end -- this should get preserved instead.) |
| if (!plsImplGL->capabilities() |
| .EXT_multisampled_render_to_texture) |
| { |
| auto renderTextureTargetGL = |
| static_cast<RenderTargetGL*>(renderTarget); |
| renderTextureTargetGL->bindMSAAFramebuffer(plsImplGL, |
| sampleCount, |
| nullptr, |
| nullptr); |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| } |
| } |
| } |
| else |
| #endif |
| { |
| TestingWindow::Get()->flushPLSContext(renderTarget); |
| } |
| frameDescriptor.loadAction = gpu::LoadAction::preserveRenderTarget; |
| renderContext->beginFrame(std::move(frameDescriptor)); |
| } |
| Paint paint5(0x8000ffff); |
| paint5->blendMode(m_blendMode); |
| drawStar5(renderer, paint5); |
| |
| if (auto renderContext = TestingWindow::Get()->renderContext()) |
| { |
| auto frameDescriptor = renderContext->frameDescriptor(); |
| #ifndef RIVE_TOOLS_NO_GL |
| if (auto plsImplGL = TestingWindow::Get()->renderContextGLImpl()) |
| { |
| assert(renderTarget); |
| TestingWindow::Get()->flushPLSContext(renderTarget); |
| if (int sampleCount = frameDescriptor.msaaSampleCount) |
| { |
| // If the MSAA framebuffer target is not the target texture, |
| // wipe it to red behind the scenes in order to make sure |
| // our preservation codepath works. (It shouldn't appear red |
| // in the end -- this should get preserved instead.) |
| if (!plsImplGL->capabilities() |
| .EXT_multisampled_render_to_texture) |
| { |
| auto renderTextureTargetGL = |
| static_cast<RenderTargetGL*>(renderTarget); |
| renderTextureTargetGL->bindMSAAFramebuffer(plsImplGL, |
| sampleCount, |
| nullptr, |
| nullptr); |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| } |
| } |
| } |
| else |
| #endif |
| { |
| TestingWindow::Get()->flushPLSContext(renderTarget); |
| } |
| frameDescriptor.loadAction = gpu::LoadAction::preserveRenderTarget; |
| renderContext->beginFrame(std::move(frameDescriptor)); |
| } |
| Paint paint13(0x80ffff00); |
| paint13->blendMode(m_blendMode); |
| drawStar13(renderer, paint13); |
| } |
| |
| private: |
| BlendMode m_blendMode; |
| }; |
| GMREGISTER(offscreen_render_target_preserve, |
| return new OffscreenRenderTargetPreserve(true)) |
| GMREGISTER(offscreen_render_target_preserve_nonrenderable, |
| return new OffscreenRenderTargetPreserve(false)) |
| |
| // ...And verify that blend modes work on a texture target. |
| class OffscreenRenderTargetPreserveLum : public OffscreenRenderTargetPreserve |
| { |
| public: |
| OffscreenRenderTargetPreserveLum(bool riveRenderable) : |
| OffscreenRenderTargetPreserve(BlendMode::luminosity, riveRenderable) |
| {} |
| }; |
| GMREGISTER(offscreen_render_target_preserve_lum, |
| return new OffscreenRenderTargetPreserveLum(true)) |
| GMREGISTER(offscreen_render_target_preserve_lum_nonrenderable, |
| return new OffscreenRenderTargetPreserveLum(false)) |