pathfinder/shaders/d3d11/bin.cs.glsl

257 lines
9.1 KiB
Plaintext
Raw Permalink Normal View History

2020-06-23 15:35:57 -04:00
#version 430
// pathfinder/shaders/bin.cs.glsl
//
// Copyright © 2020 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Assigns microlines to tiles.
#extension GL_GOOGLE_include_directive : enable
#define MAX_ITERATIONS 1024u
#define STEP_DIRECTION_NONE 0
#define STEP_DIRECTION_X 1
#define STEP_DIRECTION_Y 2
#define TILE_FIELD_NEXT_TILE_ID 0
#define TILE_FIELD_FIRST_FILL_ID 1
#define TILE_FIELD_BACKDROP_ALPHA_TILE_ID 2
#define TILE_FIELD_CONTROL 3
precision highp float;
#ifdef GL_ES
precision highp sampler2D;
#endif
layout(local_size_x = 64) in;
uniform int uMicrolineCount;
// How many slots we have allocated for fills.
uniform int uMaxFillCount;
layout(std430, binding = 0) buffer bMicrolines {
restrict readonly uvec4 iMicrolines[];
};
layout(std430, binding = 1) buffer bMetadata {
// [0]: tile rect
// [1].x: tile offset
// [1].y: path ID
// [1].z: z write flag
// [1].w: clip path ID
// [2].x: backdrop offset
restrict readonly ivec4 iMetadata[];
};
// [0]: vertexCount (6)
// [1]: instanceCount (of fills)
// [2]: vertexStart (0)
// [3]: baseInstance (0)
// [4]: alpha tile count
layout(std430, binding = 2) buffer bIndirectDrawParams {
restrict uint iIndirectDrawParams[];
};
layout(std430, binding = 3) buffer bFills {
restrict writeonly uint iFills[];
};
layout(std430, binding = 4) buffer bTiles {
// [0]: next tile ID (initialized to -1)
// [1]: first fill ID (initialized to -1)
// [2]: backdrop delta upper 8 bits, alpha tile ID lower 24 (initialized to 0, -1 respectively)
// [3]: color/ctrl/backdrop word
restrict uint iTiles[];
};
layout(std430, binding = 5) buffer bBackdrops {
// [0]: backdrop
// [1]: tile X offset
// [2]: path ID
restrict uint iBackdrops[];
};
uint computeTileIndexNoCheck(ivec2 tileCoords, ivec4 pathTileRect, uint pathTileOffset) {
ivec2 offsetCoords = tileCoords - pathTileRect.xy;
return pathTileOffset + offsetCoords.x + offsetCoords.y * (pathTileRect.z - pathTileRect.x);
}
bvec4 computeTileOutcodes(ivec2 tileCoords, ivec4 pathTileRect) {
return bvec4(lessThan(tileCoords, pathTileRect.xy),
greaterThanEqual(tileCoords, pathTileRect.zw));
}
bool computeTileIndex(ivec2 tileCoords,
ivec4 pathTileRect,
uint pathTileOffset,
out uint outTileIndex) {
outTileIndex = computeTileIndexNoCheck(tileCoords, pathTileRect, pathTileOffset);
return !any(computeTileOutcodes(tileCoords, pathTileRect));
}
void addFill(vec4 lineSegment, ivec2 tileCoords, ivec4 pathTileRect, uint pathTileOffset) {
// Compute tile offset. If out of bounds, cull.
uint tileIndex;
if (!computeTileIndex(tileCoords, pathTileRect, pathTileOffset, tileIndex)) {
return;
}
// Clip line. If too narrow, cull.
uvec4 scaledLocalLine = uvec4((lineSegment - vec4(tileCoords.xyxy * ivec4(16))) * vec4(256.0));
if (scaledLocalLine.x == scaledLocalLine.z)
return;
// Bump instance count.
uint fillIndex = atomicAdd(iIndirectDrawParams[1], 1);
// Fill out the link field, inserting into the linked list.
uint fillLink = atomicExchange(iTiles[tileIndex * 4 + TILE_FIELD_FIRST_FILL_ID],
int(fillIndex));
// Write fill.
if (fillIndex < uMaxFillCount) {
iFills[fillIndex * 3 + 0] = scaledLocalLine.x | (scaledLocalLine.y << 16);
iFills[fillIndex * 3 + 1] = scaledLocalLine.z | (scaledLocalLine.w << 16);
iFills[fillIndex * 3 + 2] = fillLink;
}
}
void adjustBackdrop(int backdropDelta,
ivec2 tileCoords,
ivec4 pathTileRect,
uint pathTileOffset,
uint pathBackdropOffset) {
bvec4 outcodes = computeTileOutcodes(tileCoords, pathTileRect);
if (any(outcodes)) {
if (!outcodes.x && outcodes.y && !outcodes.z) {
uint backdropIndex = pathBackdropOffset + uint(tileCoords.x - pathTileRect.x);
atomicAdd(iBackdrops[backdropIndex * 3], backdropDelta);
}
} else {
uint tileIndex = computeTileIndexNoCheck(tileCoords, pathTileRect, pathTileOffset);
atomicAdd(iTiles[tileIndex * 4 + TILE_FIELD_BACKDROP_ALPHA_TILE_ID],
uint(backdropDelta) << 24);
}
}
vec4 unpackMicroline(uvec4 packedMicroline, out uint outPathIndex) {
outPathIndex = packedMicroline.w;
ivec4 signedMicroline = ivec4(packedMicroline);
return vec4((signedMicroline.x << 16) >> 16, signedMicroline.x >> 16,
(signedMicroline.y << 16) >> 16, signedMicroline.y >> 16) +
vec4(signedMicroline.z & 0xff, (signedMicroline.z >> 8) & 0xff,
(signedMicroline.z >> 16) & 0xff, (signedMicroline.z >> 24) & 0xff) / 256.0;
}
void main() {
uint segmentIndex = gl_GlobalInvocationID.x;
if (segmentIndex >= uMicrolineCount)
return;
uint pathIndex;
vec4 lineSegment = unpackMicroline(iMicrolines[segmentIndex], pathIndex);
ivec4 pathTileRect = iMetadata[pathIndex * 3 + 0];
uint pathTileOffset = uint(iMetadata[pathIndex * 3 + 1].x);
uint pathBackdropOffset = uint(iMetadata[pathIndex * 3 + 2].x);
// Following is a straight port of `process_line_segment()`:
ivec2 tileSize = ivec2(16);
ivec4 tileLineSegment = ivec4(floor(lineSegment / vec4(tileSize.xyxy)));
ivec2 fromTileCoords = tileLineSegment.xy, toTileCoords = tileLineSegment.zw;
vec2 vector = lineSegment.zw - lineSegment.xy;
vec2 vectorIsNegative = vec2(vector.x < 0.0 ? -1.0 : 0.0, vector.y < 0.0 ? -1.0 : 0.0);
ivec2 tileStep = ivec2(vector.x < 0.0 ? -1 : 1, vector.y < 0.0 ? -1 : 1);
vec2 firstTileCrossing = vec2((fromTileCoords + ivec2(vector.x >= 0.0 ? 1 : 0,
vector.y >= 0.0 ? 1 : 0)) * tileSize);
vec2 tMax = (firstTileCrossing - lineSegment.xy) / vector;
vec2 tDelta = abs(tileSize / vector);
vec2 currentPosition = lineSegment.xy;
ivec2 tileCoords = fromTileCoords;
int lastStepDirection = STEP_DIRECTION_NONE;
uint iteration = 0;
while (iteration < MAX_ITERATIONS) {
int nextStepDirection;
if (tMax.x < tMax.y)
nextStepDirection = STEP_DIRECTION_X;
else if (tMax.x > tMax.y)
nextStepDirection = STEP_DIRECTION_Y;
else if (tileStep.x > 0.0)
nextStepDirection = STEP_DIRECTION_X;
else
nextStepDirection = STEP_DIRECTION_Y;
float nextT = min(nextStepDirection == STEP_DIRECTION_X ? tMax.x : tMax.y, 1.0);
// If we've reached the end tile, don't step at all.
if (tileCoords == toTileCoords)
nextStepDirection = STEP_DIRECTION_NONE;
vec2 nextPosition = mix(lineSegment.xy, lineSegment.zw, nextT);
vec4 clippedLineSegment = vec4(currentPosition, nextPosition);
addFill(clippedLineSegment, tileCoords, pathTileRect, pathTileOffset);
// Add extra fills if necessary.
vec4 auxiliarySegment;
bool haveAuxiliarySegment = false;
if (tileStep.y < 0 && nextStepDirection == STEP_DIRECTION_Y) {
auxiliarySegment = vec4(clippedLineSegment.zw, vec2(tileCoords * tileSize));
haveAuxiliarySegment = true;
} else if (tileStep.y > 0 && lastStepDirection == STEP_DIRECTION_Y) {
auxiliarySegment = vec4(vec2(tileCoords * tileSize), clippedLineSegment.xy);
haveAuxiliarySegment = true;
}
if (haveAuxiliarySegment)
addFill(auxiliarySegment, tileCoords, pathTileRect, pathTileOffset);
// Adjust backdrop if necessary.
//
// NB: Do not refactor the calls below. This exact code sequence is needed to avoid a
// miscompilation on the Radeon Metal compiler.
if (tileStep.x < 0 && lastStepDirection == STEP_DIRECTION_X) {
adjustBackdrop(1,
tileCoords,
pathTileRect,
pathTileOffset,
pathBackdropOffset);
} else if (tileStep.x > 0 && nextStepDirection == STEP_DIRECTION_X) {
adjustBackdrop(-1,
tileCoords,
pathTileRect,
pathTileOffset,
pathBackdropOffset);
}
// Take a step.
if (nextStepDirection == STEP_DIRECTION_X) {
tMax.x += tDelta.x;
tileCoords.x += tileStep.x;
} else if (nextStepDirection == STEP_DIRECTION_Y) {
tMax.y += tDelta.y;
tileCoords.y += tileStep.y;
} else if (nextStepDirection == STEP_DIRECTION_NONE) {
break;
}
currentPosition = nextPosition;
lastStepDirection = nextStepDirection;
iteration++;
}
}