refactor: Start splitting shaders into separate files (#10698) b73733a052

* refactor: Start splitting shaders into separate files

The draw shaders are getting unruly with #defines all over the place.
Before we can add more, we need to clean these up a little. This PR
splits out the fragment portions of raster ordered and msaa shaders into
separate files. This also required teaching minify.py to handle .vert and
.frag files.

* apply_frag_coverage

* fixwebgpu

* unreal

* fixmetalagain

* formats

* oops

* rm draw_image_mesh.glsl

Co-authored-by: Chris Dalton <99840794+csmartdalton@users.noreply.github.com>
This commit is contained in:
csmartdalton
2025-10-07 22:39:23 +00:00
parent 196a386b7e
commit 454d644c3c
35 changed files with 771 additions and 663 deletions

View File

@@ -1 +1 @@
986c92af9ab29f5c68bb3a6a39c1f05924683028
b73733a0525518a76e8a6b32a2529a2dea319ace

View File

@@ -277,9 +277,9 @@ private:
glutils::Texture m_atlasTexture = glutils::Texture::Zero();
glutils::Framebuffer m_atlasFBO;
// Wraps a compiled GL shader of draw_path.glsl or draw_image_mesh.glsl,
// either vertex or fragment, with a specific set of features enabled via
// #define. The set of features to enable is dictated by ShaderFeatures.
// Wraps a compiled GL "draw" shader, either vertex or fragment, with a
// specific set of features enabled via #define. The set of features to
// enable is dictated by ShaderFeatures.
class DrawShader
{
public:
@@ -304,9 +304,9 @@ private:
GLuint m_id = 0;
};
// Wraps a compiled and linked GL program of draw_path.glsl or
// draw_image_mesh.glsl, with a specific set of features enabled via
// #define. The set of features to enable is dictated by ShaderFeatures.
// Wraps a compiled and linked GL "draw" program, with a specific set of
// features enabled via #define. The set of features to enable is dictated
// by ShaderFeatures.
class DrawProgram
{
public:

View File

@@ -10,16 +10,14 @@
#include "generated/shaders/advanced_blend.glsl.hpp"
#include "generated/shaders/atomic_draw.glsl.hpp"
#include "generated/shaders/color_ramp.glsl.hpp"
#include "generated/shaders/constants.glsl.hpp"
#include "generated/shaders/common.glsl.hpp"
#include "generated/shaders/draw_image_mesh.glsl.hpp"
#include "generated/shaders/draw_image_mesh.vert.hpp"
#include "generated/shaders/draw_raster_order_image_mesh.frag.hpp"
#include "generated/shaders/draw_path_common.glsl.hpp"
#include "generated/shaders/draw_path.glsl.hpp"
#include "generated/shaders/draw_path.vert.hpp"
#include "generated/shaders/draw_raster_order_path.frag.hpp"
#include "generated/shaders/hlsl.glsl.hpp"
#include "generated/shaders/bezier_utils.glsl.hpp"
#include "generated/shaders/render_atlas.glsl.hpp"
#include "generated/shaders/tessellate.glsl.hpp"
namespace rive::gpu::d3d_utils
{
@@ -125,18 +123,30 @@ static std::string build_shader(DrawType drawType,
case DrawType::midpointFanCenterAAPatches:
case DrawType::outerCurvePatches:
s << glsl::draw_path_common << '\n';
s << (interlockMode == InterlockMode::rasterOrdering
? glsl::draw_path
: glsl::atomic_draw)
<< '\n';
if (interlockMode == gpu::InterlockMode::rasterOrdering)
{
s << glsl::draw_path_vert << '\n';
s << glsl::draw_raster_order_path_frag << '\n';
}
else
{
assert(interlockMode == gpu::InterlockMode::atomics);
s << glsl::atomic_draw << '\n';
}
break;
case DrawType::interiorTriangulation:
case DrawType::atlasBlit:
s << glsl::draw_path_common << '\n';
s << (interlockMode == InterlockMode::rasterOrdering
? glsl::draw_path
: glsl::atomic_draw)
<< '\n';
if (interlockMode == gpu::InterlockMode::rasterOrdering)
{
s << glsl::draw_path_vert << '\n';
s << glsl::draw_raster_order_path_frag << '\n';
}
else
{
assert(interlockMode == gpu::InterlockMode::atomics);
s << glsl::atomic_draw << '\n';
}
break;
case DrawType::imageRect:
assert(interlockMode == InterlockMode::atomics);
@@ -146,7 +156,8 @@ static std::string build_shader(DrawType drawType,
case DrawType::imageMesh:
if (interlockMode == InterlockMode::rasterOrdering)
{
s << glsl::draw_image_mesh << '\n';
s << glsl::draw_image_mesh_vert << '\n';
s << glsl::draw_raster_order_image_mesh_frag << '\n';
}
else
{

View File

@@ -9,20 +9,8 @@
#include "rive/renderer/texture.hpp"
#include <D3DCompiler.h>
#include <sstream>
#include "generated/shaders/advanced_blend.glsl.hpp"
#include "generated/shaders/atomic_draw.glsl.hpp"
#include "generated/shaders/color_ramp.glsl.hpp"
#include "generated/shaders/constants.glsl.hpp"
#include "generated/shaders/common.glsl.hpp"
#include "generated/shaders/draw_image_mesh.glsl.hpp"
#include "generated/shaders/draw_path_common.glsl.hpp"
#include "generated/shaders/draw_path.glsl.hpp"
#include "generated/shaders/hlsl.glsl.hpp"
#include "generated/shaders/bezier_utils.glsl.hpp"
#include "generated/shaders/render_atlas.glsl.hpp"
#include "generated/shaders/tessellate.glsl.hpp"
#include "generated/shaders/tessellate.glsl.exports.h"
// offline shaders
namespace shader

View File

@@ -4,18 +4,7 @@
#include "rive/renderer/d3d12/d3d12_pipeline_manager.hpp"
#include "rive/renderer/d3d/d3d_constants.hpp"
#include "generated/shaders/advanced_blend.glsl.hpp"
#include "generated/shaders/atomic_draw.glsl.hpp"
#include "generated/shaders/color_ramp.glsl.hpp"
#include "generated/shaders/constants.glsl.hpp"
#include "generated/shaders/common.glsl.hpp"
#include "generated/shaders/draw_image_mesh.glsl.hpp"
#include "generated/shaders/draw_path_common.glsl.hpp"
#include "generated/shaders/draw_path.glsl.hpp"
#include "generated/shaders/hlsl.glsl.hpp"
#include "generated/shaders/bezier_utils.glsl.hpp"
#include "generated/shaders/render_atlas.glsl.hpp"
#include "generated/shaders/tessellate.glsl.hpp"
#include "generated/shaders/tessellate.glsl.exports.h"
// offline shaders
namespace shader

View File

@@ -10,7 +10,7 @@
#include "shaders/constants.glsl"
#include <sstream>
#include "generated/shaders/pls_load_store_ext.exports.h"
#include "generated/shaders/pls_load_store_ext.glsl.exports.h"
namespace rive::gpu
{

View File

@@ -8,7 +8,7 @@
#include "shaders/constants.glsl"
#include "rive/renderer/gl/gl_utils.hpp"
#include "generated/shaders/glsl.exports.h"
#include "generated/shaders/glsl.glsl.exports.h"
namespace rive::gpu
{

View File

@@ -8,7 +8,7 @@
#include "rive/renderer/gl/render_target_gl.hpp"
#include "shaders/constants.glsl"
#include "generated/shaders/glsl.exports.h"
#include "generated/shaders/glsl.glsl.exports.h"
#ifdef RIVE_WEBGL
#include <emscripten/emscripten.h>

View File

@@ -18,8 +18,12 @@
#include "generated/shaders/constants.glsl.hpp"
#include "generated/shaders/common.glsl.hpp"
#include "generated/shaders/draw_path_common.glsl.hpp"
#include "generated/shaders/draw_path.glsl.hpp"
#include "generated/shaders/draw_image_mesh.glsl.hpp"
#include "generated/shaders/draw_path.vert.hpp"
#include "generated/shaders/draw_raster_order_path.frag.hpp"
#include "generated/shaders/draw_msaa_path.frag.hpp"
#include "generated/shaders/draw_image_mesh.vert.hpp"
#include "generated/shaders/draw_raster_order_image_mesh.frag.hpp"
#include "generated/shaders/draw_msaa_image_mesh.frag.hpp"
#include "generated/shaders/bezier_utils.glsl.hpp"
#include "generated/shaders/tessellate.glsl.hpp"
#include "generated/shaders/render_atlas.glsl.hpp"
@@ -1098,9 +1102,18 @@ RenderContextGLImpl::DrawShader::DrawShader(
}
defines.push_back(GLSL_DRAW_PATH);
sources.push_back(gpu::glsl::draw_path_common);
sources.push_back(interlockMode == gpu::InterlockMode::atomics
? gpu::glsl::atomic_draw
: gpu::glsl::draw_path);
if (interlockMode == gpu::InterlockMode::atomics)
{
sources.push_back(gpu::glsl::atomic_draw);
}
else
{
sources.push_back(gpu::glsl::draw_path_vert);
sources.push_back(interlockMode == gpu::InterlockMode::msaa
? gpu::glsl::draw_msaa_path_frag
: gpu::glsl::draw_raster_order_path_frag);
}
break;
case gpu::DrawType::msaaStencilClipReset:
assert(interlockMode == gpu::InterlockMode::msaa);
@@ -1128,9 +1141,18 @@ RenderContextGLImpl::DrawShader::DrawShader(
case gpu::DrawType::interiorTriangulation:
defines.push_back(GLSL_DRAW_INTERIOR_TRIANGLES);
sources.push_back(gpu::glsl::draw_path_common);
sources.push_back(interlockMode == gpu::InterlockMode::atomics
? gpu::glsl::atomic_draw
: gpu::glsl::draw_path);
if (interlockMode == gpu::InterlockMode::atomics)
{
sources.push_back(gpu::glsl::atomic_draw);
}
else
{
sources.push_back(gpu::glsl::draw_path_vert);
sources.push_back(interlockMode == gpu::InterlockMode::msaa
? gpu::glsl::draw_msaa_path_frag
: gpu::glsl::draw_raster_order_path_frag);
}
break;
case gpu::DrawType::imageRect:
assert(interlockMode == gpu::InterlockMode::atomics);
@@ -1149,7 +1171,11 @@ RenderContextGLImpl::DrawShader::DrawShader(
}
else
{
sources.push_back(gpu::glsl::draw_image_mesh);
sources.push_back(gpu::glsl::draw_image_mesh_vert);
sources.push_back(
interlockMode == gpu::InterlockMode::msaa
? gpu::glsl::draw_msaa_image_mesh_frag
: gpu::glsl::draw_raster_order_image_mesh_frag);
}
break;
case gpu::DrawType::renderPassResolve:

View File

@@ -10,7 +10,7 @@
#include "rive_render_paint.hpp"
#include "gradient.hpp"
#include "generated/shaders/draw_path.exports.h"
#include "generated/shaders/draw_path.vert.exports.h"
namespace rive::gpu
{

View File

@@ -14,8 +14,8 @@
namespace rive::gpu
{
// Defines a job to compile a "draw" shader -- either draw_path.glsl or
// draw_image_mesh.glsl, with a specific set of features enabled.
// Defines a job to compile a "draw" shader, with a specific set of features
// enabled.
struct BackgroundCompileJob
{
gpu::DrawType drawType;
@@ -29,9 +29,8 @@ struct BackgroundCompileJob
#endif
};
// Compiles "draw" shaders in a background thread. A "draw" shaders is either
// draw_path.glsl or draw_image_mesh.glsl, with a specific set of features
// enabled.
// Compiles "draw" shaders in a background thread, with a specific set of
// features enabled.
class BackgroundShaderCompiler
{
public:

View File

@@ -9,8 +9,10 @@
#include "generated/shaders/common.glsl.hpp"
#include "generated/shaders/advanced_blend.glsl.hpp"
#include "generated/shaders/draw_path_common.glsl.hpp"
#include "generated/shaders/draw_path.glsl.hpp"
#include "generated/shaders/draw_image_mesh.glsl.hpp"
#include "generated/shaders/draw_path.vert.hpp"
#include "generated/shaders/draw_raster_order_path.frag.hpp"
#include "generated/shaders/draw_image_mesh.vert.hpp"
#include "generated/shaders/draw_raster_order_image_mesh.frag.hpp"
#ifndef RIVE_IOS
// iOS doesn't need the atomic shaders; every non-simulated iOS device supports
@@ -144,15 +146,20 @@ void BackgroundShaderCompiler::threadMain()
defines[@GLSL_ENABLE_INSTANCE_INDEX] = @"";
defines[@GLSL_DRAW_PATH] = @"";
[source appendFormat:@"%s\n", gpu::glsl::draw_path_common];
#ifdef RIVE_IOS
[source appendFormat:@"%s\n", gpu::glsl::draw_path];
#else
[source appendFormat:@"%s\n",
interlockMode ==
gpu::InterlockMode::rasterOrdering
? gpu::glsl::draw_path
: gpu::glsl::atomic_draw];
#ifndef RIVE_IOS
if (interlockMode == gpu::InterlockMode::atomics)
{
[source appendFormat:@"%s\n", gpu::glsl::atomic_draw];
}
else
#endif
{
assert(interlockMode == gpu::InterlockMode::rasterOrdering);
[source appendFormat:@"%s\n", gpu::glsl::draw_path_vert];
[source
appendFormat:@"%s\n",
gpu::glsl::draw_raster_order_path_frag];
}
break;
case DrawType::atlasBlit:
defines[@GLSL_ATLAS_BLIT] = @"1";
@@ -160,15 +167,20 @@ void BackgroundShaderCompiler::threadMain()
case DrawType::interiorTriangulation:
defines[@GLSL_DRAW_INTERIOR_TRIANGLES] = @"";
[source appendFormat:@"%s\n", gpu::glsl::draw_path_common];
#ifdef RIVE_IOS
[source appendFormat:@"%s\n", gpu::glsl::draw_path];
#else
[source appendFormat:@"%s\n",
interlockMode ==
gpu::InterlockMode::rasterOrdering
? gpu::glsl::draw_path
: gpu::glsl::atomic_draw];
#ifndef RIVE_IOS
if (interlockMode == gpu::InterlockMode::atomics)
{
[source appendFormat:@"%s\n", gpu::glsl::atomic_draw];
}
else
#endif
{
assert(interlockMode == gpu::InterlockMode::rasterOrdering);
[source appendFormat:@"%s\n", gpu::glsl::draw_path_vert];
[source
appendFormat:@"%s\n",
gpu::glsl::draw_raster_order_path_frag];
}
break;
case DrawType::imageRect:
#ifdef RIVE_IOS
@@ -184,19 +196,22 @@ void BackgroundShaderCompiler::threadMain()
case DrawType::imageMesh:
defines[@GLSL_DRAW_IMAGE] = @"";
defines[@GLSL_DRAW_IMAGE_MESH] = @"";
#ifdef RIVE_IOS
[source appendFormat:@"%s\n", gpu::glsl::draw_image_mesh];
#else
if (interlockMode == gpu::InterlockMode::rasterOrdering)
{
[source appendFormat:@"%s\n", gpu::glsl::draw_image_mesh];
}
else
#ifndef RIVE_IOS
if (interlockMode == gpu::InterlockMode::atomics)
{
[source appendFormat:@"%s\n", gpu::glsl::draw_path_common];
[source appendFormat:@"%s\n", gpu::glsl::atomic_draw];
}
else
#endif
{
assert(interlockMode == gpu::InterlockMode::rasterOrdering);
[source
appendFormat:@"%s\n", gpu::glsl::draw_image_mesh_vert];
[source appendFormat:@"%s\n",
gpu::glsl::
draw_raster_order_image_mesh_frag];
}
break;
case DrawType::renderPassInitialize:
#ifdef RIVE_IOS

View File

@@ -11,8 +11,8 @@
#include "shaders/constants.glsl"
#include <sstream>
#include "generated/shaders/color_ramp.exports.h"
#include "generated/shaders/tessellate.exports.h"
#include "generated/shaders/color_ramp.glsl.exports.h"
#include "generated/shaders/tessellate.glsl.exports.h"
#if defined(RIVE_IOS_SIMULATOR)
#import <mach-o/arch.h>

View File

@@ -7,7 +7,7 @@ OUT := out/generated
FLAGS :=
## Shader minification.
MINIFY_INPUTS := $(wildcard *.glsl)
MINIFY_INPUTS := $(wildcard *.glsl) $(wildcard *.vert) $(wildcard *.frag)
MINIFY_EXPORT_OUTPUTS := $(addprefix $(OUT)/, $(patsubst %.glsl, %.exports.h, $(MINIFY_INPUTS)))
MINIFY_GLSL_OUTPUTS := $(addprefix $(OUT)/, $(patsubst %.glsl, %.minified.glsl, $(MINIFY_INPUTS)))
MINIFY_HPP_OUTPUTS := $(addprefix $(OUT)/, $(patsubst %.glsl, %.glsl.hpp, $(MINIFY_INPUTS)))

View File

@@ -23,6 +23,8 @@ layout(input_attachment_index = 0,
binding = MSAA_COLOR_SEED_IDX,
set = PLS_TEXTURE_BINDINGS_SET) uniform lowp subpassInput
inputAttachment;
layout(location = 0) out half4 outputColor;
void main() { outputColor = subpassLoad(inputAttachment); }
#endif

View File

@@ -1,209 +0,0 @@
/*
* Copyright 2023 Rive
*/
#ifdef @VERTEX
ATTR_BLOCK_BEGIN(PositionAttr)
ATTR(0, float2, @a_position);
ATTR_BLOCK_END
ATTR_BLOCK_BEGIN(UVAttr)
ATTR(1, float2, @a_texCoord);
ATTR_BLOCK_END
#endif
VARYING_BLOCK_BEGIN
NO_PERSPECTIVE VARYING(0, float2, v_texCoord);
#ifdef @ENABLE_CLIPPING
@OPTIONALLY_FLAT VARYING(1, half, v_clipID);
#endif
#if defined(@ENABLE_CLIP_RECT) && !defined(@RENDER_MODE_MSAA)
NO_PERSPECTIVE VARYING(2, float4, v_clipRect);
#endif
VARYING_BLOCK_END
#ifdef @VERTEX
VERTEX_TEXTURE_BLOCK_BEGIN
VERTEX_TEXTURE_BLOCK_END
IMAGE_MESH_VERTEX_MAIN(@drawVertexMain,
PositionAttr,
position,
UVAttr,
uv,
_vertexID)
{
ATTR_UNPACK(_vertexID, position, @a_position, float2);
ATTR_UNPACK(_vertexID, uv, @a_texCoord, float2);
VARYING_INIT(v_texCoord, float2);
#ifdef @ENABLE_CLIPPING
VARYING_INIT(v_clipID, half);
#endif
#if defined(@ENABLE_CLIP_RECT) && !defined(@RENDER_MODE_MSAA)
VARYING_INIT(v_clipRect, float4);
#endif
float2 vertexPosition =
MUL(make_float2x2(imageDrawUniforms.viewMatrix), @a_position) +
imageDrawUniforms.translate;
v_texCoord = @a_texCoord;
#ifdef @ENABLE_CLIPPING
if (@ENABLE_CLIPPING)
{
v_clipID = id_bits_to_f16(imageDrawUniforms.clipID,
uniforms.pathIDGranularity);
}
#endif
#ifdef @ENABLE_CLIP_RECT
if (@ENABLE_CLIP_RECT)
{
#ifndef @RENDER_MODE_MSAA
v_clipRect = find_clip_rect_coverage_distances(
make_float2x2(imageDrawUniforms.clipRectInverseMatrix),
imageDrawUniforms.clipRectInverseTranslate,
vertexPosition);
#else
set_clip_rect_plane_distances(
make_float2x2(imageDrawUniforms.clipRectInverseMatrix),
imageDrawUniforms.clipRectInverseTranslate,
vertexPosition);
#endif
}
#endif // ENABLE_CLIP_RECT
float4 pos = RENDER_TARGET_COORD_TO_CLIP_COORD(vertexPosition);
#ifdef @POST_INVERT_Y
pos.y = -pos.y;
#endif
#ifdef @RENDER_MODE_MSAA
pos.z = normalize_z_index(imageDrawUniforms.zIndex);
#endif
VARYING_PACK(v_texCoord);
#ifdef @ENABLE_CLIPPING
VARYING_PACK(v_clipID);
#endif
#ifdef @ENABLE_CLIP_RECT
VARYING_PACK(v_clipRect);
#endif
EMIT_VERTEX(pos);
}
#endif
#ifdef @FRAGMENT
FRAG_TEXTURE_BLOCK_BEGIN
TEXTURE_RGBA8(PER_DRAW_BINDINGS_SET, IMAGE_TEXTURE_IDX, @imageTexture);
#if defined(@RENDER_MODE_MSAA) && defined(@ENABLE_ADVANCED_BLEND)
DST_COLOR_TEXTURE(@dstColorTexture);
#endif
FRAG_TEXTURE_BLOCK_END
DYNAMIC_SAMPLER_BLOCK_BEGIN
SAMPLER_DYNAMIC(PER_DRAW_BINDINGS_SET, IMAGE_SAMPLER_IDX, imageSampler)
DYNAMIC_SAMPLER_BLOCK_END
FRAG_STORAGE_BUFFER_BLOCK_BEGIN
FRAG_STORAGE_BUFFER_BLOCK_END
#ifndef @RENDER_MODE_MSAA
PLS_BLOCK_BEGIN
PLS_DECL4F(COLOR_PLANE_IDX, colorBuffer);
PLS_DECLUI(CLIP_PLANE_IDX, clipBuffer);
PLS_DECL4F(SCRATCH_COLOR_PLANE_IDX, scratchColorBuffer);
PLS_DECLUI(COVERAGE_PLANE_IDX, coverageCountBuffer);
PLS_BLOCK_END
PLS_MAIN_WITH_IMAGE_UNIFORMS(@drawFragmentMain)
{
VARYING_UNPACK(v_texCoord, float2);
#ifdef @ENABLE_CLIPPING
VARYING_UNPACK(v_clipID, half);
#endif
#ifdef @ENABLE_CLIP_RECT
VARYING_UNPACK(v_clipRect, float4);
#endif
half4 color = TEXTURE_SAMPLE_DYNAMIC_LODBIAS(@imageTexture,
imageSampler,
v_texCoord,
uniforms.mipMapLODBias);
half coverage = 1.;
#ifdef @ENABLE_CLIP_RECT
if (@ENABLE_CLIP_RECT)
{
half clipRectCoverage = min_value(cast_float4_to_half4(v_clipRect));
coverage = clamp(clipRectCoverage, make_half(.0), coverage);
}
#endif
PLS_INTERLOCK_BEGIN;
#ifdef @ENABLE_CLIPPING
if (@ENABLE_CLIPPING && v_clipID != .0)
{
half2 clipData = unpackHalf2x16(PLS_LOADUI(clipBuffer));
half clipContentID = clipData.g;
half clipCoverage =
clipContentID == v_clipID ? clipData.r : make_half(.0);
coverage = min(coverage, clipCoverage);
}
#endif
// Blend with the framebuffer color.
half4 dstColorPremul = PLS_LOAD4F(colorBuffer);
#ifdef @ENABLE_ADVANCED_BLEND
if (@ENABLE_ADVANCED_BLEND && imageDrawUniforms.blendMode != BLEND_SRC_OVER)
{
color.rgb = advanced_color_blend(
unmultiply_rgb(color),
dstColorPremul,
cast_uint_to_ushort(imageDrawUniforms.blendMode)) *
color.a;
}
#endif
color *= imageDrawUniforms.opacity * coverage;
color += dstColorPremul * (1. - color.a);
PLS_STORE4F(colorBuffer, color);
PLS_PRESERVE_UI(clipBuffer);
PLS_PRESERVE_UI(coverageCountBuffer);
PLS_INTERLOCK_END;
EMIT_PLS;
}
#else // !@RENDER_MODE_MSAA => @RENDER_MODE_MSAA
FRAG_DATA_MAIN(half4, @drawFragmentMain)
{
VARYING_UNPACK(v_texCoord, float2);
half4 color = TEXTURE_SAMPLE_DYNAMIC_LODBIAS(@imageTexture,
imageSampler,
v_texCoord,
uniforms.mipMapLODBias) *
imageDrawUniforms.opacity;
#if defined(@ENABLE_ADVANCED_BLEND) && !defined(@FIXED_FUNCTION_COLOR_OUTPUT)
if (@ENABLE_ADVANCED_BLEND)
{
// Do the color portion of the blend mode in the shader.
half4 dstColorPremul = DST_COLOR_FETCH(@dstColorTexture);
color.rgb = advanced_color_blend(unmultiply_rgb(color),
dstColorPremul,
imageDrawUniforms.blendMode);
// Src-over blending is enabled, so just premultiply and let the HW
// finish the the the alpha portion of the blend mode.
color.rgb *= color.a;
}
#endif // @ENABLE_ADVANCED_BLEND && !@FIXED_FUNCTION_COLOR_OUTPUT
EMIT_FRAG_DATA(color);
}
#endif // @RENDER_MODE_MSAA
#endif // FRAGMENT

View File

@@ -0,0 +1,91 @@
/*
* Copyright 2023 Rive
*/
#ifdef @VERTEX
ATTR_BLOCK_BEGIN(PositionAttr)
ATTR(0, float2, @a_position);
ATTR_BLOCK_END
ATTR_BLOCK_BEGIN(UVAttr)
ATTR(1, float2, @a_texCoord);
ATTR_BLOCK_END
#endif
VARYING_BLOCK_BEGIN
NO_PERSPECTIVE VARYING(0, float2, v_texCoord);
#ifdef @ENABLE_CLIPPING
@OPTIONALLY_FLAT VARYING(1, half, v_clipID);
#endif
#if defined(@ENABLE_CLIP_RECT) && !defined(@RENDER_MODE_MSAA)
NO_PERSPECTIVE VARYING(2, float4, v_clipRect);
#endif
VARYING_BLOCK_END
#ifdef @VERTEX
VERTEX_TEXTURE_BLOCK_BEGIN
VERTEX_TEXTURE_BLOCK_END
IMAGE_MESH_VERTEX_MAIN(@drawVertexMain,
PositionAttr,
position,
UVAttr,
uv,
_vertexID)
{
ATTR_UNPACK(_vertexID, position, @a_position, float2);
ATTR_UNPACK(_vertexID, uv, @a_texCoord, float2);
VARYING_INIT(v_texCoord, float2);
#ifdef @ENABLE_CLIPPING
VARYING_INIT(v_clipID, half);
#endif
#if defined(@ENABLE_CLIP_RECT) && !defined(@RENDER_MODE_MSAA)
VARYING_INIT(v_clipRect, float4);
#endif
float2 vertexPosition =
MUL(make_float2x2(imageDrawUniforms.viewMatrix), @a_position) +
imageDrawUniforms.translate;
v_texCoord = @a_texCoord;
#ifdef @ENABLE_CLIPPING
if (@ENABLE_CLIPPING)
{
v_clipID = id_bits_to_f16(imageDrawUniforms.clipID,
uniforms.pathIDGranularity);
}
#endif
#ifdef @ENABLE_CLIP_RECT
if (@ENABLE_CLIP_RECT)
{
#ifndef @RENDER_MODE_MSAA
v_clipRect = find_clip_rect_coverage_distances(
make_float2x2(imageDrawUniforms.clipRectInverseMatrix),
imageDrawUniforms.clipRectInverseTranslate,
vertexPosition);
#else
set_clip_rect_plane_distances(
make_float2x2(imageDrawUniforms.clipRectInverseMatrix),
imageDrawUniforms.clipRectInverseTranslate,
vertexPosition);
#endif
}
#endif // ENABLE_CLIP_RECT
float4 pos = RENDER_TARGET_COORD_TO_CLIP_COORD(vertexPosition);
#ifdef @POST_INVERT_Y
pos.y = -pos.y;
#endif
#ifdef @RENDER_MODE_MSAA
pos.z = normalize_z_index(imageDrawUniforms.zIndex);
#endif
VARYING_PACK(v_texCoord);
#ifdef @ENABLE_CLIPPING
VARYING_PACK(v_clipID);
#endif
#ifdef @ENABLE_CLIP_RECT
VARYING_PACK(v_clipRect);
#endif
EMIT_VERTEX(pos);
}
#endif

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2023 Rive
*/
#ifdef @FRAGMENT
FRAG_TEXTURE_BLOCK_BEGIN
TEXTURE_RGBA8(PER_DRAW_BINDINGS_SET, IMAGE_TEXTURE_IDX, @imageTexture);
#ifdef @ENABLE_ADVANCED_BLEND
DST_COLOR_TEXTURE(@dstColorTexture);
#endif
FRAG_TEXTURE_BLOCK_END
DYNAMIC_SAMPLER_BLOCK_BEGIN
SAMPLER_DYNAMIC(PER_DRAW_BINDINGS_SET, IMAGE_SAMPLER_IDX, imageSampler)
DYNAMIC_SAMPLER_BLOCK_END
FRAG_STORAGE_BUFFER_BLOCK_BEGIN
FRAG_STORAGE_BUFFER_BLOCK_END
FRAG_DATA_MAIN(half4, @drawFragmentMain)
{
VARYING_UNPACK(v_texCoord, float2);
half4 color = TEXTURE_SAMPLE_DYNAMIC_LODBIAS(@imageTexture,
imageSampler,
v_texCoord,
uniforms.mipMapLODBias) *
imageDrawUniforms.opacity;
#if defined(@ENABLE_ADVANCED_BLEND) && !defined(@FIXED_FUNCTION_COLOR_OUTPUT)
if (@ENABLE_ADVANCED_BLEND)
{
// Do the color portion of the blend mode in the shader.
half4 dstColorPremul = DST_COLOR_FETCH(@dstColorTexture);
color.rgb = advanced_color_blend(unmultiply_rgb(color),
dstColorPremul,
imageDrawUniforms.blendMode);
// Src-over blending is enabled, so just premultiply and let the HW
// finish the the the alpha portion of the blend mode.
color.rgb *= color.a;
}
#endif // @ENABLE_ADVANCED_BLEND && !@FIXED_FUNCTION_COLOR_OUTPUT
EMIT_FRAG_DATA(color);
}
#endif // @FRAGMENT

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2022 Rive
*/
#ifdef @FRAGMENT
FRAG_DATA_MAIN(half4, @drawFragmentMain)
{
VARYING_UNPACK(v_paint, float4);
#ifdef @ATLAS_BLIT
VARYING_UNPACK(v_atlasCoord, float2);
#endif
#ifdef @ENABLE_ADVANCED_BLEND
VARYING_UNPACK(v_blendMode, half);
#endif
half coverage =
#ifdef @ATLAS_BLIT
filter_feather_atlas(
v_atlasCoord,
uniforms.atlasTextureInverseSize TEXTURE_CONTEXT_FORWARD);
#else
1.;
#endif
half4 color = find_paint_color(v_paint, coverage FRAGMENT_CONTEXT_UNPACK);
#if defined(@ENABLE_ADVANCED_BLEND) && !defined(@FIXED_FUNCTION_COLOR_OUTPUT)
if (@ENABLE_ADVANCED_BLEND)
{
// Do the color portion of the blend mode in the shader.
//
// NOTE: "color" is already unmultiplied because
// GENERATE_PREMULTIPLIED_PAINT_COLORS is false when using advanced
// blend.
half4 dstColorPremul = DST_COLOR_FETCH(@dstColorTexture);
color.rgb = advanced_color_blend(color.rgb,
dstColorPremul,
cast_half_to_ushort(v_blendMode));
// Src-over blending is enabled, so just premultiply and let the HW
// finish the the the alpha portion of the blend mode.
color.rgb *= color.a;
}
#endif // @ENABLE_ADVANCED_BLEND && !@FIXED_FUNCTION_COLOR_OUTPUT
// Certain platforms give us less control of the format of what we are
// rendering too. Specifically, we are auto converted from linear -> sRGB on
// render target writes in unreal. In those cases we made need to end up in
// linear color space
#ifdef @NEEDS_GAMMA_CORRECTION
if (@NEEDS_GAMMA_CORRECTION)
{
color = gamma_to_linear(color);
}
#endif
EMIT_FRAG_DATA(color);
}
#endif // FRAGMENT

View File

@@ -2,6 +2,9 @@
* Copyright 2022 Rive
*/
// undef GENERATE_PREMULTIPLIED_PAINT_COLORS first because this file gets
// included multiple times with different defines in the Metal library.
#undef GENERATE_PREMULTIPLIED_PAINT_COLORS
#ifdef @ENABLE_ADVANCED_BLEND
// If advanced blend is enabled, we generate unmultiplied paint colors in the
// shader. Otherwise we would have to just turn around and unmultiply them in
@@ -13,6 +16,15 @@
#define GENERATE_PREMULTIPLIED_PAINT_COLORS true
#endif
// undef COVERAGE_TYPE first because this file gets included multiple times with
// different defines in the Metal library.
#undef COVERAGE_TYPE
#ifdef @ENABLE_FEATHER
#define COVERAGE_TYPE float4
#else
#define COVERAGE_TYPE half2
#endif
#ifdef @VERTEX
ATTR_BLOCK_BEGIN(Attrs)
#ifdef @DRAW_INTERIOR_TRIANGLES
@@ -34,10 +46,8 @@ NO_PERSPECTIVE VARYING(1, float2, v_atlasCoord);
#elif !defined(@RENDER_MODE_MSAA)
#ifdef @DRAW_INTERIOR_TRIANGLES
@OPTIONALLY_FLAT VARYING(1, half, v_windingWeight);
#elif defined(@ENABLE_FEATHER)
NO_PERSPECTIVE VARYING(2, float4, v_coverages);
#else
NO_PERSPECTIVE VARYING(2, half2, v_coverages);
NO_PERSPECTIVE VARYING(2, COVERAGE_TYPE, v_coverages);
#endif //@DRAW_INTERIOR_TRIANGLES
@OPTIONALLY_FLAT VARYING(3, half, v_pathID);
#endif // !@RENDER_MODE_MSAA
@@ -70,10 +80,8 @@ VERTEX_MAIN(@drawVertexMain, Attrs, attrs, _vertexID, _instanceID)
#elif !defined(@RENDER_MODE_MSAA)
#ifdef @DRAW_INTERIOR_TRIANGLES
VARYING_INIT(v_windingWeight, half);
#elif defined(@ENABLE_FEATHER)
VARYING_INIT(v_coverages, float4);
#else
VARYING_INIT(v_coverages, half2);
VARYING_INIT(v_coverages, COVERAGE_TYPE);
#endif //@DRAW_INTERIOR_TRIANGLES
VARYING_INIT(v_pathID, half);
#endif // !@RENDER_MODE_MSAA
@@ -304,8 +312,6 @@ VERTEX_MAIN(@drawVertexMain, Attrs, attrs, _vertexID, _instanceID)
#elif !defined(@RENDER_MODE_MSAA)
#ifdef @DRAW_INTERIOR_TRIANGLES
VARYING_PACK(v_windingWeight);
#elif defined(@ENABLE_FEATHER)
VARYING_PACK(v_coverages);
#else
VARYING_PACK(v_coverages);
#endif //@DRAW_INTERIOR_TRIANGLES
@@ -326,9 +332,12 @@ VERTEX_MAIN(@drawVertexMain, Attrs, attrs, _vertexID, _instanceID)
#endif
#ifdef @FRAGMENT
FRAG_STORAGE_BUFFER_BLOCK_BEGIN
FRAG_STORAGE_BUFFER_BLOCK_END
// Add a function here for fragments to unpack the paint since we're the ones
// who packed it in the vertex shader.
INLINE half4 find_paint_color(float4 paint,
float coverage FRAGMENT_CONTEXT_DECL)
{
@@ -382,339 +391,55 @@ INLINE half4 find_paint_color(float4 paint,
return color;
}
#ifndef @RENDER_MODE_MSAA
#ifndef @DRAW_INTERIOR_TRIANGLES
PLS_BLOCK_BEGIN
PLS_DECL4F(COLOR_PLANE_IDX, colorBuffer);
PLS_DECLUI(CLIP_PLANE_IDX, clipBuffer);
PLS_DECL4F(SCRATCH_COLOR_PLANE_IDX, scratchColorBuffer);
PLS_DECLUI(COVERAGE_PLANE_IDX, coverageCountBuffer);
PLS_BLOCK_END
PLS_MAIN(@drawFragmentMain)
// Add functions here for fragments to unpack and evaluate coverage since we're
// the ones who packed the coverage components in the vertex shader.
INLINE half find_stroke_coverage(COVERAGE_TYPE coverages TEXTURE_CONTEXT_DECL)
{
VARYING_UNPACK(v_paint, float4);
#ifdef @ATLAS_BLIT
VARYING_UNPACK(v_atlasCoord, float2);
#elif !defined(@RENDER_MODE_MSAA)
#ifdef @DRAW_INTERIOR_TRIANGLES
VARYING_UNPACK(v_windingWeight, half);
#elif defined(@ENABLE_FEATHER)
VARYING_UNPACK(v_coverages, float4);
#else
VARYING_UNPACK(v_coverages, half2);
#endif //@DRAW_INTERIOR_TRIANGLES
VARYING_UNPACK(v_pathID, half);
#endif // !@RENDER_MODE_MSAA
#ifdef @ENABLE_CLIPPING
VARYING_UNPACK(v_clipIDs, half2);
#endif
#ifdef @ENABLE_CLIP_RECT
VARYING_UNPACK(v_clipRect, float4);
#endif
#ifdef @ENABLE_ADVANCED_BLEND
VARYING_UNPACK(v_blendMode, half);
#endif
#if !defined(@DRAW_INTERIOR_TRIANGLES) || defined(@ATLAS_BLIT)
// Interior triangles don't overlap, so don't need raster ordering.
PLS_INTERLOCK_BEGIN;
#endif
half coverage;
#ifdef @ATLAS_BLIT
coverage = filter_feather_atlas(
v_atlasCoord,
uniforms.atlasTextureInverseSize TEXTURE_CONTEXT_FORWARD);
#else
half2 coverageData = unpackHalf2x16(PLS_LOADUI(coverageCountBuffer));
half coverageBufferID = coverageData.g;
half coverageCount =
coverageBufferID == v_pathID ? coverageData.r : make_half(.0);
#ifdef @DRAW_INTERIOR_TRIANGLES
coverageCount += v_windingWeight;
PLS_PRESERVE_UI(coverageCountBuffer);
#else
if (is_stroke(v_coverages))
{
half fragCoverage;
#ifdef @ENABLE_FEATHER
if (@ENABLE_FEATHER && is_feathered_stroke(v_coverages))
{
fragCoverage =
eval_feathered_stroke(v_coverages TEXTURE_CONTEXT_FORWARD);
}
else
if (@ENABLE_FEATHER && is_feathered_stroke(coverages))
return eval_feathered_stroke(coverages TEXTURE_CONTEXT_FORWARD);
else
#endif // @ENABLE_FEATHER
{
fragCoverage = min(v_coverages.x, v_coverages.y);
}
coverageCount = max(fragCoverage, coverageCount);
}
else // Fill. (Back-face culling handles the sign of v_coverages.x.)
{
half fragCoverage;
#if defined(@ENABLE_FEATHER)
if (@ENABLE_FEATHER && is_feathered_fill(v_coverages))
{
fragCoverage =
eval_feathered_fill(v_coverages TEXTURE_CONTEXT_FORWARD);
}
else
#endif // @CLOCKWISE_FILL && @ENABLE_FEATHER
{
fragCoverage = v_coverages.x;
}
coverageCount += fragCoverage;
}
return min(coverages.x, coverages.y);
}
INLINE half find_fill_coverage(COVERAGE_TYPE coverages TEXTURE_CONTEXT_DECL)
{
#if defined(@ENABLE_FEATHER)
if (@ENABLE_FEATHER && is_feathered_fill(coverages))
return eval_feathered_fill(coverages TEXTURE_CONTEXT_FORWARD);
else
#endif // @ENABLE_FEATHER
return coverages.x;
}
INLINE half find_frag_coverage(COVERAGE_TYPE coverages TEXTURE_CONTEXT_DECL)
{
if (is_stroke(coverages))
return find_stroke_coverage(coverages TEXTURE_CONTEXT_FORWARD);
else // Fill. (Back-face culling handles the sign of coverages.x.)
return find_fill_coverage(coverages TEXTURE_CONTEXT_FORWARD);
}
INLINE half apply_frag_coverage(half initialCoverage,
COVERAGE_TYPE coverages TEXTURE_CONTEXT_DECL)
{
if (is_stroke(coverages))
{
half fragCoverage =
find_stroke_coverage(coverages TEXTURE_CONTEXT_FORWARD);
return max(fragCoverage, initialCoverage);
}
else // Fill. (Back-face culling handles the sign of coverages.x.)
{
half fragCoverage =
find_fill_coverage(coverages TEXTURE_CONTEXT_FORWARD);
return initialCoverage + fragCoverage;
}
}
// Save the updated coverage.
PLS_STOREUI(coverageCountBuffer,
packHalf2x16(make_half2(coverageCount, v_pathID)));
#endif // !@DRAW_INTERIOR_TRIANGLES
// Convert coverageCount to coverage.
#ifdef @CLOCKWISE_FILL
if (@CLOCKWISE_FILL)
{
#ifdef @VULKAN_VENDOR_ID
if (@VULKAN_VENDOR_ID == VULKAN_VENDOR_ARM)
{
// ARM hits a bug if we use clamp() here.
if (coverageCount < .0)
coverage = .0;
else if (coverageCount <= 1.)
coverage = coverageCount;
else
coverage = 1.;
}
else
#endif
{
coverage = clamp(coverageCount, make_half(.0), make_half(1.));
}
}
else
#endif // CLOCKWISE_FILL
{
coverage = abs(coverageCount);
#ifdef @ENABLE_EVEN_ODD
if (@ENABLE_EVEN_ODD && v_pathID < .0 /*even-odd*/)
{
coverage = 1. - make_half(abs(fract(coverage * .5) * 2. + -1.));
}
#endif
// This also caps stroke coverage, which can be >1.
coverage = min(coverage, make_half(1.));
}
#endif // !@ATLAS_BLIT
#ifdef @ENABLE_CLIPPING
if (@ENABLE_CLIPPING && v_clipIDs.x < .0) // Update the clip buffer.
{
half clipID = -v_clipIDs.x;
#ifdef @ENABLE_NESTED_CLIPPING
if (@ENABLE_NESTED_CLIPPING)
{
half outerClipID = v_clipIDs.y;
if (outerClipID != .0)
{
// This is a nested clip. Intersect coverage with the enclosing
// clip (outerClipID).
half2 clipData = unpackHalf2x16(PLS_LOADUI(clipBuffer));
half clipContentID = clipData.g;
half outerClipCoverage;
if (clipContentID != clipID)
{
// First hit: either clipBuffer contains outerClipCoverage,
// or this pixel is not inside the outer clip and
// outerClipCoverage is zero.
outerClipCoverage =
clipContentID == outerClipID ? clipData.r : .0;
#ifndef @DRAW_INTERIOR_TRIANGLES
// Stash outerClipCoverage before overwriting clipBuffer, in
// case we hit this pixel again and need it. (Not necessary
// when drawing interior triangles because they always go
// last and don't overlap.)
PLS_STORE4F(scratchColorBuffer,
make_half4(outerClipCoverage, .0, .0, .0));
#endif
}
else
{
// Subsequent hit: outerClipCoverage is stashed in
// scratchColorBuffer.
outerClipCoverage = PLS_LOAD4F(scratchColorBuffer).r;
#ifndef @DRAW_INTERIOR_TRIANGLES
// Since interior triangles are always last, there's no need
// to preserve this value.
PLS_PRESERVE_4F(scratchColorBuffer);
#endif
}
coverage = min(coverage, outerClipCoverage);
}
}
#endif // @ENABLE_NESTED_CLIPPING
PLS_STOREUI(clipBuffer, packHalf2x16(make_half2(coverage, clipID)));
PLS_PRESERVE_4F(colorBuffer);
}
else // Render to the main framebuffer.
#endif // @ENABLE_CLIPPING
{
#ifdef @ENABLE_CLIPPING
if (@ENABLE_CLIPPING)
{
// Apply the clip.
half clipID = v_clipIDs.x;
if (clipID != .0)
{
// Clip IDs are not necessarily drawn in monotonically
// increasing order, so always check exact equality of the
// clipID.
half2 clipData = unpackHalf2x16(PLS_LOADUI(clipBuffer));
half clipContentID = clipData.g;
coverage = (clipContentID == clipID) ? min(clipData.r, coverage)
: make_half(.0);
}
}
#endif
#ifdef @ENABLE_CLIP_RECT
if (@ENABLE_CLIP_RECT)
{
half clipRectCoverage = min_value(cast_float4_to_half4(v_clipRect));
coverage = clamp(clipRectCoverage, make_half(.0), coverage);
}
#endif // ENABLE_CLIP_RECT
half4 color =
find_paint_color(v_paint, coverage FRAGMENT_CONTEXT_UNPACK);
half4 dstColorPremul;
#ifdef @ATLAS_BLIT
dstColorPremul = PLS_LOAD4F(colorBuffer);
#else
if (coverageBufferID != v_pathID)
{
// This is the first fragment from pathID to touch this pixel.
dstColorPremul = PLS_LOAD4F(colorBuffer);
#ifndef @DRAW_INTERIOR_TRIANGLES
// We don't need to store coverage when drawing interior triangles
// because they always go last and don't overlap, so every fragment
// is the final one in the path.
PLS_STORE4F(scratchColorBuffer, dstColorPremul);
#endif
}
else
{
dstColorPremul = PLS_LOAD4F(scratchColorBuffer);
#ifndef @DRAW_INTERIOR_TRIANGLES
// Since interior triangles are always last, there's no need to
// preserve this value.
PLS_PRESERVE_4F(scratchColorBuffer);
#endif
}
#endif // @ATLAS_BLIT
// Blend with the framebuffer color.
#ifdef @ENABLE_ADVANCED_BLEND
if (@ENABLE_ADVANCED_BLEND)
{
// GENERATE_PREMULTIPLIED_PAINT_COLORS is false in this case because
// advanced blend needs unmultiplied colors.
if (v_blendMode != cast_uint_to_half(BLEND_SRC_OVER))
{
color.rgb =
advanced_color_blend(color.rgb,
dstColorPremul,
cast_half_to_ushort(v_blendMode));
}
// Premultiply alpha now.
color.rgb *= color.a;
}
#endif
// Certain platforms give us less control of the format of what we are
// rendering too. Specifically, we are auto converted from linear ->
// sRGB on render target writes in unreal. In those cases we made need
// to end up in linear color space
#ifdef @NEEDS_GAMMA_CORRECTION
if (@NEEDS_GAMMA_CORRECTION)
{
color = gamma_to_linear(color);
}
#endif
color += dstColorPremul * (1. - color.a);
PLS_STORE4F(colorBuffer, color);
PLS_PRESERVE_UI(clipBuffer);
}
#if !defined(@DRAW_INTERIOR_TRIANGLES) || defined(@ATLAS_BLIT)
// Interior triangles don't overlap, so don't need raster ordering.
PLS_INTERLOCK_END;
#endif
EMIT_PLS;
}
#else // !@RENDER_MODE_MSAA => @RENDER_MODE_MSAA
FRAG_DATA_MAIN(half4, @drawFragmentMain)
{
VARYING_UNPACK(v_paint, float4);
#ifdef @ATLAS_BLIT
VARYING_UNPACK(v_atlasCoord, float2);
#endif
#ifdef @ENABLE_ADVANCED_BLEND
VARYING_UNPACK(v_blendMode, half);
#endif
half coverage =
#ifdef @ATLAS_BLIT
filter_feather_atlas(
v_atlasCoord,
uniforms.atlasTextureInverseSize TEXTURE_CONTEXT_FORWARD);
#else
1.;
#endif
half4 color = find_paint_color(v_paint, coverage FRAGMENT_CONTEXT_UNPACK);
#if defined(@ENABLE_ADVANCED_BLEND) && !defined(@FIXED_FUNCTION_COLOR_OUTPUT)
if (@ENABLE_ADVANCED_BLEND)
{
// Do the color portion of the blend mode in the shader.
//
// NOTE: "color" is already unmultiplied because
// GENERATE_PREMULTIPLIED_PAINT_COLORS is false when using advanced
// blend.
half4 dstColorPremul = DST_COLOR_FETCH(@dstColorTexture);
color.rgb = advanced_color_blend(color.rgb,
dstColorPremul,
cast_half_to_ushort(v_blendMode));
// Src-over blending is enabled, so just premultiply and let the HW
// finish the the the alpha portion of the blend mode.
color.rgb *= color.a;
}
#endif // @ENABLE_ADVANCED_BLEND && !@FIXED_FUNCTION_COLOR_OUTPUT
// Certain platforms give us less control of the format of what we are
// rendering too. Specifically, we are auto converted from linear -> sRGB on
// render target writes in unreal. In those cases we made need to end up in
// linear color space
#ifdef @NEEDS_GAMMA_CORRECTION
if (@NEEDS_GAMMA_CORRECTION)
{
color = gamma_to_linear(color);
}
#endif
EMIT_FRAG_DATA(color);
}
#endif // @RENDER_MODE_MSAA
#endif // FRAGMENT
#endif // @FRAGMENT

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2023 Rive
*/
#ifdef @FRAGMENT
PLS_BLOCK_BEGIN
PLS_DECL4F(COLOR_PLANE_IDX, colorBuffer);
PLS_DECLUI(CLIP_PLANE_IDX, clipBuffer);
PLS_DECL4F(SCRATCH_COLOR_PLANE_IDX, scratchColorBuffer);
PLS_DECLUI(COVERAGE_PLANE_IDX, coverageCountBuffer);
PLS_BLOCK_END
FRAG_TEXTURE_BLOCK_BEGIN
TEXTURE_RGBA8(PER_DRAW_BINDINGS_SET, IMAGE_TEXTURE_IDX, @imageTexture);
FRAG_TEXTURE_BLOCK_END
DYNAMIC_SAMPLER_BLOCK_BEGIN
SAMPLER_DYNAMIC(PER_DRAW_BINDINGS_SET, IMAGE_SAMPLER_IDX, imageSampler)
DYNAMIC_SAMPLER_BLOCK_END
FRAG_STORAGE_BUFFER_BLOCK_BEGIN
FRAG_STORAGE_BUFFER_BLOCK_END
PLS_MAIN_WITH_IMAGE_UNIFORMS(@drawFragmentMain)
{
VARYING_UNPACK(v_texCoord, float2);
#ifdef @ENABLE_CLIPPING
VARYING_UNPACK(v_clipID, half);
#endif
#ifdef @ENABLE_CLIP_RECT
VARYING_UNPACK(v_clipRect, float4);
#endif
half4 color = TEXTURE_SAMPLE_DYNAMIC_LODBIAS(@imageTexture,
imageSampler,
v_texCoord,
uniforms.mipMapLODBias);
half coverage = 1.;
#ifdef @ENABLE_CLIP_RECT
if (@ENABLE_CLIP_RECT)
{
half clipRectCoverage = min_value(cast_float4_to_half4(v_clipRect));
coverage = clamp(clipRectCoverage, make_half(.0), coverage);
}
#endif
PLS_INTERLOCK_BEGIN;
#ifdef @ENABLE_CLIPPING
if (@ENABLE_CLIPPING && v_clipID != .0)
{
half2 clipData = unpackHalf2x16(PLS_LOADUI(clipBuffer));
half clipContentID = clipData.g;
half clipCoverage =
clipContentID == v_clipID ? clipData.r : make_half(.0);
coverage = min(coverage, clipCoverage);
}
#endif
// Blend with the framebuffer color.
half4 dstColorPremul = PLS_LOAD4F(colorBuffer);
#ifdef @ENABLE_ADVANCED_BLEND
if (@ENABLE_ADVANCED_BLEND && imageDrawUniforms.blendMode != BLEND_SRC_OVER)
{
color.rgb = advanced_color_blend(
unmultiply_rgb(color),
dstColorPremul,
cast_uint_to_ushort(imageDrawUniforms.blendMode)) *
color.a;
}
#endif
color *= imageDrawUniforms.opacity * coverage;
color += dstColorPremul * (1. - color.a);
PLS_STORE4F(colorBuffer, color);
PLS_PRESERVE_UI(clipBuffer);
PLS_PRESERVE_UI(coverageCountBuffer);
PLS_INTERLOCK_END;
EMIT_PLS;
}
#endif // @FRAGMENT

View File

@@ -0,0 +1,254 @@
/*
* Copyright 2022 Rive
*/
#ifdef @FRAGMENT
PLS_BLOCK_BEGIN
PLS_DECL4F(COLOR_PLANE_IDX, colorBuffer);
PLS_DECLUI(CLIP_PLANE_IDX, clipBuffer);
PLS_DECL4F(SCRATCH_COLOR_PLANE_IDX, scratchColorBuffer);
PLS_DECLUI(COVERAGE_PLANE_IDX, coverageCountBuffer);
PLS_BLOCK_END
PLS_MAIN(@drawFragmentMain)
{
VARYING_UNPACK(v_paint, float4);
#ifdef @ATLAS_BLIT
VARYING_UNPACK(v_atlasCoord, float2);
#else
#ifdef @DRAW_INTERIOR_TRIANGLES
VARYING_UNPACK(v_windingWeight, half);
#else
VARYING_UNPACK(v_coverages, COVERAGE_TYPE);
#endif //@DRAW_INTERIOR_TRIANGLES
VARYING_UNPACK(v_pathID, half);
#endif
#ifdef @ENABLE_CLIPPING
VARYING_UNPACK(v_clipIDs, half2);
#endif
#ifdef @ENABLE_CLIP_RECT
VARYING_UNPACK(v_clipRect, float4);
#endif
#ifdef @ENABLE_ADVANCED_BLEND
VARYING_UNPACK(v_blendMode, half);
#endif
#if !defined(@DRAW_INTERIOR_TRIANGLES) || defined(@ATLAS_BLIT)
// Interior triangles don't overlap, so don't need raster ordering.
PLS_INTERLOCK_BEGIN;
#endif
half coverage;
#ifdef @ATLAS_BLIT
coverage = filter_feather_atlas(
v_atlasCoord,
uniforms.atlasTextureInverseSize TEXTURE_CONTEXT_FORWARD);
#else
half2 coverageData = unpackHalf2x16(PLS_LOADUI(coverageCountBuffer));
half coverageBufferID = coverageData.g;
half coverageCount =
coverageBufferID == v_pathID ? coverageData.r : make_half(.0);
#ifdef @DRAW_INTERIOR_TRIANGLES
coverageCount += v_windingWeight;
// Preserve the coverage buffer even though we don't use it, so it doesn't
// get overwritten in a way that would corrupt a future draw (e.g., by
// accidentally writing the next path's id with a bogus coverage.)
PLS_PRESERVE_UI(coverageCountBuffer);
#else
coverageCount =
apply_frag_coverage(coverageCount, v_coverages TEXTURE_CONTEXT_FORWARD);
// Save the updated coverage.
PLS_STOREUI(coverageCountBuffer,
packHalf2x16(make_half2(coverageCount, v_pathID)));
#endif // !@DRAW_INTERIOR_TRIANGLES
// Convert coverageCount to coverage.
#ifdef @CLOCKWISE_FILL
if (@CLOCKWISE_FILL)
{
#ifdef @VULKAN_VENDOR_ID
if (@VULKAN_VENDOR_ID == VULKAN_VENDOR_ARM)
{
// ARM hits a bug if we use clamp() here.
if (coverageCount < .0)
coverage = .0;
else if (coverageCount <= 1.)
coverage = coverageCount;
else
coverage = 1.;
}
else
#endif
{
coverage = clamp(coverageCount, make_half(.0), make_half(1.));
}
}
else
#endif // CLOCKWISE_FILL
{
coverage = abs(coverageCount);
#ifdef @ENABLE_EVEN_ODD
if (@ENABLE_EVEN_ODD && v_pathID < .0 /*even-odd*/)
{
coverage = 1. - make_half(abs(fract(coverage * .5) * 2. + -1.));
}
#endif
// This also caps stroke coverage, which can be >1.
coverage = min(coverage, make_half(1.));
}
#endif // !@ATLAS_BLIT
#ifdef @ENABLE_CLIPPING
if (@ENABLE_CLIPPING && v_clipIDs.x < .0) // Update the clip buffer.
{
half clipID = -v_clipIDs.x;
#ifdef @ENABLE_NESTED_CLIPPING
if (@ENABLE_NESTED_CLIPPING)
{
half outerClipID = v_clipIDs.y;
if (outerClipID != .0)
{
// This is a nested clip. Intersect coverage with the enclosing
// clip (outerClipID).
half2 clipData = unpackHalf2x16(PLS_LOADUI(clipBuffer));
half clipContentID = clipData.g;
half outerClipCoverage;
if (clipContentID != clipID)
{
// First hit: either clipBuffer contains outerClipCoverage,
// or this pixel is not inside the outer clip and
// outerClipCoverage is zero.
outerClipCoverage =
clipContentID == outerClipID ? clipData.r : .0;
#ifndef @DRAW_INTERIOR_TRIANGLES
// Stash outerClipCoverage before overwriting clipBuffer, in
// case we hit this pixel again and need it. (Not necessary
// when drawing interior triangles because they always go
// last and don't overlap.)
PLS_STORE4F(scratchColorBuffer,
make_half4(outerClipCoverage, .0, .0, .0));
#endif
}
else
{
// Subsequent hit: outerClipCoverage is stashed in
// scratchColorBuffer.
outerClipCoverage = PLS_LOAD4F(scratchColorBuffer).r;
#ifndef @DRAW_INTERIOR_TRIANGLES
// Since interior triangles are always last, there's no need
// to preserve this value.
PLS_PRESERVE_4F(scratchColorBuffer);
#endif
}
coverage = min(coverage, outerClipCoverage);
}
}
#endif // @ENABLE_NESTED_CLIPPING
PLS_STOREUI(clipBuffer, packHalf2x16(make_half2(coverage, clipID)));
PLS_PRESERVE_4F(colorBuffer);
}
else // Render to the main framebuffer.
#endif // @ENABLE_CLIPPING
{
#ifdef @ENABLE_CLIPPING
if (@ENABLE_CLIPPING)
{
// Apply the clip.
half clipID = v_clipIDs.x;
if (clipID != .0)
{
// Clip IDs are not necessarily drawn in monotonically
// increasing order, so always check exact equality of the
// clipID.
half2 clipData = unpackHalf2x16(PLS_LOADUI(clipBuffer));
half clipContentID = clipData.g;
coverage = (clipContentID == clipID) ? min(clipData.r, coverage)
: make_half(.0);
}
}
#endif
#ifdef @ENABLE_CLIP_RECT
if (@ENABLE_CLIP_RECT)
{
half clipRectCoverage = min_value(cast_float4_to_half4(v_clipRect));
coverage = clamp(clipRectCoverage, make_half(.0), coverage);
}
#endif // ENABLE_CLIP_RECT
half4 color =
find_paint_color(v_paint, coverage FRAGMENT_CONTEXT_UNPACK);
half4 dstColorPremul;
#ifdef @ATLAS_BLIT
dstColorPremul = PLS_LOAD4F(colorBuffer);
#else
if (coverageBufferID != v_pathID)
{
// This is the first fragment from pathID to touch this pixel.
dstColorPremul = PLS_LOAD4F(colorBuffer);
#ifndef @DRAW_INTERIOR_TRIANGLES
// We don't need to store coverage when drawing interior triangles
// because they always go last and don't overlap, so every fragment
// is the final one in the path.
PLS_STORE4F(scratchColorBuffer, dstColorPremul);
#endif
}
else
{
dstColorPremul = PLS_LOAD4F(scratchColorBuffer);
#ifndef @DRAW_INTERIOR_TRIANGLES
// Since interior triangles are always last, there's no need to
// preserve this value.
PLS_PRESERVE_4F(scratchColorBuffer);
#endif
}
#endif // @ATLAS_BLIT
// Blend with the framebuffer color.
#ifdef @ENABLE_ADVANCED_BLEND
if (@ENABLE_ADVANCED_BLEND)
{
// GENERATE_PREMULTIPLIED_PAINT_COLORS is false in this case because
// advanced blend needs unmultiplied colors.
if (v_blendMode != cast_uint_to_half(BLEND_SRC_OVER))
{
color.rgb =
advanced_color_blend(color.rgb,
dstColorPremul,
cast_half_to_ushort(v_blendMode));
}
// Premultiply alpha now.
color.rgb *= color.a;
}
#endif
// Certain platforms give us less control of the format of what we are
// rendering too. Specifically, we are auto converted from linear ->
// sRGB on render target writes in unreal. In those cases we made need
// to end up in linear color space
#ifdef @NEEDS_GAMMA_CORRECTION
if (@NEEDS_GAMMA_CORRECTION)
{
color = gamma_to_linear(color);
}
#endif
color += dstColorPremul * (1. - color.a);
PLS_STORE4F(colorBuffer, color);
PLS_PRESERVE_UI(clipBuffer);
}
#if !defined(@DRAW_INTERIOR_TRIANGLES) || defined(@ATLAS_BLIT)
// Interior triangles don't overlap, so don't need raster ordering.
PLS_INTERLOCK_END;
#endif
EMIT_PLS;
}
#endif // FRAGMENT

View File

@@ -87,12 +87,14 @@ def emit_shader(out, shader_type, draw_type, fill_type, feature_set):
('c' if fill_type == FillType.CLOCKWISE else 'p',
''.join(namespace_id)))
out.write('{\n')
out.write('#include "draw_path.minified.glsl"\n')
out.write('#include "draw_path.minified.vert"\n')
out.write('#include "draw_raster_order_path.minified.frag"\n')
out.write('}\n')
else:
out.write('namespace m%s\n' % ''.join(namespace_id))
out.write('{\n')
out.write('#include "draw_image_mesh.minified.glsl"\n')
out.write('#include "draw_image_mesh.minified.vert"\n')
out.write('#include "draw_raster_order_image_mesh.minified.frag"\n')
out.write('}\n')
for feature in feature_set:
out.write('#undef %s\n' % feature.name)

View File

@@ -549,8 +549,8 @@ class Minifier:
def write_exports(self, outdir):
output_path = os.path.join(outdir, os.path.splitext(self.basename)[0] + ".exports.h")
print("Exporting %s <- %s" % (output_path, self.basename))
output_path = os.path.join(outdir, f"{self.basename}.exports.h")
print(f"Exporting {output_path} <- {self.basename}")
out = open(output_path, "w", newline='\n')
out.write('#pragma once\n\n')
for exp in sorted(self.exports):
@@ -565,13 +565,17 @@ class Minifier:
out = open(output_path, "w", newline='\n')
out.write("#pragma once\n\n")
out.write('#include "%s"\n\n' % (os.path.splitext(self.basename)[0] + ".exports.h"))
out.write(f'#include "{self.basename}.exports.h"\n\n')
# emit shader code.
root, ext = os.path.splitext(self.basename)
cpp_name = root
if ext != '.glsl':
cpp_name = f"{root}_{ext[1:]}"
out.write("namespace rive {\n")
out.write("namespace gpu {\n")
out.write("namespace glsl {\n")
out.write('const char %s[] = R"===(' % os.path.splitext(self.basename)[0])
out.write(f'const char {cpp_name}[] = R"===(')
is_newline = self.emit_tokens_to_rewritten_glsl(out, preserve_exported_switches=False)
if not is_newline:
@@ -584,8 +588,9 @@ class Minifier:
out.close()
def write_offline_glsl(self, outdir):
output_path = os.path.join(outdir, os.path.splitext(self.basename)[0] + ".minified.glsl")
print("Minifying %s <- %s" % (output_path, self.basename))
root, ext = os.path.splitext(self.basename)
output_path = os.path.join(outdir, f"{root}.minified{ext}")
print(f"Minifying f{output_path} <- {self.basename}")
out = open(output_path, "w", newline='\n')
self.emit_tokens_to_rewritten_glsl(out, preserve_exported_switches=True)
out.close()

View File

@@ -10,4 +10,5 @@
#include "common.minified.glsl"
#include "draw_path_common.minified.glsl"
#include "advanced_blend.minified.glsl"
#include "draw_path.minified.glsl"
#include "draw_path.minified.vert"
#include "draw_raster_order_path.minified.frag"

View File

@@ -9,4 +9,5 @@
#include "specialization.minified.glsl"
#include "common.minified.glsl"
#include "advanced_blend.minified.glsl"
#include "draw_image_mesh.minified.glsl"
#include "draw_image_mesh.minified.vert"
#include "draw_raster_order_image_mesh.minified.frag"

View File

@@ -9,4 +9,5 @@
#include "common.minified.glsl"
#include "draw_path_common.minified.glsl"
#include "advanced_blend.minified.glsl"
#include "draw_path.minified.glsl"
#include "draw_path.minified.vert"
#include "draw_raster_order_path.minified.frag"

View File

@@ -11,4 +11,5 @@
#include "common.minified.glsl"
#include "draw_path_common.minified.glsl"
#include "advanced_blend.minified.glsl"
#include "draw_path.minified.glsl"
#include "draw_path.minified.vert"
#include "draw_msaa_path.minified.frag"

View File

@@ -10,4 +10,5 @@
#include "specialization.minified.glsl"
#include "common.minified.glsl"
#include "advanced_blend.minified.glsl"
#include "draw_image_mesh.minified.glsl"
#include "draw_image_mesh.minified.vert"
#include "draw_msaa_image_mesh.minified.frag"

View File

@@ -11,4 +11,5 @@
#include "common.minified.glsl"
#include "draw_path_common.minified.glsl"
#include "advanced_blend.minified.glsl"
#include "draw_path.minified.glsl"
#include "draw_path.minified.vert"
#include "draw_msaa_path.minified.frag"

View File

@@ -10,4 +10,5 @@
#include "common.minified.glsl"
#include "draw_path_common.minified.glsl"
#include "advanced_blend.minified.glsl"
#include "draw_path.minified.glsl"
#include "draw_path.minified.vert"
#include "draw_raster_order_path.minified.frag"

View File

@@ -14,4 +14,5 @@
#include "Generated/constants.minified.ush"
#include "Generated/common.minified.ush"
#include "Generated/advanced_blend.minified.ush"
#include "Generated/draw_image_mesh.minified.ush"
#include "Generated/draw_image_mesh.minified.vert.ush"
#include "Generated/draw_raster_order_image_mesh.minified.frag.ush"

View File

@@ -13,4 +13,5 @@
#include "Generated/common.minified.ush"
#include "Generated/draw_path_common.minified.ush"
#include "Generated/advanced_blend.minified.ush"
#include "Generated/draw_path.minified.ush"
#include "Generated/draw_path.minified.vert.ush"
#include "Generated/draw_raster_order_path.minified.frag.ush"

View File

@@ -15,4 +15,5 @@
#include "Generated/common.minified.ush"
#include "Generated/draw_path_common.minified.ush"
#include "Generated/advanced_blend.minified.ush"
#include "Generated/draw_path.minified.ush"
#include "Generated/draw_path.minified.vert.ush"
#include "Generated/draw_raster_order_path.minified.frag.ush"

View File

@@ -69,9 +69,11 @@ constexpr static auto RIVE_FRONT_FACE = wgpu::FrontFace::CW;
#include "generated/shaders/tessellate.glsl.hpp"
#include "generated/shaders/render_atlas.glsl.hpp"
#include "generated/shaders/advanced_blend.glsl.hpp"
#include "generated/shaders/draw_path.glsl.hpp"
#include "generated/shaders/draw_path_common.glsl.hpp"
#include "generated/shaders/draw_image_mesh.glsl.hpp"
#include "generated/shaders/draw_path.vert.hpp"
#include "generated/shaders/draw_raster_order_path.frag.hpp"
#include "generated/shaders/draw_image_mesh.vert.hpp"
#include "generated/shaders/draw_raster_order_image_mesh.frag.hpp"
// When compiling "glslRaw" shaders, the WebGPU driver will automatically
// search for a uniform with this name and update its value when draw commands
@@ -845,15 +847,19 @@ public:
case DrawType::outerCurvePatches:
addDefine(GLSL_DRAW_PATH);
glsl << gpu::glsl::draw_path_common << '\n';
glsl << gpu::glsl::draw_path << '\n';
glsl << gpu::glsl::draw_path_vert << '\n';
glsl << gpu::glsl::draw_raster_order_path_frag << '\n';
break;
case DrawType::interiorTriangulation:
case DrawType::atlasBlit:
glsl << gpu::glsl::draw_path_common << '\n';
glsl << gpu::glsl::draw_path << '\n';
glsl << gpu::glsl::draw_path_vert << '\n';
glsl << gpu::glsl::draw_raster_order_path_frag << '\n';
break;
case DrawType::imageMesh:
glsl << gpu::glsl::draw_image_mesh << '\n';
glsl << gpu::glsl::draw_image_mesh_vert << '\n';
glsl << gpu::glsl::draw_raster_order_image_mesh_frag
<< '\n';
break;
case DrawType::imageRect:
case DrawType::msaaStrokes: