pathfinder/resources/shaders/draw.gs.glsl

136 lines
4.4 KiB
GLSL

// Copyright 2017 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.
// A geometry shader fallback when tessellation is not available. This is *not* linked into the
// program by default.
//
// This will probably not perform well, but it's useful for testing, since llvmpipe does not
// support tessellation as of Jan. 2017.
//
// To use this shader, set `RasterizerOptions::force_geometry_shader` to true or set the
// environment variable `PATHFINDER_FORCE_GEOMETRY_SHADER` to 1.
#version 330
#define CURVE_THRESHOLD 0.333f
#define CURVE_TOLERANCE 3.0f
#define PIXELS_TO_DEVICE(x, y) (vec2((x), (y)) / vec2(uAtlasSize) * 2.0f - 1.0f)
#define SET_VARYINGS(primID) \
vP0 = lP0; \
vP1 = lP1; \
vDirection = direction; \
vSlope = slope; \
vYMinMax = yMinMax; \
gl_PrimitiveID = gl_PrimitiveIDIn + (primID)
layout(triangles) in;
layout(triangle_strip, max_vertices = 256) out;
// The size of the atlas in pixels.
uniform uvec2 uAtlasSize;
// The vertex ID, passed into this shader.
flat in int vVertexID[];
// The starting point of the segment.
flat out vec2 vP0;
// The endpoint of this segment.
flat out vec2 vP1;
// 1.0 if this segment runs left to right; -1.0 otherwise.
flat out float vDirection;
// The slope of this line.
flat out float vSlope;
// Minimum and maximum vertical extents, unrounded.
flat out vec2 vYMinMax;
void main() {
vec2 p0 = gl_in[0].gl_Position.xy;
vec2 p1 = gl_in[1].gl_Position.xy;
vec2 p2 = gl_in[2].gl_Position.xy;
// Compute direction. Flip around if necessary so that p0 is to the left of p2.
float direction;
if (p0.x < p2.x) {
direction = 1.0f;
} else {
direction = -1.0f;
vec2 tmp = p0;
p0 = p2;
p2 = tmp;
}
// Determine how many lines to divide into.
uint lineCount = 1u;
if (vVertexID[1] > 0) {
// Quadratic curve.
vec2 dev = p0 - 2.0f * p1 + p2;
float devSq = dot(dev, dev);
if (devSq >= CURVE_THRESHOLD) {
// Inverse square root is likely no slower and may be faster than regular square root
// (e.g. on x86).
lineCount += uint(floor(inversesqrt(inversesqrt(CURVE_TOLERANCE * devSq))));
}
}
// Divide into lines.
for (uint lineIndex = 0u; lineIndex < lineCount; lineIndex++) {
// Compute our endpoints (trivial if this is part of a line, less trivial if this is part
// of a curve).
vec2 lP0, lP1;
if (lineCount == 1u) {
lP0 = p0;
lP1 = p2;
} else {
float t0 = float(lineIndex + 0u) / float(lineCount);
float t1 = float(lineIndex + 1u) / float(lineCount);
lP0 = mix(mix(p0, p1, t0), mix(p1, p2, t0), t0);
lP1 = mix(mix(p0, p1, t1), mix(p1, p2, t1), t1);
}
// Compute Y extents and slope.
vec2 yMinMax = lP0.y <= lP1.y ? vec2(lP0.y, lP1.y) : vec2(lP1.y, lP0.y);
float slope = (lP1.y - lP0.y) / (lP1.x - lP0.x);
// Convert atlas space to device space.
vec2 pTL = PIXELS_TO_DEVICE(floor(lP0.x), floor(yMinMax.x));
vec2 pBR = PIXELS_TO_DEVICE(ceil(lP1.x), ceil(yMinMax.y) + 1.0f);
vec2 pTR = vec2(pBR.x, pTL.y);
vec2 pBL = vec2(pTL.x, pBR.y);
// Assign primitive IDs.
int primID0 = int(lineIndex) * 2, primID1 = int(lineIndex) * 2 + 1;
// Emit vertices.
SET_VARYINGS(primID0);
gl_Position = vec4(pTL, 0.0f, 1.0f);
EmitVertex();
SET_VARYINGS(primID0);
gl_Position = vec4(pTR, 0.0f, 1.0f);
EmitVertex();
SET_VARYINGS(primID0);
gl_Position = vec4(pBL, 0.0f, 1.0f);
EmitVertex();
EndPrimitive();
SET_VARYINGS(primID1);
gl_Position = vec4(pBR, 0.0f, 1.0f);
EmitVertex();
SET_VARYINGS(primID1);
gl_Position = vec4(pBL, 0.0f, 1.0f);
EmitVertex();
SET_VARYINGS(primID1);
gl_Position = vec4(pTR, 0.0f, 1.0f);
EmitVertex();
EndPrimitive();
}
}