blob: 6e8c5a0a6d4b87d847d885706f7415743dd53129 [file] [log] [blame] [edit]
/*
* 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))