Skip to content

Commit

Permalink
Implement a feather atlas for msaa
Browse files Browse the repository at this point in the history
Diffs=
8f7120837b Implement a feather atlas for msaa (#9001)

Co-authored-by: Chris Dalton <[email protected]>
  • Loading branch information
csmartdalton and csmartdalton committed Feb 8, 2025
1 parent 2db93d8 commit d6e9052
Show file tree
Hide file tree
Showing 31 changed files with 1,809 additions and 530 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1be3488d52ec4c2f32a03eb111afca5b1f01ecd8
8f7120837bc9eb91c6cd23a8eb25d311e4057534
22 changes: 12 additions & 10 deletions include/rive/math/aabb.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,46 +7,48 @@

namespace rive
{
struct IAABB
template <typename T> struct TAABB
{
int32_t left, top, right, bottom;

constexpr int width() const { return right - left; }
constexpr int height() const { return bottom - top; }
constexpr T width() const { return right - left; }
constexpr T height() const { return bottom - top; }
constexpr bool empty() const { return left >= right || top >= bottom; }

IAABB inset(int dx, int dy) const
TAABB inset(T dx, T dy) const
{
return {left + dx, top + dy, right - dx, bottom - dy};
}
IAABB outset(int dx, int dy) const { return inset(-dx, -dy); }
IAABB offset(int dx, int dy) const
TAABB outset(T dx, T dy) const { return inset(-dx, -dy); }
TAABB offset(T dx, T dy) const
{
return {left + dx, top + dy, right + dx, bottom + dy};
}
IAABB join(IAABB b) const
TAABB join(TAABB b) const
{
return {std::min(left, b.left),
std::min(top, b.top),
std::max(right, b.right),
std::max(bottom, b.bottom)};
}
IAABB intersect(IAABB b) const
TAABB intersect(TAABB b) const
{
return {std::max(left, b.left),
std::max(top, b.top),
std::min(right, b.right),
std::min(bottom, b.bottom)};
}

bool operator==(const IAABB& o) const
bool operator==(const TAABB& o) const
{
return left == o.left && top == o.top && right == o.right &&
bottom == o.bottom;
}
bool operator!=(const IAABB& o) const { return !(*this == o); }
bool operator!=(const TAABB& o) const { return !(*this == o); }
};

using IAABB = TAABB<int32_t>;

class AABB
{
public:
Expand Down
82 changes: 58 additions & 24 deletions renderer/include/rive/renderer/draw.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ class Draw

enum class Type : uint8_t
{
midpointFanPath,
interiorTriangulationPath,
path,
imageRect,
imageMesh,
stencilClipReset,
Expand All @@ -55,11 +54,6 @@ class Draw
BlendMode blendMode() const { return m_blendMode; }
Type type() const { return m_type; }
gpu::DrawContents drawContents() const { return m_drawContents; }
bool isStroke() const { return m_drawContents & gpu::DrawContents::stroke; }
bool isEvenOddFill() const
{
return m_drawContents & gpu::DrawContents::evenOddFill;
}
bool isOpaque() const
{
return m_drawContents & gpu::DrawContents::opaquePaint;
Expand Down Expand Up @@ -196,46 +190,82 @@ class PathDraw : public Draw
const RiveRenderPaint*,
RawPath* scratchPath);

// Determines how coverage is calculated for antialiasing and feathers.
// CoverageType is mostly decided by the InterlockMode, but we keep these
// concepts separate because atlas coverage may be used with any
// InterlockMode.
enum class CoverageType
{
pixelLocalStorage, // InterlockMode::rasterOrdering and atomics
clockwiseAtomic, // InterlockMode::clockwiseAtomic
msaa, // InterlockMode::msaa
atlas, // Any InterlockMode may opt to use atlas coverage for large
// feathers; msaa always has to use an atlas for feathers.
};

PathDraw(IAABB pixelBounds,
const Mat2D&,
rcp<const RiveRenderPath>,
FillRule,
const RiveRenderPaint*,
Type,
const RenderContext::FrameDescriptor&,
gpu::InterlockMode);
CoverageType,
const RenderContext::FrameDescriptor&);

CoverageType coverageType() const { return m_coverageType; }

const Gradient* gradient() const { return m_gradientRef; }
gpu::PaintType paintType() const { return m_paintType; }
bool isFeatheredFill() const
{
return m_drawContents & gpu::DrawContents::featheredFill;
return m_featherRadius != 0 && m_strokeRadius == 0;
}
bool isStrokeOrFeather() const
{
return m_drawContents &
(gpu::DrawContents::stroke | gpu::DrawContents::featheredFill);
return (math::bit_cast<uint32_t>(m_featherRadius) |
math::bit_cast<uint32_t>(m_strokeRadius)) != 0;
}
bool isStroke() const { return m_strokeRadius != 0; }
float strokeRadius() const { return m_strokeRadius; }
float featherRadius() const { return m_featherRadius; }
gpu::ContourDirections contourDirections() const
{
return m_contourDirections;
}

// Only used when rendering coverage via the atlas.
int16_t screenToAtlasOffsetX() const { return m_screenToAtlasOffsetX; }
int16_t screenToAtlasOffsetY() const { return m_screenToAtlasOffsetY; }
const TAABB<uint16_t>& atlasScissor() const { return m_atlasScissor; }
bool atlasScissorEnabled() const { return m_atlasScissorEnabled; }

// clockwiseAtomic only.
const gpu::CoverageBufferRange& coverageBufferRange() const
{
return m_coverageBufferRange;
}

GrInnerFanTriangulator* triangulator() const { return m_triangulator; }

bool allocateResourcesAndSubpasses(RenderContext::LogicalFlush*) override;

void pushToRenderContext(RenderContext::LogicalFlush*,
int subpassIndex) override;

// Called after pushToRenderContext(), and only when this draw uses an atlas
// for tessellation. In the CoverageType::atlas case, pushToRenderContext()
// is where we emit the rectangle that reads the atlas (and writes to the
// main render target). So this method is where we push the tessellation
// that gets rendered separately to the offscreen atlas.
void pushAtlasTessellation(RenderContext::LogicalFlush*,
uint32_t* tessVertexCount,
uint32_t* tessBaseVertex);

void releaseRefs() override;

protected:
static CoverageType SelectCoverageType(const RiveRenderPaint*,
gpu::InterlockMode);

// Prepares to draw the path by tessellating a fan around its midpoint.
void initForMidpointFan(RenderContext*, const RiveRenderPaint*);

Expand All @@ -252,16 +282,9 @@ class PathDraw : public Draw
RawPath*,
TriangulatorAxis);

// Implements pushToRenderContext() for paths that use rasterOrdering or
// atomics.
void pushToRenderContextImpl(RenderContext::LogicalFlush*,
int subpassIndex,
uint32_t tessVertexCount);

// Implements pushToRenderContext() for paths that use MSAA.
void pushToRenderContextImplMSAA(RenderContext::LogicalFlush*,
int subpassIndex,
uint32_t tessVertexCount);
void pushTessellationData(RenderContext::LogicalFlush*,
uint32_t* tessVertexCount,
uint32_t* tessLocation);

// Pushes the contours and cubics to the renderContext for a
// "midpointFanPatches" draw.
Expand Down Expand Up @@ -300,13 +323,24 @@ class PathDraw : public Draw
RenderContext::TessellationWriter*);

const RiveRenderPath* const m_pathRef;
const FillRule m_pathFillRule; // Fill rule can mutate on RenderPath.
const Gradient* m_gradientRef;
const gpu::PaintType m_paintType;
const CoverageType m_coverageType;
float m_strokeRadius = 0;
float m_featherRadius = 0;
gpu::ContourDirections m_contourDirections;
uint32_t m_contourFlags = 0;
gpu::CoverageBufferRange m_coverageBufferRange; // clockwiseAtomic only.

// Only used when rendering coverage via the atlas.
int16_t m_screenToAtlasOffsetX;
int16_t m_screenToAtlasOffsetY;
TAABB<uint16_t> m_atlasScissor; // Scissor rect when rendering to the atlas.
bool m_atlasScissorEnabled;

// clockwiseAtomic only.
gpu::CoverageBufferRange m_coverageBufferRange;

GrInnerFanTriangulator* m_triangulator = nullptr;

StrokeJoin m_strokeJoin;
Expand Down
34 changes: 31 additions & 3 deletions renderer/include/rive/renderer/gl/gl_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,36 @@ class GLState : public RefCnt<GLState>

void invalidate();

void setBlendEquation(BlendMode);
void disableBlending();
enum class GLBlendMode
{
none = 0,

srcOver = static_cast<int>(rive::BlendMode::srcOver),
screen = static_cast<int>(rive::BlendMode::screen),
overlay = static_cast<int>(rive::BlendMode::overlay),
darken = static_cast<int>(rive::BlendMode::darken),
lighten = static_cast<int>(rive::BlendMode::lighten),
colorDodge = static_cast<int>(rive::BlendMode::colorDodge),
colorBurn = static_cast<int>(rive::BlendMode::colorBurn),
hardLight = static_cast<int>(rive::BlendMode::hardLight),
softLight = static_cast<int>(rive::BlendMode::softLight),
difference = static_cast<int>(rive::BlendMode::difference),
exclusion = static_cast<int>(rive::BlendMode::exclusion),
multiply = static_cast<int>(rive::BlendMode::multiply),
hue = static_cast<int>(rive::BlendMode::hue),
saturation = static_cast<int>(rive::BlendMode::saturation),
color = static_cast<int>(rive::BlendMode::color),
luminosity = static_cast<int>(rive::BlendMode::luminosity),

plus = srcOver + 1,
max = plus + 1,
};
void setGLBlendMode(GLBlendMode);
void setBlendMode(rive::BlendMode blendMode)
{
setGLBlendMode(static_cast<GLBlendMode>(blendMode));
}
void disableBlending() { setGLBlendMode(GLBlendMode::none); }

void setWriteMasks(bool colorWriteMask,
bool depthWriteMask,
Expand All @@ -39,7 +67,7 @@ class GLState : public RefCnt<GLState>

private:
const GLCapabilities m_capabilities;
GLenum m_blendEquation;
GLBlendMode m_blendMode;
bool m_colorWriteMask;
bool m_depthWriteMask;
GLuint m_stencilWriteMask;
Expand Down
45 changes: 40 additions & 5 deletions renderer/include/rive/renderer/gl/gl_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void CompileAndAttachShader(GLuint program,
size_t numSources,
const GLCapabilities&);

[[nodiscard]] GLuint CompileRawGLSL(GLuint shaderType, const char* rawGLSL);
[[nodiscard]] GLuint CompileRawGLSL(GLenum shaderType, const char* rawGLSL);

void LinkProgram(GLuint program);

Expand Down Expand Up @@ -151,15 +151,50 @@ class VAO : public GLObject
~VAO() { glDeleteVertexArrays(1, &m_id); }
};

class Shader : public GLObject
{
public:
Shader() = default;
Shader(Shader&& rhs) : GLObject(std::move(rhs)) {}
Shader& operator=(Shader&& rhs)
{
reset(std::exchange(rhs.m_id, 0));
return *this;
}
~Shader() { reset(0); }

void compile(GLenum type,
const char* source,
const GLCapabilities& capabilities)
{
compile(type, nullptr, 0, &source, 1, capabilities);
}
void compile(GLenum type,
const char* defines[],
size_t numDefines,
const char* sources[],
size_t numSources,
const GLCapabilities&);

void reset(GLuint adoptedID = 0)
{
if (m_id != 0)
{
glDeleteShader(m_id);
}
m_id = adoptedID;
}
};

class Program : public GLObject
{
public:
Program() : GLObject(glCreateProgram()) {}
Program& operator=(Program&& rhs)
{
reset(std::exchange(rhs.m_id, 0));
m_vertexShaderID = std::exchange(rhs.m_vertexShaderID, 0);
m_fragmentShaderID = std::exchange(rhs.m_fragmentShaderID, 0);
m_vertexShader = std::move(rhs.m_vertexShader);
m_fragmentShader = std::move(rhs.m_fragmentShader);
return *this;
}
~Program() { reset(0); }
Expand All @@ -186,8 +221,8 @@ class Program : public GLObject

void reset(GLuint adoptedProgramID);

GLuint m_vertexShaderID = 0;
GLuint m_fragmentShaderID = 0;
glutils::Shader m_vertexShader;
glutils::Shader m_fragmentShader;
};

void SetTexture2DSamplingParams(GLenum minFilter, GLenum magFilter);
Expand Down
31 changes: 31 additions & 0 deletions renderer/include/rive/renderer/gl/render_context_gl_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ class RenderContextGLImpl : public RenderContextHelperImpl

void resizeGradientTexture(uint32_t width, uint32_t height) override;
void resizeTessellationTexture(uint32_t width, uint32_t height) override;
void resizeAtlasTexture(uint32_t width, uint32_t height) override;

void flush(const FlushDescriptor&) override;

Expand All @@ -231,6 +232,36 @@ class RenderContextGLImpl : public RenderContextHelperImpl
glutils::Framebuffer m_tessellateFBO;
GLuint m_tessVertexTexture = 0;

// Atlas rendering.
class AtlasProgram
{
public:
void compile(GLuint vertexShaderID,
const char* defines[],
size_t numDefines,
const char* sources[],
size_t numSources,
const GLCapabilities&,
GLState* state);

operator GLuint() const { return m_program; }

GLint spirvCrossBaseInstanceLocation() const
{
return m_spirvCrossBaseInstanceLocation;
}

private:
glutils::Program m_program = glutils::Program::Zero();
GLint m_spirvCrossBaseInstanceLocation = -1;
};

glutils::Shader m_atlasVertexShader;
AtlasProgram m_atlasFillProgram;
AtlasProgram m_atlasStrokeProgram;
glutils::Texture m_atlasTexture = glutils::Texture::Zero();
glutils::Framebuffer m_atlasFBO;

// Not all programs have a unique vertex shader, so we cache and reuse them
// where possible.
std::map<uint32_t, DrawShader> m_vertexShaders;
Expand Down
Loading

0 comments on commit d6e9052

Please sign in to comment.