blob: 280080bb22d77348bb8876ce9d30f5a7bd9a7383 [file] [log] [blame] [edit]
/*
* Copyright 2022 Rive
*/
#include "gm.hpp"
#include "gmutils.hpp"
#include "common/rand.hpp"
#include "common/write_png_file.hpp"
#include "rive/shapes/paint/image_sampler.hpp"
#ifndef RIVE_TOOLS_NO_GL
#include "rive/renderer/gl/render_context_gl_impl.hpp"
#endif
using namespace rivegm;
constexpr static size_t kRows = 100;
constexpr static size_t kCols = 100;
// Base class for stress tests that draw huge amounts of something.
class LotsOfSquaresGM : public GM
{
public:
LotsOfSquaresGM() : GM(1701, 1701) {}
rive::ColorInt clearColor() const override { return 0xff000000; }
void onDraw(rive::Renderer* renderer) override
{
preDraw(renderer);
renderer->translate(0, 1);
for (size_t j = 0; j < kRows; ++j)
{
renderer->save();
for (size_t i = 0; i < kCols; ++i)
{
drawSquare(renderer, 16, i, j);
renderer->translate(17, 0);
}
renderer->restore();
renderer->translate(0, 17);
}
}
virtual void preDraw(rive::Renderer*) {}
virtual void drawSquare(rive::Renderer*,
float size,
size_t i,
size_t j) = 0;
protected:
rive::ColorInt randColor()
{
return static_cast<rive::ColorInt>(m_rand.u64()) | 0x80000000;
}
Rand m_rand;
};
// Stress-test the gradient library by drawing more color ramps than fit in the
// gradient texture.
class LotsOfGradsGM : public LotsOfSquaresGM
{
public:
LotsOfGradsGM() : LotsOfSquaresGM() {}
void drawSquare(rive::Renderer* renderer,
float size,
size_t i,
size_t j) override
{
rive::Factory* factory = TestingWindow::Get()->factory();
auto paint = factory->makeRenderPaint();
paint->shader(makeGradient(factory, i, j));
rivegm::draw_rect(renderer, {0, 0, size, size}, paint.get());
}
virtual rive::rcp<rive::RenderShader> makeGradient(rive::Factory*,
size_t i,
size_t j) = 0;
};
// Run out of "simple" texels in the gradient texture.
class LotsOfGradsSimpleGM : public LotsOfGradsGM
{
public:
LotsOfGradsSimpleGM() : LotsOfGradsGM() {}
rive::rcp<rive::RenderShader> makeGradient(rive::Factory* factory,
size_t i,
size_t j) override
{
float stops[2] = {0, 1};
rive::ColorInt colors[2] = {randColor(), randColor()};
return factory->makeLinearGradient((i & 1) ? 16 : 0,
(j & 1) ? 16 : 0,
(i & 1) ? 0 : 16,
(j & 1) ? 0 : 16,
colors,
stops,
2);
}
};
GMREGISTER(lots_of_grads_simple, return new LotsOfGradsSimpleGM)
// Run out of "complex" texel rows in the gradient texture.
class LotsOfGradsComplexGM : public LotsOfGradsGM
{
public:
LotsOfGradsComplexGM() : LotsOfGradsGM() {}
rive::rcp<rive::RenderShader> makeGradient(rive::Factory* factory,
size_t i,
size_t j) override
{
float stops[3] = {.05f, .147f, 1};
rive::ColorInt colors[3] = {randColor(), randColor(), randColor()};
return factory->makeRadialGradient((i & 1) ? 0 : 16,
(j & 1) ? 2 : 22,
22,
colors,
stops,
3);
}
};
GMREGISTER(lots_of_grads_complex, return new LotsOfGradsComplexGM)
// Run out of "GradSpan" instances for rendering color ramps.
class LotsOfGradSpansGM : public LotsOfGradsGM
{
public:
LotsOfGradSpansGM() : LotsOfGradsGM() {}
rive::rcp<rive::RenderShader> makeGradient(rive::Factory* factory,
size_t i,
size_t j) override
{
float stops[7] = {0, .147f, .148f, .23f, .67f, .8f, 1};
rive::ColorInt colors[7] = {randColor(),
randColor(),
randColor(),
randColor(),
randColor(),
randColor(),
randColor()};
return factory->makeLinearGradient((i & 1) ? 16 : 0,
(j & 1) ? 16 : 0,
(i & 1) ? 0 : 16,
(j & 1) ? 0 : 16,
colors,
stops,
7);
}
};
GMREGISTER(lots_of_grad_spans, return new LotsOfGradSpansGM)
// Ensure that an intermediate flush does not invalidate the clip buffer
// contents.
class LotsOfGradsClippedGM : public LotsOfGradSpansGM
{
public:
LotsOfGradsClippedGM() : LotsOfGradSpansGM() {}
void preDraw(rive::Renderer* renderer) override
{
Path circle;
path_addOval(circle, rive::AABB(-1700, -1700, 3400, 3400));
renderer->clipPath(circle);
}
};
GMREGISTER(lots_of_grads_clipped, return new LotsOfGradsClippedGM)
// Run out of complex and simple gradients both.
class LotsOfGradsMixedGM : public LotsOfGradsGM
{
public:
LotsOfGradsMixedGM() : LotsOfGradsGM() {}
rive::rcp<rive::RenderShader> makeGradient(rive::Factory* factory,
size_t i,
size_t j) override
{
rive::ColorInt colors[3] = {randColor(), randColor(), randColor()};
std::array<float, 3> stops;
// Also test gradients with only one color stop (which render as a solid
// color).
int count = m_rand.u32(1, 3);
if (count == 3)
stops = {.05f, .147f, 1};
else
stops = {0, 1};
if (m_rand.boolean())
{
return factory->makeRadialGradient((i & 1) ? 0 : 16,
(j & 1) ? 2 : 22,
22,
colors,
stops.data(),
count);
}
else
{
return factory->makeLinearGradient((i & 1) ? 16 : 0,
(j & 1) ? 16 : 0,
(i & 1) ? 0 : 16,
(j & 1) ? 0 : 16,
colors,
stops.data(),
count);
}
}
};
GMREGISTER(lots_of_grads_mixed, return new LotsOfGradsMixedGM)
// Run out of VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE descriptors, requiring the Vulkan
// backend to allocate a new VkDescriptorPool during flush.
class LotsOfImagesGM : public LotsOfSquaresGM
{
public:
LotsOfImagesGM() : LotsOfSquaresGM() {}
void drawSquare(rive::Renderer* renderer,
float size,
size_t i,
size_t j) override
{
size_t imageCount = std::size(m_images);
auto& image = m_images[(j * kCols + i) % imageCount];
if (image == nullptr)
{
constexpr static size_t pngSize = 4;
rive::Factory* factory = TestingWindow::Get()->factory();
auto color = randColor();
std::vector<uint8_t> imagePixels(pngSize * pngSize * 4);
for (auto it = imagePixels.begin(); it != imagePixels.end();
it += 4)
{
rive::UnpackColorToRGBA8(color, &(*it));
}
std::vector<uint8_t> encodedData =
EncodePNGToBuffer(pngSize,
pngSize,
imagePixels.data(),
PNGCompression::fast_rle);
image = factory->decodeImage(rive::Span<uint8_t>(encodedData));
if (image == nullptr)
{
return;
}
}
AutoRestore ar(renderer, /*doSave=*/true);
renderer->scale(size / image->width(), size / image->height());
renderer->drawImage(image.get(),
rive::ImageSampler::LinearClamp(),
rive::BlendMode::srcOver,
1);
}
private:
rive::rcp<rive::RenderImage> m_images[512];
};
GMREGISTER(lots_of_images, return new LotsOfImagesGM)
// Run out of kMaxImageTextureUpdates descriptors, requiring the Vulkan
// backend to allocate a new VkDescriptorPool during flush.
class LotsOfImagesSampledGM : public LotsOfSquaresGM
{
public:
LotsOfImagesSampledGM() : LotsOfSquaresGM() {}
void drawSquare(rive::Renderer* renderer,
float size,
size_t i,
size_t j) override
{
auto flatIndex = (j * kCols + i);
auto sampler = rive::ImageSampler::SamplerFromKey(
flatIndex % rive::ImageSampler::MAX_SAMPLER_PERMUTATIONS);
if (m_image == nullptr)
{
constexpr static size_t pngSize = 4;
rive::Factory* factory = TestingWindow::Get()->factory();
auto color = randColor();
std::vector<uint8_t> imagePixels(pngSize * pngSize * 4);
for (auto it = imagePixels.begin(); it != imagePixels.end();
it += 4)
{
rive::UnpackColorToRGBA8(color, &(*it));
}
std::vector<uint8_t> encodedData =
EncodePNGToBuffer(pngSize,
pngSize,
imagePixels.data(),
PNGCompression::fast_rle);
m_image = factory->decodeImage(rive::Span<uint8_t>(encodedData));
if (m_image == nullptr)
{
return;
}
}
AutoRestore ar(renderer, /*doSave=*/true);
renderer->scale(size / m_image->width(), size / m_image->height());
renderer->drawImage(m_image.get(),
sampler,
rive::BlendMode::srcOver,
1);
}
private:
rive::rcp<rive::RenderImage> m_image;
};
GMREGISTER(lots_of_images_sampled, return new LotsOfImagesSampledGM)