GPU-based font rendering (keming edition)

This commit is contained in:
2026-09-19 23:17:48 +02:00
parent f7576a941d
commit 183e825827
12 changed files with 966 additions and 35 deletions

BIN
assets/fonts/FiraMono-Regular.ttf (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -3,15 +3,17 @@ import "includes/structs";
struct Varyings {
float4 positionCS : SV_Position;
nointerpolation uint instance;
float2 texCoordEM : U_TEXCOORD;
float2 texCoordEM;
}
struct GuiText {
float4 color;
// Index into _GlyphData
uint glyph;
float2 bottomLeftSSPX;
float fontSizePX;
// Index into _GlyphData[font]
uint16_t glyph;
// Index into _GlyphData, _ControlPoints and _BandData
uint16_t font;
}
struct GlyphData {
@@ -28,10 +30,9 @@ struct GlyphData {
[[vk::binding(0, 0)]] ConstantBuffer<GlobalUniforms> _Global;
[[vk::binding(1, 0)]] StructuredBuffer<GuiText> _Instances;
[[vk::binding(0, 1)]] StructuredBuffer<GlyphData> _GlyphData;
[[vk::binding(1, 1)]] StructuredBuffer<half2> _ControlPoints;
[[vk::binding(2, 1)]] StructuredBuffer<uint> _BandData;
[[vk::binding(2, 0)]] StructuredBuffer<GlyphData> _GlyphData[];
[[vk::binding(3, 0)]] StructuredBuffer<half2> _ControlPoints[];
[[vk::binding(4, 0)]] StructuredBuffer<uint> _BandData[];
static const float2 VERTICES[4] = {
float2(0, 1),
@@ -70,7 +71,7 @@ uint classifyQuadCurve(float y0, float y1, float y2)
let class = (sign0 << 0) | (sign1 << 1) | (sign2 << 2);
let shift = 2 * class;
return (0x2E74U >> shift) & 0b11U;
return (0x1DB8 >> shift) & 0b11U;
}
// Find intersections between a quadratic Bézier curve and `y = 0` line by
@@ -156,12 +157,12 @@ Varyings vertexMain(uint vertexID : SV_VulkanVertexID, uint instanceID : SV_Vulk
let vertex = VERTICES[vertexID];
let instance = _Instances[instanceID];
let glyph = _GlyphData[instance.glyph];
let glyph = _GlyphData[instance.font][instance.glyph];
let normal = 2.0 * vertex - 1.0;
let sizePX = instance.fontSizePX * glyph.sizeEM;
let positionSSPX = vertex * sizePX + instance.bottomLeftSSPX;
let positionSSPX = vertex * float2(sizePX.x, -sizePX.y) + instance.bottomLeftSSPX;
let dilatedPositionSSPX = positionSSPX + 0.5 * normal;
let dilatedPositionCS = float4(mul(_Global.matrixSSPXtoCS, float3(positionSSPX, 1.0)), 0.0, 1.0);
@@ -177,7 +178,9 @@ Varyings vertexMain(uint vertexID : SV_VulkanVertexID, uint instanceID : SV_Vulk
[shader("fragment")]
float4 fragmentMain(Varyings frag) : SV_Target {
let instance = _Instances[frag.instance];
let glyph = _GlyphData[instance.glyph];
let glyph = _GlyphData[instance.font][instance.glyph];
let bandData = _BandData[instance.font];
let controlPoints = _ControlPoints[instance.font];
let emPerPX = fwidth(frag.texCoordEM);
let pxPerEM = 1.0 / emPerPX;
@@ -191,17 +194,17 @@ float4 fragmentMain(Varyings frag) : SV_Target {
var xWeight = 0.0;
let horizontalBandPtr = glyph.bandPtr.y + band.y;
let horizontalBandData = _BandData[horizontalBandPtr];
let horizontalBandData = bandData[horizontalBandPtr];
let horizontalBandCurvePtr = horizontalBandPtr + (horizontalBandData >> 16);
let horizontalBandCurveCount = horizontalBandData & 0xFFFF;
for (uint horizontalBandCurveID = 0; horizontalBandCurveID < horizontalBandCurveCount; horizontalBandCurveID++) {
let cpPtr = _BandData[horizontalBandCurvePtr + horizontalBandCurveID];
let cp0 = float2(_ControlPoints[cpPtr]) - frag.texCoordEM;
let cp1 = float2(_ControlPoints[cpPtr + 1]) - frag.texCoordEM;
let cp2 = float2(_ControlPoints[cpPtr + 2]) - frag.texCoordEM;
let cpPtr = bandData[horizontalBandCurvePtr + horizontalBandCurveID];
let cp0 = float2(controlPoints[cpPtr + 0]) - frag.texCoordEM;
let cp1 = float2(controlPoints[cpPtr + 1]) - frag.texCoordEM;
let cp2 = float2(controlPoints[cpPtr + 2]) - frag.texCoordEM;
if (max(max(cp0.x, cp1.x), cp2.x) * pxPerEM.x < -0.5) break;
if (max(max(cp0.x, cp1.x), cp2.x) * pxPerEM.x < -0.5) break; // XXX
let rootFlags = classifyQuadCurve(cp0.y, cp1.y, cp2.y);
if (rootFlags != 0U) {
@@ -228,17 +231,17 @@ float4 fragmentMain(Varyings frag) : SV_Target {
var yWeight = 0.0;
let verticalBandPtr = glyph.bandPtr.x + band.x;
let verticalBandData = _BandData[verticalBandPtr];
let verticalBandData = bandData[verticalBandPtr];
let verticalBandCurvePtr = verticalBandPtr + (verticalBandData >> 16);
let verticalBandCurveCount = verticalBandData & 0xFFFF;
for (uint verticalBandCurveID = 0; verticalBandCurveID < verticalBandCurveCount; verticalBandCurveID++) {
let cpPtr = _BandData[verticalBandCurvePtr + verticalBandCurveID];
let cp0 = float2(_ControlPoints[cpPtr]) - frag.texCoordEM;
let cp1 = float2(_ControlPoints[cpPtr + 1]) - frag.texCoordEM;
let cp2 = float2(_ControlPoints[cpPtr + 2]) - frag.texCoordEM;
let cpPtr = bandData[verticalBandCurvePtr + verticalBandCurveID];
let cp0 = float2(controlPoints[cpPtr + 0]) - frag.texCoordEM;
let cp1 = float2(controlPoints[cpPtr + 1]) - frag.texCoordEM;
let cp2 = float2(controlPoints[cpPtr + 2]) - frag.texCoordEM;
if (max(max(cp0.y, cp1.y), cp2.y) * pxPerEM.y < -0.5) break;
if (max(max(cp0.y, cp1.y), cp2.y) * pxPerEM.y < -0.5) break; // XXX
let rootFlags = classifyQuadCurve(cp0.x, cp1.x, cp2.x);
if (rootFlags != 0U) {