| /* | |
| * Copyright 2023 Rive | |
| */ | |
| #ifdef @DRAW_PATH | |
| #ifdef @VERTEX | |
| ATTR_BLOCK_BEGIN(Attrs) | |
| ATTR(0, | |
| float4, | |
| @a_patchVertexData); // [localVertexID, outset, fillCoverage, vertexType] | |
| ATTR(1, float4, @a_mirroredVertexData); | |
| ATTR_BLOCK_END | |
| #endif | |
| VARYING_BLOCK_BEGIN | |
| #ifdef @ENABLE_FEATHER | |
| NO_PERSPECTIVE VARYING(0, float4, v_coverages); | |
| #else | |
| NO_PERSPECTIVE VARYING(0, half2, v_coverages); | |
| #endif | |
| FLAT VARYING(1, ushort, v_pathID); | |
| VARYING_BLOCK_END | |
| #ifdef @VERTEX | |
| VERTEX_MAIN(@drawVertexMain, Attrs, attrs, _vertexID, _instanceID) | |
| { | |
| ATTR_UNPACK(_vertexID, attrs, @a_patchVertexData, float4); | |
| ATTR_UNPACK(_vertexID, attrs, @a_mirroredVertexData, float4); | |
| #ifdef @ENABLE_FEATHER | |
| VARYING_INIT(v_coverages, float4); | |
| #else | |
| VARYING_INIT(v_coverages, half2); | |
| #endif | |
| VARYING_INIT(v_pathID, ushort); | |
| float4 pos; | |
| uint pathID; | |
| float2 vertexPosition; | |
| float4 coverages; | |
| if (unpack_tessellated_path_vertex(@a_patchVertexData, | |
| @a_mirroredVertexData, | |
| _instanceID, | |
| pathID, | |
| vertexPosition, | |
| coverages VERTEX_CONTEXT_UNPACK)) | |
| { | |
| #ifdef @ENABLE_FEATHER | |
| v_coverages = coverages; | |
| #else | |
| v_coverages.xy = cast_float2_to_half2(coverages.xy); | |
| #endif | |
| v_pathID = cast_uint_to_ushort(pathID); | |
| pos = RENDER_TARGET_COORD_TO_CLIP_COORD(vertexPosition); | |
| } | |
| else | |
| { | |
| pos = float4(uniforms.vertexDiscardValue, | |
| uniforms.vertexDiscardValue, | |
| uniforms.vertexDiscardValue, | |
| uniforms.vertexDiscardValue); | |
| } | |
| VARYING_PACK(v_coverages); | |
| VARYING_PACK(v_pathID); | |
| EMIT_VERTEX(pos); | |
| } | |
| #endif // VERTEX | |
| #endif // DRAW_PATH | |
| #if defined(@DRAW_INTERIOR_TRIANGLES) || defined(@FEATHER_ATLAS_BLIT) | |
| #ifdef @VERTEX | |
| ATTR_BLOCK_BEGIN(Attrs) | |
| ATTR(0, packed_float3, @a_triangleVertex); | |
| ATTR_BLOCK_END | |
| #endif | |
| VARYING_BLOCK_BEGIN | |
| #ifdef @FEATHER_ATLAS_BLIT | |
| NO_PERSPECTIVE VARYING(0, float2, v_atlasCoord); | |
| #else | |
| @OPTIONALLY_FLAT VARYING(0, half, v_windingWeight); | |
| #endif | |
| FLAT VARYING(1, ushort, v_pathID); | |
| VARYING_BLOCK_END | |
| #ifdef @VERTEX | |
| VERTEX_MAIN(@drawVertexMain, Attrs, attrs, _vertexID, _instanceID) | |
| { | |
| ATTR_UNPACK(_vertexID, attrs, @a_triangleVertex, float3); | |
| #ifdef @FEATHER_ATLAS_BLIT | |
| VARYING_INIT(v_atlasCoord, float2); | |
| #else | |
| VARYING_INIT(v_windingWeight, half); | |
| #endif | |
| VARYING_INIT(v_pathID, ushort); | |
| uint pathID; | |
| float2 vertexPosition; | |
| #ifdef @FEATHER_ATLAS_BLIT | |
| vertexPosition = | |
| unpack_atlas_coverage_vertex(@a_triangleVertex, | |
| pathID, | |
| v_atlasCoord VERTEX_CONTEXT_UNPACK); | |
| #else | |
| vertexPosition = | |
| unpack_interior_triangle_vertex(@a_triangleVertex, | |
| pathID, | |
| v_windingWeight VERTEX_CONTEXT_UNPACK); | |
| #endif | |
| v_pathID = cast_uint_to_ushort(pathID); | |
| float4 pos = RENDER_TARGET_COORD_TO_CLIP_COORD(vertexPosition); | |
| #ifdef @FEATHER_ATLAS_BLIT | |
| VARYING_PACK(v_atlasCoord); | |
| #else | |
| VARYING_PACK(v_windingWeight); | |
| #endif | |
| VARYING_PACK(v_pathID); | |
| EMIT_VERTEX(pos); | |
| } | |
| #endif // @VERTEX | |
| #endif // @DRAW_INTERIOR_TRIANGLES || @FEATHER_ATLAS_BLIT | |
| #ifdef @DRAW_IMAGE_RECT | |
| #ifdef @VERTEX | |
| ATTR_BLOCK_BEGIN(Attrs) | |
| ATTR(0, float4, @a_imageRectVertex); | |
| ATTR_BLOCK_END | |
| ATTR_BLOCK_BEGIN(ImageDrawAttrs) | |
| ATTR(IMAGE_VIEW_MATRIX_ATTRIB_IDX, float4, @a_imageDrawViewMatrix); | |
| ATTR(IMAGE_CLIP_RECT_INVERSE_MATRIX_ATTRIB_IDX, | |
| float4, | |
| @a_imageDrawClipRectInverseMatrix); | |
| ATTR(IMAGE_TRANSLATES_ATTRIB_IDX, float4, @a_imageDrawTranslates); | |
| ATTR(IMAGE_OPACITY_ATTRIB_IDX, float, @a_imageDrawOpacity); | |
| ATTR(IMAGE_CLIP_ID_ATTRIB_IDX, uint, @a_imageDrawClipID); | |
| ATTR(IMAGE_BLEND_MODE_ATTRIB_IDX, uint, @a_imageDrawBlendMode); | |
| ATTR(IMAGE_ZINDEX_ATTRIB_IDX, uint, @a_imageDrawZIndex); | |
| ATTR_BLOCK_END | |
| #endif | |
| VARYING_BLOCK_BEGIN | |
| NO_PERSPECTIVE VARYING(0, float2, v_texCoord); | |
| NO_PERSPECTIVE VARYING(1, half, v_edgeCoverage); | |
| #ifdef @ENABLE_CLIP_RECT | |
| NO_PERSPECTIVE VARYING(2, float4, v_clipRect); | |
| #endif | |
| @OPTIONALLY_FLAT VARYING(3, half, v_imageOpacity); | |
| #ifdef @ENABLE_CLIPPING | |
| FLAT VARYING(4, ushort, v_imageClipID); | |
| #endif | |
| #ifdef @ENABLE_ADVANCED_BLEND | |
| FLAT VARYING(5, ushort, v_imageBlendMode); | |
| #endif | |
| VARYING_BLOCK_END | |
| #ifdef @VERTEX | |
| IMAGE_RECT_VERTEX_MAIN(@drawVertexMain, | |
| Attrs, | |
| attrs, | |
| ImageDrawAttrs, | |
| imageDrawAttrs, | |
| _vertexID, | |
| _instanceID) | |
| { | |
| ATTR_UNPACK(_vertexID, attrs, @a_imageRectVertex, float4); | |
| ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawViewMatrix, float4); | |
| ATTR_UNPACK(_instanceID, | |
| imageDrawAttrs, | |
| @a_imageDrawClipRectInverseMatrix, | |
| float4); | |
| ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawTranslates, float4); | |
| ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawOpacity, float); | |
| ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawClipID, uint); | |
| ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawBlendMode, uint); | |
| ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawZIndex, uint); | |
| VARYING_INIT(v_texCoord, float2); | |
| VARYING_INIT(v_edgeCoverage, half); | |
| #ifdef @ENABLE_CLIP_RECT | |
| VARYING_INIT(v_clipRect, float4); | |
| #endif | |
| VARYING_INIT(v_imageOpacity, half); | |
| #ifdef @ENABLE_CLIPPING | |
| VARYING_INIT(v_imageClipID, ushort); | |
| #endif | |
| #ifdef @ENABLE_ADVANCED_BLEND | |
| VARYING_INIT(v_imageBlendMode, ushort); | |
| #endif | |
| bool isOuterVertex = | |
| @a_imageRectVertex.z == .0 || @a_imageRectVertex.w == .0; | |
| v_edgeCoverage = isOuterVertex ? .0 : 1.; | |
| float2 vertexPosition = @a_imageRectVertex.xy; | |
| float2x2 M = make_float2x2(@a_imageDrawViewMatrix); | |
| float2x2 MIT = transpose(inverse(M)); | |
| if (!isOuterVertex) | |
| { | |
| // Inset the inner vertices to the point where coverage == 1. | |
| // NOTE: if width/height ever change from 1, these equations need to be | |
| // updated. | |
| float aaRadiusX = | |
| AA_RADIUS * manhattan_width(MIT[1]) / dot(M[1], MIT[1]); | |
| if (aaRadiusX >= .5) | |
| { | |
| vertexPosition.x = .5; | |
| v_edgeCoverage *= cast_float_to_half(.5 / aaRadiusX); | |
| } | |
| else | |
| { | |
| vertexPosition.x += aaRadiusX * @a_imageRectVertex.z; | |
| } | |
| float aaRadiusY = | |
| AA_RADIUS * manhattan_width(MIT[0]) / dot(M[0], MIT[0]); | |
| if (aaRadiusY >= .5) | |
| { | |
| vertexPosition.y = .5; | |
| v_edgeCoverage *= cast_float_to_half(.5 / aaRadiusY); | |
| } | |
| else | |
| { | |
| vertexPosition.y += aaRadiusY * @a_imageRectVertex.w; | |
| } | |
| } | |
| v_texCoord = vertexPosition; | |
| vertexPosition = MUL(M, vertexPosition) + @a_imageDrawTranslates.xy; | |
| if (isOuterVertex) | |
| { | |
| // Outset the outer vertices to the point where coverage == 0. | |
| float2 n = MUL(MIT, @a_imageRectVertex.zw); | |
| n *= manhattan_width(n) / dot(n, n); | |
| vertexPosition += AA_RADIUS * n; | |
| } | |
| #ifdef @ENABLE_CLIP_RECT | |
| if (@ENABLE_CLIP_RECT) | |
| { | |
| v_clipRect = find_clip_rect_coverage_distances( | |
| make_float2x2(@a_imageDrawClipRectInverseMatrix), | |
| @a_imageDrawTranslates.zw, | |
| vertexPosition); | |
| } | |
| #endif | |
| v_imageOpacity = @a_imageDrawOpacity; | |
| #ifdef @ENABLE_CLIPPING | |
| v_imageClipID = cast_uint_to_ushort(@a_imageDrawClipID); | |
| #endif | |
| #ifdef @ENABLE_ADVANCED_BLEND | |
| v_imageBlendMode = cast_uint_to_ushort(@a_imageDrawBlendMode); | |
| #endif | |
| float4 pos = RENDER_TARGET_COORD_TO_CLIP_COORD(vertexPosition); | |
| VARYING_PACK(v_texCoord); | |
| VARYING_PACK(v_edgeCoverage); | |
| #ifdef @ENABLE_CLIP_RECT | |
| VARYING_PACK(v_clipRect); | |
| #endif | |
| VARYING_PACK(v_imageOpacity); | |
| #ifdef @ENABLE_CLIPPING | |
| VARYING_PACK(v_imageClipID); | |
| #endif | |
| #ifdef @ENABLE_ADVANCED_BLEND | |
| VARYING_PACK(v_imageBlendMode); | |
| #endif | |
| EMIT_VERTEX(pos); | |
| } | |
| #endif // VERTEX | |
| #elif defined(@DRAW_IMAGE_MESH) | |
| #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 | |
| ATTR_BLOCK_BEGIN(ImageDrawAttrs) | |
| ATTR(IMAGE_VIEW_MATRIX_ATTRIB_IDX, float4, @a_imageDrawViewMatrix); | |
| ATTR(IMAGE_CLIP_RECT_INVERSE_MATRIX_ATTRIB_IDX, | |
| float4, | |
| @a_imageDrawClipRectInverseMatrix); | |
| ATTR(IMAGE_TRANSLATES_ATTRIB_IDX, float4, @a_imageDrawTranslates); | |
| ATTR(IMAGE_OPACITY_ATTRIB_IDX, float, @a_imageDrawOpacity); | |
| ATTR(IMAGE_CLIP_ID_ATTRIB_IDX, uint, @a_imageDrawClipID); | |
| ATTR(IMAGE_BLEND_MODE_ATTRIB_IDX, uint, @a_imageDrawBlendMode); | |
| ATTR(IMAGE_ZINDEX_ATTRIB_IDX, uint, @a_imageDrawZIndex); | |
| ATTR_BLOCK_END | |
| #endif | |
| VARYING_BLOCK_BEGIN | |
| NO_PERSPECTIVE VARYING(0, float2, v_texCoord); | |
| #ifdef @ENABLE_CLIP_RECT | |
| NO_PERSPECTIVE VARYING(1, float4, v_clipRect); | |
| #endif | |
| @OPTIONALLY_FLAT VARYING(3, half, v_imageOpacity); | |
| #ifdef @ENABLE_CLIPPING | |
| FLAT VARYING(4, ushort, v_imageClipID); | |
| #endif | |
| #ifdef @ENABLE_ADVANCED_BLEND | |
| FLAT VARYING(5, ushort, v_imageBlendMode); | |
| #endif | |
| VARYING_BLOCK_END | |
| #ifdef @VERTEX | |
| IMAGE_MESH_VERTEX_MAIN(@drawVertexMain, | |
| PositionAttr, | |
| position, | |
| UVAttr, | |
| uv, | |
| ImageDrawAttrs, | |
| imageDrawAttrs, | |
| _vertexID) | |
| { | |
| ATTR_UNPACK(_vertexID, position, @a_position, float2); | |
| ATTR_UNPACK(_vertexID, uv, @a_texCoord, float2); | |
| ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawViewMatrix, float4); | |
| ATTR_UNPACK(_instanceID, | |
| imageDrawAttrs, | |
| @a_imageDrawClipRectInverseMatrix, | |
| float4); | |
| ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawTranslates, float4); | |
| ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawOpacity, float); | |
| ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawClipID, uint); | |
| ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawBlendMode, uint); | |
| ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawZIndex, uint); | |
| VARYING_INIT(v_texCoord, float2); | |
| #ifdef @ENABLE_CLIP_RECT | |
| VARYING_INIT(v_clipRect, float4); | |
| #endif | |
| VARYING_INIT(v_imageOpacity, half); | |
| #ifdef @ENABLE_CLIPPING | |
| VARYING_INIT(v_imageClipID, ushort); | |
| #endif | |
| #ifdef @ENABLE_ADVANCED_BLEND | |
| VARYING_INIT(v_imageBlendMode, ushort); | |
| #endif | |
| float2x2 M = make_float2x2(@a_imageDrawViewMatrix); | |
| float2 vertexPosition = MUL(M, @a_position) + @a_imageDrawTranslates.xy; | |
| v_texCoord = @a_texCoord; | |
| #ifdef @ENABLE_CLIP_RECT | |
| if (@ENABLE_CLIP_RECT) | |
| { | |
| v_clipRect = find_clip_rect_coverage_distances( | |
| make_float2x2(@a_imageDrawClipRectInverseMatrix), | |
| @a_imageDrawTranslates.zw, | |
| vertexPosition); | |
| } | |
| #endif | |
| v_imageOpacity = @a_imageDrawOpacity; | |
| #ifdef @ENABLE_CLIPPING | |
| v_imageClipID = cast_uint_to_ushort(@a_imageDrawClipID); | |
| #endif | |
| #ifdef @ENABLE_ADVANCED_BLEND | |
| v_imageBlendMode = cast_uint_to_ushort(@a_imageDrawBlendMode); | |
| #endif | |
| float4 pos = RENDER_TARGET_COORD_TO_CLIP_COORD(vertexPosition); | |
| VARYING_PACK(v_texCoord); | |
| #ifdef @ENABLE_CLIP_RECT | |
| VARYING_PACK(v_clipRect); | |
| #endif | |
| VARYING_PACK(v_imageOpacity); | |
| #ifdef @ENABLE_CLIPPING | |
| VARYING_PACK(v_imageClipID); | |
| #endif | |
| #ifdef @ENABLE_ADVANCED_BLEND | |
| VARYING_PACK(v_imageBlendMode); | |
| #endif | |
| EMIT_VERTEX(pos); | |
| } | |
| #endif // VERTEX | |
| #endif // DRAW_IMAGE_MESH | |
| #ifdef @DRAW_RENDER_TARGET_UPDATE_BOUNDS | |
| #ifdef @VERTEX | |
| ATTR_BLOCK_BEGIN(Attrs) | |
| ATTR_BLOCK_END | |
| #endif // VERTEX | |
| VARYING_BLOCK_BEGIN | |
| VARYING_BLOCK_END | |
| #ifdef @VERTEX | |
| VERTEX_MAIN(@drawVertexMain, Attrs, attrs, _vertexID, _instanceID) | |
| { | |
| int2 coord; | |
| coord.x = (_vertexID & 1) == 0 ? uniforms.renderTargetUpdateBounds.x | |
| : uniforms.renderTargetUpdateBounds.z; | |
| coord.y = (_vertexID & 2) == 0 ? uniforms.renderTargetUpdateBounds.y | |
| : uniforms.renderTargetUpdateBounds.w; | |
| float4 pos = RENDER_TARGET_COORD_TO_CLIP_COORD(float2(coord)); | |
| EMIT_VERTEX(pos); | |
| } | |
| #endif // VERTEX | |
| #endif // DRAW_RENDER_TARGET_UPDATE_BOUNDS | |
| #ifdef @DRAW_IMAGE | |
| #define NEEDS_IMAGE_TEXTURE | |
| #endif | |
| // INITIALIZE_PLS may sample @imageTexture (the previous framebuffer contents | |
| // copied to dstColorTexture) when LoadAction::preserveRenderTarget is | |
| // requested. The spec-const LOAD_COLOR_FROM_DST_TEXTURE is set at runtime. We | |
| // have to declare the binding unconditionally though for non-FFCO | |
| // INITIALIZE_PLS so the spec-const branch can reach it; the caller binds a null | |
| // texture when the branch is off. | |
| #if defined(@INITIALIZE_PLS) && !defined(@FIXED_FUNCTION_COLOR_OUTPUT) | |
| #define NEEDS_IMAGE_TEXTURE | |
| #endif | |
| #ifdef @FRAGMENT | |
| PLS_BLOCK_BEGIN | |
| // We only bind the framebuffer as PLS when there are blend modes. Otherwise, we | |
| // render to it as a normal color attachment. | |
| #ifndef @FIXED_FUNCTION_COLOR_OUTPUT | |
| #ifdef @COLOR_PLANE_IDX_OVERRIDE | |
| // D3D11 doesn't let us bind the framebuffer UAV to slot 0 when there is a | |
| // color output. | |
| #define LOCAL_COLOR_PLANE_IDX @COLOR_PLANE_IDX_OVERRIDE | |
| #else | |
| #define LOCAL_COLOR_PLANE_IDX COLOR_PLANE_IDX | |
| #endif | |
| #ifdef @COALESCED_PLS_RESOLVE_AND_TRANSFER | |
| PLS_DECL4F_READONLY(LOCAL_COLOR_PLANE_IDX, colorBuffer); | |
| #else | |
| PLS_DECL4F(LOCAL_COLOR_PLANE_IDX, colorBuffer); | |
| #endif | |
| #endif // !FIXED_FUNCTION_COLOR_OUTPUT | |
| #ifdef @PLS_BLEND_SRC_OVER | |
| // When PLS has src-over blending enabled, the clip buffer is RGBA8 so we | |
| // can preserve clip contents by emitting a=0 instead of loading the current | |
| // value. This is also is a hint to the hardware that it doesn't need to | |
| // write anything to the clip attachment. | |
| #define CLIP_VALUE_TYPE half4 | |
| #define PLS_LOAD_CLIP_TYPE PLS_LOAD4F | |
| #define MAKE_NON_UPDATING_CLIP_VALUE make_half4(.0) | |
| #define HAS_UPDATED_CLIP_VALUE(X) ((X).a != .0) | |
| #ifdef @ENABLE_CLIPPING | |
| #ifndef @RESOLVE_PLS | |
| PLS_DECL4F(CLIP_PLANE_IDX, clipBuffer); | |
| #else | |
| PLS_DECL4F_READONLY(CLIP_PLANE_IDX, clipBuffer); | |
| #endif | |
| #endif // ENABLE_CLIPPING | |
| #else | |
| // When PLS does not have src-over blending, the clip buffer the usual | |
| // packed R32UI. | |
| #define CLIP_VALUE_TYPE uint | |
| #define MAKE_NON_UPDATING_CLIP_VALUE 0u | |
| #define PLS_LOAD_CLIP_TYPE PLS_LOADUI | |
| #define HAS_UPDATED_CLIP_VALUE(X) ((X) != 0u) | |
| #ifdef @ENABLE_CLIPPING | |
| PLS_DECLUI(CLIP_PLANE_IDX, clipBuffer); | |
| #endif // ENABLE_CLIPPING | |
| #endif // !PLS_BLEND_SRC_OVER | |
| PLS_DECLUI_UAV(COVERAGE_PLANE_IDX, coverageAtomicBuffer); | |
| PLS_BLOCK_END | |
| FRAG_STORAGE_BUFFER_BLOCK_BEGIN | |
| STORAGE_BUFFER_U32x2(PAINT_BUFFER_IDX, PaintBuffer, @paintBuffer); | |
| STORAGE_BUFFER_F32x4(PAINT_AUX_BUFFER_IDX, PaintAuxBuffer, @paintAuxBuffer); | |
| FRAG_STORAGE_BUFFER_BLOCK_END | |
| INLINE uint to_fixed(float x) | |
| { | |
| return uint(round(x * FIXED_COVERAGE_PRECISION + FIXED_COVERAGE_ZERO)); | |
| } | |
| INLINE half from_fixed(uint x) | |
| { | |
| return cast_float_to_half( | |
| float(x) * FIXED_COVERAGE_INVERSE_PRECISION + | |
| (-FIXED_COVERAGE_ZERO * FIXED_COVERAGE_INVERSE_PRECISION)); | |
| } | |
| ushort apply_driver_workaround_for_path_id(ushort pathID) | |
| { | |
| #ifdef @NEEDS_PATH_ID_CLAMP_WORKAROUND | |
| // We have observed that on some hardware, inactive threads or helper lanes | |
| // appear to issue calls that access storage textures, even though they | |
| // should be NO-OP. clamping the path ID prevents crashes in these | |
| // scenarios. | |
| pathID = min(pathID, uniforms.maxPathId); | |
| #endif | |
| return pathID; | |
| } | |
| #ifdef @ENABLE_CLIPPING | |
| INLINE void apply_clip(uint clipID, | |
| CLIP_VALUE_TYPE clipData, | |
| INOUT(half) coverage) | |
| { | |
| #ifdef @PLS_BLEND_SRC_OVER | |
| // The clipID is packed into r & g of clipData. Use a fuzzy equality test | |
| // since we lose precision when the clip value gets stored to and from the | |
| // attachment. | |
| if (all(lessThan(abs(clipData.rg - unpackUnorm4x8(clipID).rg), | |
| make_half2(.25 / 255.)))) | |
| coverage = min(coverage, clipData.b); | |
| else | |
| coverage = .0; | |
| #else | |
| // The clipID is the top 16 bits of clipData. | |
| if (clipID == clipData >> 16) | |
| coverage = min(coverage, unpackHalf2x16(clipData).r); | |
| else | |
| coverage = .0; | |
| #endif | |
| } | |
| #endif | |
| INLINE void resolve_paint(uint pathID, | |
| half coverageCount, | |
| OUT(half4) fragColorOut | |
| #if defined(@ENABLE_CLIPPING) && !defined(@RESOLVE_PLS) | |
| , | |
| INOUT(CLIP_VALUE_TYPE) fragClipOut | |
| #endif | |
| FRAGMENT_CONTEXT_DECL PLS_CONTEXT_DECL) | |
| { | |
| uint2 paintData = STORAGE_BUFFER_LOAD2(@paintBuffer, pathID); | |
| half coverage = coverageCount; | |
| if ((paintData.x & (PAINT_FLAG_NON_ZERO_FILL | PAINT_FLAG_EVEN_ODD_FILL)) != | |
| 0u) | |
| { | |
| // This path has a legacy (non-clockwise) fill. | |
| coverage = abs(coverage); | |
| #ifdef @ENABLE_EVEN_ODD | |
| if (@ENABLE_EVEN_ODD && (paintData.x & PAINT_FLAG_EVEN_ODD_FILL) != 0u) | |
| { | |
| coverage = 1. - abs(fract(coverage * .5) * 2. + -1.); | |
| } | |
| #endif | |
| } | |
| // This also caps stroke coverage, which can be >1. | |
| coverage = clamp(coverage, make_half(.0), make_half(1.)); | |
| #ifdef @ENABLE_CLIPPING | |
| if (@ENABLE_CLIPPING) | |
| { | |
| uint clipID = paintData.x >> 16u; | |
| if (clipID != 0u) | |
| { | |
| apply_clip(clipID, PLS_LOAD_CLIP_TYPE(clipBuffer), coverage); | |
| } | |
| } | |
| #endif // ENABLE_CLIPPING | |
| #ifdef @ENABLE_CLIP_RECT | |
| if (@ENABLE_CLIP_RECT && (paintData.x & PAINT_FLAG_HAS_CLIP_RECT) != 0u) | |
| { | |
| float2x2 M = make_float2x2( | |
| STORAGE_BUFFER_LOAD4(@paintAuxBuffer, | |
| pathID * PAINT_AUX_ENTRY_ELEMENT_COUNT + 2u)); | |
| float4 translate = | |
| STORAGE_BUFFER_LOAD4(@paintAuxBuffer, | |
| pathID * PAINT_AUX_ENTRY_ELEMENT_COUNT + 3u); | |
| float2 clipCoord = MUL(M, _fragCoord) + translate.xy; | |
| // translate.zw contains -1 / fwidth(clipCoord), which we use to | |
| // calculate antialiasing. | |
| half2 distXY = | |
| cast_float2_to_half2(abs(clipCoord) * translate.zw - translate.zw); | |
| half clipRectCoverage = clamp(min(distXY.x, distXY.y) + .5, .0, 1.); | |
| coverage = min(coverage, clipRectCoverage); | |
| } | |
| #endif // ENABLE_CLIP_RECT | |
| uint paintType = paintData.x & 0xfu; | |
| if (paintType <= SOLID_COLOR_PAINT_TYPE) // CLIP_UPDATE_PAINT_TYPE or | |
| // SOLID_COLOR_PAINT_TYPE | |
| { | |
| fragColorOut = unpackUnorm4x8(paintData.y); | |
| #ifdef @ENABLE_CLIPPING | |
| if (@ENABLE_CLIPPING && paintType == CLIP_UPDATE_PAINT_TYPE) | |
| { | |
| #ifndef @RESOLVE_PLS | |
| #ifdef @PLS_BLEND_SRC_OVER | |
| // This was actually a clip update. fragColorOut contains the packed | |
| // clipID. | |
| fragClipOut.rg = fragColorOut.ba; // Pack the clipID into r & g. | |
| fragClipOut.b = coverage; // Put the clipCoverage in b. | |
| fragClipOut.a = | |
| 1.; // a=1 so we replace the value in the clipBuffer. | |
| #else | |
| fragClipOut = paintData.y | packHalf2x16(make_half2(coverage, .0)); | |
| #endif | |
| #endif | |
| // Don't update the colorBuffer when this is a clip update. | |
| fragColorOut = make_half4(.0); | |
| } | |
| #endif | |
| } | |
| else // LINEAR_GRADIENT_PAINT_TYPE or RADIAL_GRADIENT_PAINT_TYPE | |
| { | |
| float2x2 M = make_float2x2( | |
| STORAGE_BUFFER_LOAD4(@paintAuxBuffer, | |
| pathID * PAINT_AUX_ENTRY_ELEMENT_COUNT)); | |
| float4 translate = | |
| STORAGE_BUFFER_LOAD4(@paintAuxBuffer, | |
| pathID * PAINT_AUX_ENTRY_ELEMENT_COUNT + 1u); | |
| float2 paintCoord = MUL(M, _fragCoord) + translate.xy; | |
| float t = paintType == LINEAR_GRADIENT_PAINT_TYPE | |
| ? /*linear*/ paintCoord.x | |
| : /*radial*/ length(paintCoord); | |
| t = clamp(t, .0, 1.); | |
| float x = t * translate.z + translate.w; | |
| float y = uintBitsToFloat(paintData.y); | |
| fragColorOut = | |
| TEXTURE_SAMPLE_LOD(@gradTexture, gradSampler, float2(x, y), .0); | |
| } | |
| fragColorOut.a *= coverage; | |
| #if !defined(@FIXED_FUNCTION_COLOR_OUTPUT) && defined(@ENABLE_ADVANCED_BLEND) | |
| // Apply the advanced blend mode, if applicable. | |
| ushort blendMode; | |
| if (@ENABLE_ADVANCED_BLEND && fragColorOut.a != .0 && | |
| (blendMode = cast_uint_to_ushort((paintData.x >> 4) & 0xfu)) != | |
| BLEND_SRC_OVER) | |
| { | |
| half4 dstColorPremul = PLS_LOAD4F(colorBuffer); | |
| fragColorOut.rgb = | |
| advanced_color_blend(fragColorOut.rgb, dstColorPremul, blendMode); | |
| } | |
| #endif // !FIXED_FUNCTION_COLOR_OUTPUT && ENABLE_ADVANCED_BLEND | |
| // 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 | |
| #if defined(@NEEDS_GAMMA_CORRECTION) && \ | |
| (defined(@FIXED_FUNCTION_COLOR_OUTPUT) || defined(@RESOLVE_PLS)) | |
| fragColorOut = gamma_to_linear(fragColorOut); | |
| #endif | |
| fragColorOut.rgb *= fragColorOut.a; | |
| } | |
| #if !defined(@FIXED_FUNCTION_COLOR_OUTPUT) && \ | |
| !defined(@COALESCED_PLS_RESOLVE_AND_TRANSFER) | |
| INLINE void blend_pls_color_src_over(half4 fragColorOut PLS_CONTEXT_DECL) | |
| { | |
| #ifndef @PLS_BLEND_SRC_OVER | |
| if (fragColorOut.a == .0) | |
| return; | |
| float oneMinusSrcAlpha = 1. - fragColorOut.a; | |
| if (oneMinusSrcAlpha != .0) | |
| fragColorOut += PLS_LOAD4F(colorBuffer) * oneMinusSrcAlpha; | |
| #endif | |
| PLS_STORE4F(colorBuffer, fragColorOut); | |
| } | |
| #endif // !@FIXED_FUNCTION_COLOR_OUTPUT && | |
| // !@COALESCED_PLS_RESOLVE_AND_TRANSFER | |
| #if defined(@ENABLE_CLIPPING) && !defined(@RESOLVE_PLS) | |
| INLINE void emit_pls_clip(CLIP_VALUE_TYPE fragClipOut PLS_CONTEXT_DECL) | |
| { | |
| #ifdef @PLS_BLEND_SRC_OVER | |
| PLS_STORE4F(clipBuffer, fragClipOut); | |
| #else | |
| if (fragClipOut != 0u) | |
| PLS_STOREUI(clipBuffer, fragClipOut); | |
| #endif | |
| } | |
| #endif // ENABLE_CLIPPING && !RESOLVE_PLS | |
| #ifdef @FIXED_FUNCTION_COLOR_OUTPUT | |
| #define ATOMIC_PLS_MAIN PLS_FRAG_COLOR_MAIN | |
| #define EMIT_ATOMIC_PLS EMIT_PLS_AND_FRAG_COLOR | |
| #else // !FIXED_FUNCTION_COLOR_OUTPUT | |
| #define ATOMIC_PLS_MAIN PLS_MAIN | |
| #define EMIT_ATOMIC_PLS EMIT_PLS | |
| #endif | |
| #ifdef @DRAW_PATH | |
| ATOMIC_PLS_MAIN(@drawFragmentMain) | |
| { | |
| #ifdef @ENABLE_FEATHER | |
| VARYING_UNPACK(v_coverages, float4); | |
| #else | |
| VARYING_UNPACK(v_coverages, half2); | |
| #endif | |
| VARYING_UNPACK(v_pathID, ushort); | |
| half fragmentCoverage; | |
| #ifdef @ENABLE_FEATHER | |
| if (@ENABLE_FEATHER && is_feathered_stroke(v_coverages)) | |
| { | |
| fragmentCoverage = | |
| eval_feathered_stroke(v_coverages TEXTURE_CONTEXT_FORWARD); | |
| } | |
| else if (@ENABLE_FEATHER && is_feathered_fill(v_coverages)) | |
| { | |
| fragmentCoverage = | |
| eval_feathered_fill(v_coverages TEXTURE_CONTEXT_FORWARD); | |
| } | |
| else | |
| #endif | |
| { | |
| // Cover stroke and fill both in a branchless expression. | |
| fragmentCoverage = | |
| min(min(make_half(v_coverages.x), abs(make_half(v_coverages.y))), | |
| make_half(1.)); | |
| } | |
| half4 fragColorOut = make_half4(.0); | |
| #ifdef @ENABLE_CLIPPING | |
| CLIP_VALUE_TYPE fragClipOut = MAKE_NON_UPDATING_CLIP_VALUE; | |
| #endif | |
| // Since v_pathID increases monotonically with every draw, and since it | |
| // lives in the most significant bits of the coverage data, an atomic max() | |
| // function will serve 3 purposes: | |
| // | |
| // * The invocation that changes the pathID is the single first fragment | |
| // invocation to hit the new path, and the one that should resolve the | |
| // previous path in the framebuffer. | |
| // * Properly resets coverage to zero when we do cross over into | |
| // processing a new path. | |
| // * Accumulates coverage for strokes. | |
| // | |
| uint fixedCoverage = to_fixed(fragmentCoverage); | |
| uint minCoverageData = | |
| (make_uint(v_pathID) << FIXED_COVERAGE_BIT_COUNT) | fixedCoverage; | |
| uint lastCoverageData = | |
| PLS_ATOMIC_MAX(coverageAtomicBuffer, minCoverageData); | |
| ushort lastPathID = | |
| cast_uint_to_ushort(lastCoverageData >> FIXED_COVERAGE_BIT_COUNT); | |
| lastPathID = apply_driver_workaround_for_path_id(lastPathID); | |
| if (lastPathID == v_pathID) | |
| { | |
| // This is not the first fragment of the current path to touch this | |
| // pixel. We already resolved the previous path, so just update coverage | |
| // (if we're a fill) and move on. | |
| if (!is_stroke(v_coverages)) | |
| { | |
| // Only apply the effect of the min() the first time we cross into a | |
| // path. | |
| fixedCoverage += | |
| lastCoverageData - max(minCoverageData, lastCoverageData); | |
| fixedCoverage -= | |
| FIXED_COVERAGE_ZERO_UINT; // Only apply the zero bias once. | |
| PLS_ATOMIC_ADD(coverageAtomicBuffer, | |
| fixedCoverage); // Count coverage. | |
| } | |
| } | |
| else | |
| { | |
| // We crossed into a new path! Resolve the previous path now that we | |
| // know its exact coverage. | |
| half coverageCount = from_fixed(lastCoverageData & FIXED_COVERAGE_MASK); | |
| resolve_paint(lastPathID, | |
| coverageCount, | |
| fragColorOut | |
| #ifdef @ENABLE_CLIPPING | |
| , | |
| fragClipOut | |
| #endif | |
| FRAGMENT_CONTEXT_UNPACK PLS_CONTEXT_UNPACK); | |
| } | |
| fragColorOut.rgb = add_dither_if_alpha_nonzero(fragColorOut.rgb, | |
| fragColorOut.a, | |
| _fragCoord.xy, | |
| uniforms.ditherScale, | |
| uniforms.ditherBias); | |
| #ifdef @FIXED_FUNCTION_COLOR_OUTPUT | |
| _fragColor = fragColorOut; | |
| #else | |
| blend_pls_color_src_over(fragColorOut PLS_CONTEXT_UNPACK); | |
| #endif | |
| #ifdef @ENABLE_CLIPPING | |
| emit_pls_clip(fragClipOut PLS_CONTEXT_UNPACK); | |
| #endif | |
| EMIT_ATOMIC_PLS | |
| } | |
| #endif // DRAW_PATH | |
| #if defined(@DRAW_INTERIOR_TRIANGLES) || defined(@FEATHER_ATLAS_BLIT) | |
| ATOMIC_PLS_MAIN(@drawFragmentMain) | |
| { | |
| #ifdef @FEATHER_ATLAS_BLIT | |
| VARYING_UNPACK(v_atlasCoord, float2); | |
| #else | |
| VARYING_UNPACK(v_windingWeight, half); | |
| #endif | |
| VARYING_UNPACK(v_pathID, ushort); | |
| uint lastCoverageData = PLS_LOADUI_UAV(coverageAtomicBuffer); | |
| ushort lastPathID = | |
| cast_uint_to_ushort(lastCoverageData >> FIXED_COVERAGE_BIT_COUNT); | |
| lastPathID = apply_driver_workaround_for_path_id(lastPathID); | |
| // Update coverageAtomicBuffer with the coverage weight of the current | |
| // triangle. This does not need to be atomic since interior triangles don't | |
| // overlap. | |
| uint currPathCoverageData; | |
| #ifndef @FEATHER_ATLAS_BLIT | |
| if (lastPathID == v_pathID) | |
| { | |
| currPathCoverageData = lastCoverageData; | |
| } | |
| else | |
| #endif | |
| { | |
| currPathCoverageData = | |
| (make_uint(v_pathID) << FIXED_COVERAGE_BIT_COUNT) + | |
| FIXED_COVERAGE_ZERO_UINT; | |
| } | |
| half coverage; | |
| #ifdef @FEATHER_ATLAS_BLIT | |
| coverage = clamp(TEXTURE_SAMPLE_LOD(@featherAtlasTexture, | |
| featherAtlasSampler, | |
| v_atlasCoord, | |
| .0) | |
| .r, | |
| make_half(.0), | |
| make_half(1.)); | |
| #else | |
| coverage = v_windingWeight; | |
| #endif | |
| int coverageDeltaFixed = int(round(coverage * FIXED_COVERAGE_PRECISION)); | |
| PLS_STOREUI_UAV(coverageAtomicBuffer, | |
| currPathCoverageData + uint(coverageDeltaFixed)); | |
| half4 fragColorOut = make_half4(.0); | |
| #ifdef @ENABLE_CLIPPING | |
| CLIP_VALUE_TYPE fragClipOut = MAKE_NON_UPDATING_CLIP_VALUE; | |
| #endif | |
| #ifndef @FEATHER_ATLAS_BLIT | |
| // If this is not the first fragment of the current path to touch this | |
| // pixel, then we've already resolved the previous path and can move on. | |
| if (lastPathID != v_pathID) | |
| #endif | |
| { | |
| // We crossed into a new path! Resolve the previous path now that we | |
| // know its exact coverage. | |
| half lastCoverageCount = | |
| from_fixed(lastCoverageData & FIXED_COVERAGE_MASK); | |
| resolve_paint(lastPathID, | |
| lastCoverageCount, | |
| fragColorOut | |
| #ifdef @ENABLE_CLIPPING | |
| , | |
| fragClipOut | |
| #endif | |
| FRAGMENT_CONTEXT_UNPACK PLS_CONTEXT_UNPACK); | |
| } | |
| fragColorOut.rgb = add_dither_if_alpha_nonzero(fragColorOut.rgb, | |
| fragColorOut.a, | |
| _fragCoord.xy, | |
| uniforms.ditherScale, | |
| uniforms.ditherBias); | |
| #ifdef @FIXED_FUNCTION_COLOR_OUTPUT | |
| _fragColor = fragColorOut; | |
| #else | |
| blend_pls_color_src_over(fragColorOut PLS_CONTEXT_UNPACK); | |
| #endif | |
| #ifdef @ENABLE_CLIPPING | |
| emit_pls_clip(fragClipOut PLS_CONTEXT_UNPACK); | |
| #endif | |
| EMIT_ATOMIC_PLS | |
| } | |
| #endif // @DRAW_INTERIOR_TRIANGLES || @FEATHER_ATLAS_BLIT | |
| #ifdef @DRAW_IMAGE | |
| ATOMIC_PLS_MAIN(@drawFragmentMain) | |
| { | |
| VARYING_UNPACK(v_texCoord, float2); | |
| #ifdef @DRAW_IMAGE_RECT | |
| VARYING_UNPACK(v_edgeCoverage, half); | |
| #endif | |
| #ifdef @ENABLE_CLIP_RECT | |
| VARYING_UNPACK(v_clipRect, float4); | |
| #endif | |
| VARYING_UNPACK(v_imageOpacity, half); | |
| #ifdef @ENABLE_CLIPPING | |
| VARYING_UNPACK(v_imageClipID, ushort); | |
| #endif | |
| #ifdef @ENABLE_ADVANCED_BLEND | |
| VARYING_UNPACK(v_imageBlendMode, ushort); | |
| #endif | |
| // Start by finding the image color. We have to do this immediately instead | |
| // of allowing it to get resolved later like other draws because the | |
| // @imageTexture binding is liable to change, and furthermore in the case of | |
| // imageMeshes, we can't calculate UV coordinates based on fragment | |
| // position. | |
| half4 imageColor = | |
| TEXTURE_SAMPLE_DYNAMIC(@imageTexture, imageSampler, v_texCoord); | |
| half imageCoverage = 1.; | |
| #ifdef @DRAW_IMAGE_RECT | |
| imageCoverage = min(v_edgeCoverage, imageCoverage); | |
| #endif | |
| #ifdef @ENABLE_CLIP_RECT | |
| if (@ENABLE_CLIP_RECT) | |
| { | |
| half clipRectCoverage = min_component(cast_float4_to_half4(v_clipRect)); | |
| imageCoverage = clamp(clipRectCoverage, make_half(.0), imageCoverage); | |
| } | |
| #endif | |
| // Resolve the previous path. | |
| uint lastCoverageData = PLS_LOADUI_UAV(coverageAtomicBuffer); | |
| ushort lastPathID = | |
| cast_uint_to_ushort(lastCoverageData >> FIXED_COVERAGE_BIT_COUNT); | |
| lastPathID = apply_driver_workaround_for_path_id(lastPathID); | |
| half lastCoverageCount = from_fixed(lastCoverageData & FIXED_COVERAGE_MASK); | |
| half4 fragColorOut; | |
| #ifdef @ENABLE_CLIPPING | |
| CLIP_VALUE_TYPE fragClipOut = MAKE_NON_UPDATING_CLIP_VALUE; | |
| #endif | |
| // TODO: consider not resolving the prior paint if we're solid and the prior | |
| // paint is not a clip update: (imageColor.a == 1. && | |
| // v_imageBlendMode == | |
| // BLEND_SRC_OVER && priorPaintType != | |
| // CLIP_UPDATE_PAINT_TYPE) | |
| resolve_paint(lastPathID, | |
| lastCoverageCount, | |
| fragColorOut | |
| #ifdef @ENABLE_CLIPPING | |
| , | |
| fragClipOut | |
| #endif | |
| FRAGMENT_CONTEXT_UNPACK PLS_CONTEXT_UNPACK); | |
| // Clip the image after resolving the previous path, since that can affect | |
| // the clip buffer. | |
| #ifdef @ENABLE_CLIPPING // TODO! ENABLE_IMAGE_CLIPPING in addition to | |
| // ENABLE_CLIPPING? | |
| if (@ENABLE_CLIPPING && v_imageClipID != 0u) | |
| { | |
| CLIP_VALUE_TYPE clipData = HAS_UPDATED_CLIP_VALUE(fragClipOut) | |
| ? fragClipOut | |
| : PLS_LOAD_CLIP_TYPE(clipBuffer); | |
| apply_clip(v_imageClipID, clipData, imageCoverage); | |
| } | |
| #endif // ENABLE_CLIPPING | |
| // Prepare imageColor for premultiplied src-over blending. | |
| #if !defined(@FIXED_FUNCTION_COLOR_OUTPUT) && defined(@ENABLE_ADVANCED_BLEND) | |
| if (@ENABLE_ADVANCED_BLEND && v_imageBlendMode != BLEND_SRC_OVER) | |
| { | |
| // Calculate what dstColorPremul will be after applying fragColorOut. | |
| half4 dstColorPremul = | |
| PLS_LOAD4F(colorBuffer) * (1. - fragColorOut.a) + fragColorOut; | |
| // Calculate the imageColor to emit *BEFORE* src-over blending, such | |
| // that the post-src-over-blend result is equivalent to the blendMode. | |
| imageColor.rgb = advanced_color_blend(unmultiply_rgb(imageColor), | |
| dstColorPremul, | |
| v_imageBlendMode) * | |
| imageColor.a; | |
| } | |
| #endif // !FIXED_FUNCTION_COLOR_OUTPUT && ENABLE_ADVANCED_BLEND | |
| imageColor *= imageCoverage * v_imageOpacity; | |
| #if defined(@NEEDS_GAMMA_CORRECTION) | |
| imageColor = gamma_to_linear(imageColor); | |
| #endif | |
| // Leverage the property that premultiplied src-over blending is associative | |
| // and blend the imageColor and fragColorOut before passing them on to the | |
| // blending pipeline. | |
| fragColorOut = fragColorOut * (1. - imageColor.a) + imageColor; | |
| fragColorOut.rgb = add_dither_if_alpha_nonzero(fragColorOut.rgb, | |
| fragColorOut.a, | |
| _fragCoord.xy, | |
| uniforms.ditherScale, | |
| uniforms.ditherBias); | |
| #ifdef @FIXED_FUNCTION_COLOR_OUTPUT | |
| _fragColor = fragColorOut; | |
| #else | |
| blend_pls_color_src_over(fragColorOut PLS_CONTEXT_UNPACK); | |
| #endif | |
| #ifdef @ENABLE_CLIPPING | |
| emit_pls_clip(fragClipOut PLS_CONTEXT_UNPACK); | |
| #endif | |
| // Write out a coverage value of "zero at pathID=0" so a future resolve | |
| // attempt doesn't affect this pixel. | |
| PLS_STOREUI_UAV(coverageAtomicBuffer, FIXED_COVERAGE_ZERO_UINT); | |
| EMIT_ATOMIC_PLS | |
| } | |
| #endif // DRAW_IMAGE | |
| #ifdef @INITIALIZE_PLS | |
| ATOMIC_PLS_MAIN(@drawFragmentMain) | |
| { | |
| #ifndef @FIXED_FUNCTION_COLOR_OUTPUT | |
| #ifdef @STORE_COLOR_CLEAR | |
| if (@STORE_COLOR_CLEAR) | |
| { | |
| PLS_STORE4F(colorBuffer, unpackUnorm4x8(uniforms.colorClearValue)); | |
| } | |
| #endif | |
| #ifdef @LOAD_COLOR_FROM_DST_TEXTURE | |
| if (@LOAD_COLOR_FROM_DST_TEXTURE) | |
| { | |
| PLS_STORE4F(colorBuffer, TEXEL_FETCH(@imageTexture, _plsCoord)); | |
| } | |
| #endif | |
| #ifdef @SWIZZLE_COLOR_BGRA_TO_RGBA | |
| half4 color = PLS_LOAD4F(colorBuffer); | |
| PLS_STORE4F(colorBuffer, color.bgra); | |
| #endif | |
| #endif | |
| PLS_STOREUI_UAV(coverageAtomicBuffer, uniforms.coverageClearValue); | |
| #ifdef @ENABLE_CLIPPING | |
| if (@ENABLE_CLIPPING) | |
| { | |
| PLS_STOREUI(clipBuffer, 0u); | |
| } | |
| #endif | |
| #ifdef @FIXED_FUNCTION_COLOR_OUTPUT | |
| discard; | |
| #endif | |
| EMIT_ATOMIC_PLS | |
| } | |
| #endif // INITIALIZE_PLS | |
| #ifdef @RESOLVE_PLS | |
| #ifdef @COALESCED_PLS_RESOLVE_AND_TRANSFER | |
| PLS_FRAG_COLOR_MAIN(@drawFragmentMain) | |
| #else | |
| ATOMIC_PLS_MAIN(@drawFragmentMain) | |
| #endif | |
| { | |
| uint lastCoverageData = PLS_LOADUI_UAV(coverageAtomicBuffer); | |
| half coverageCount = from_fixed(lastCoverageData & FIXED_COVERAGE_MASK); | |
| ushort lastPathID = | |
| cast_uint_to_ushort(lastCoverageData >> FIXED_COVERAGE_BIT_COUNT); | |
| lastPathID = apply_driver_workaround_for_path_id(lastPathID); | |
| half4 fragColorOut; | |
| resolve_paint(lastPathID, | |
| coverageCount, | |
| fragColorOut FRAGMENT_CONTEXT_UNPACK PLS_CONTEXT_UNPACK); | |
| #ifdef @COALESCED_PLS_RESOLVE_AND_TRANSFER | |
| float oneMinusSrcAlpha = 1. - fragColorOut.a; | |
| if (oneMinusSrcAlpha != .0) | |
| fragColorOut += PLS_LOAD4F(colorBuffer) * oneMinusSrcAlpha; | |
| _fragColor = fragColorOut; | |
| EMIT_PLS_AND_FRAG_COLOR | |
| #else | |
| fragColorOut.rgb = add_dither_if_alpha_nonzero(fragColorOut.rgb, | |
| fragColorOut.a, | |
| _fragCoord.xy, | |
| uniforms.ditherScale, | |
| uniforms.ditherBias); | |
| #ifdef @FIXED_FUNCTION_COLOR_OUTPUT | |
| _fragColor = fragColorOut; | |
| #else | |
| blend_pls_color_src_over(fragColorOut PLS_CONTEXT_UNPACK); | |
| #endif | |
| EMIT_ATOMIC_PLS | |
| #endif // COALESCED_PLS_RESOLVE_AND_TRANSFER | |
| } | |
| #endif // RESOLVE_PLS | |
| #endif // FRAGMENT |