eb89c3a428
These are transpiled to kernel functions that write the output of the shader to three buffers: one for per-vertex varyings, one for per-patch varyings, and one for the tessellation levels. This structure is mandated by the way Metal works, where the tessellation factors are supplied to the draw method in their own buffer, while the per-patch and per-vertex varyings are supplied as though they were vertex attributes; since they have different step rates, they must be in separate buffers. The kernel is expected to be run in a workgroup whose size is the greater of the number of input or output control points. It uses Metal's support for vertex-style stage input to a compute shader to get the input values; therefore, at least one instance must run per input point. Meanwhile, Vulkan mandates that it run at least once per output point. Overrunning the output array is a concern, but any values written should either be discarded or overwritten by subsequent patches. I'm probably going to put some slop space in the buffer when I integrate this into MoltenVK to be on the safe side.
116 lines
3.2 KiB
GLSL
116 lines
3.2 KiB
GLSL
#version 310 es
|
|
#extension GL_EXT_tessellation_shader : require
|
|
|
|
layout(vertices = 1) out;
|
|
layout(location = 0) in vec2 vPatchPosBase[];
|
|
|
|
layout(std140) uniform UBO
|
|
{
|
|
vec4 uScale;
|
|
highp vec3 uCamPos;
|
|
vec2 uPatchSize;
|
|
vec2 uMaxTessLevel;
|
|
float uDistanceMod;
|
|
vec4 uFrustum[6];
|
|
};
|
|
|
|
layout(location = 1) patch out vec2 vOutPatchPosBase;
|
|
layout(location = 2) patch out vec4 vPatchLods;
|
|
|
|
float lod_factor(vec2 pos_)
|
|
{
|
|
vec2 pos = pos_ * uScale.xy;
|
|
vec3 dist_to_cam = uCamPos - vec3(pos.x, 0.0, pos.y);
|
|
float level = log2((length(dist_to_cam) + 0.0001) * uDistanceMod);
|
|
return clamp(level, 0.0, uMaxTessLevel.x);
|
|
}
|
|
|
|
float tess_level(float lod)
|
|
{
|
|
return uMaxTessLevel.y * exp2(-lod);
|
|
}
|
|
|
|
vec4 tess_level(vec4 lod)
|
|
{
|
|
return uMaxTessLevel.y * exp2(-lod);
|
|
}
|
|
|
|
// Guard band for vertex displacement.
|
|
#define GUARD_BAND 10.0
|
|
bool frustum_cull(vec2 p0)
|
|
{
|
|
vec2 min_xz = (p0 - GUARD_BAND) * uScale.xy;
|
|
vec2 max_xz = (p0 + uPatchSize + GUARD_BAND) * uScale.xy;
|
|
|
|
vec3 bb_min = vec3(min_xz.x, -GUARD_BAND, min_xz.y);
|
|
vec3 bb_max = vec3(max_xz.x, +GUARD_BAND, max_xz.y);
|
|
vec3 center = 0.5 * (bb_min + bb_max);
|
|
float radius = 0.5 * length(bb_max - bb_min);
|
|
|
|
vec3 f0 = vec3(
|
|
dot(uFrustum[0], vec4(center, 1.0)),
|
|
dot(uFrustum[1], vec4(center, 1.0)),
|
|
dot(uFrustum[2], vec4(center, 1.0)));
|
|
|
|
vec3 f1 = vec3(
|
|
dot(uFrustum[3], vec4(center, 1.0)),
|
|
dot(uFrustum[4], vec4(center, 1.0)),
|
|
dot(uFrustum[5], vec4(center, 1.0)));
|
|
|
|
return !(any(lessThanEqual(f0, vec3(-radius))) || any(lessThanEqual(f1, vec3(-radius))));
|
|
}
|
|
|
|
void compute_tess_levels(vec2 p0)
|
|
{
|
|
vOutPatchPosBase = p0;
|
|
|
|
float l00 = lod_factor(p0 + vec2(-0.5, -0.5) * uPatchSize);
|
|
float l10 = lod_factor(p0 + vec2(+0.5, -0.5) * uPatchSize);
|
|
float l20 = lod_factor(p0 + vec2(+1.5, -0.5) * uPatchSize);
|
|
float l01 = lod_factor(p0 + vec2(-0.5, +0.5) * uPatchSize);
|
|
float l11 = lod_factor(p0 + vec2(+0.5, +0.5) * uPatchSize);
|
|
float l21 = lod_factor(p0 + vec2(+1.5, +0.5) * uPatchSize);
|
|
float l02 = lod_factor(p0 + vec2(-0.5, +1.5) * uPatchSize);
|
|
float l12 = lod_factor(p0 + vec2(+0.5, +1.5) * uPatchSize);
|
|
float l22 = lod_factor(p0 + vec2(+1.5, +1.5) * uPatchSize);
|
|
|
|
vec4 lods = vec4(
|
|
dot(vec4(l01, l11, l02, l12), vec4(0.25)),
|
|
dot(vec4(l00, l10, l01, l11), vec4(0.25)),
|
|
dot(vec4(l10, l20, l11, l21), vec4(0.25)),
|
|
dot(vec4(l11, l21, l12, l22), vec4(0.25)));
|
|
|
|
vPatchLods = lods;
|
|
|
|
vec4 outer_lods = min(lods.xyzw, lods.yzwx);
|
|
vec4 levels = tess_level(outer_lods);
|
|
gl_TessLevelOuter[0] = levels.x;
|
|
gl_TessLevelOuter[1] = levels.y;
|
|
gl_TessLevelOuter[2] = levels.z;
|
|
gl_TessLevelOuter[3] = levels.w;
|
|
|
|
float min_lod = min(min(lods.x, lods.y), min(lods.z, lods.w));
|
|
float inner = tess_level(min(min_lod, l11));
|
|
gl_TessLevelInner[0] = inner;
|
|
gl_TessLevelInner[1] = inner;
|
|
}
|
|
|
|
void main()
|
|
{
|
|
vec2 p0 = vPatchPosBase[0];
|
|
if (!frustum_cull(p0))
|
|
{
|
|
gl_TessLevelOuter[0] = -1.0;
|
|
gl_TessLevelOuter[1] = -1.0;
|
|
gl_TessLevelOuter[2] = -1.0;
|
|
gl_TessLevelOuter[3] = -1.0;
|
|
gl_TessLevelInner[0] = -1.0;
|
|
gl_TessLevelInner[1] = -1.0;
|
|
}
|
|
else
|
|
{
|
|
compute_tess_levels(p0);
|
|
}
|
|
}
|
|
|