Skip to content

Instantly share code, notes, and snippets.

@mmozeiko
Last active February 4, 2024 17:53
Show Gist options
  • Star 43 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save mmozeiko/c7cd68ba0733a0d9e4f0a97691a50d39 to your computer and use it in GitHub Desktop.
Save mmozeiko/c7cd68ba0733a0d9e4f0a97691a50d39 to your computer and use it in GitHub Desktop.
compute shader for rendering monospaced glyphs in grid
//
struct TerminalCell
{
// cell index into GlyphTexture, should be two 16-bit (x,y) values packed: "x | (y << 16)"
uint GlyphIndex;
// 0xAABBGGRR encoded colors, nonzero alpha for Foreground indicates to render colored-glyph
// which means use RGB values from GlyphTexture directly as output, not as ClearType blending weights
uint Foreground;
uint Background;
};
cbuffer ConstBuffer : register(b0)
{
uint2 CellSize;
uint2 TermSize;
};
// TermSize.x * TermSize.y amount of cells to render as output
StructuredBuffer<TerminalCell> Cells : register(t0);
// RGB blending weights for ClearType
// or alpha-premultipled RGB values for colored glyphs
Texture2D<float4> GlyphTexture : register(t1);
RWTexture2D<float4> Output : register(u0);
float3 GetColor(uint i)
{
int r = i & 0xff;
int g = (i >> 8) & 0xff;
int b = (i >> 16) & 0xff;
return float3(r, g, b) / 255.0;
}
uint2 GetGlyphPos(uint GlyphIndex)
{
return uint2(GlyphIndex & 0xffff, GlyphIndex >> 16) * CellSize;
}
// dispatch with (TermSize*CellSize+7)/8 groups for x,y and 1 for z
[numthreads(8, 8, 1)]
void shader(uint3 Id: SV_DispatchThreadID)
{
uint2 ScreenPos = Id.xy;
// index of cell on screen
uint2 CellIndex = ScreenPos / CellSize;
// pixel position in cell
uint2 CellPos = ScreenPos % CellSize;
TerminalCell Cell = Cells[CellIndex.y * TermSize.x + CellIndex.x];
// position where glyph starts in texture
uint2 GlyphPos = GetGlyphPos(Cell.GlyphIndex);
// absolute pixel location in texture to use for output
uint2 PixelPos = GlyphPos + CellPos;
float4 Tex = GlyphTexture[PixelPos];
float3 Background = GetColor(Cell.Background);
float3 Foreground = GetColor(Cell.Foreground);
bool ColoredGlyph = (Cell.Foreground >> 24) != 0;
float3 Color;
if (ColoredGlyph)
{
// colored glyphs are alpha premultiplied
Color = Background * (1.0 - Tex.a) + Tex.rgb;
}
else
{
// TODO: proper ClearType blending
Color = lerp(Background, Foreground, Tex.rgb);
}
Output[ScreenPos] = float4(Color, 1);
}
@CCDD564
Copy link

CCDD564 commented Nov 3, 2021

Thank you for your answer and also my apologies, I should have looked into the terminology of what monospace meant. I was thinking that the fonts are "proportional". So this technique know makes sense given that monospaced glyphs are all equal in width and are fixed-pitch, and each cell can take any glyph (mostly Latin).
In a text editor the fonts are proportional and usually they support smooth scrolling, so this technique won't work. My guess is a text editor would just send an array of quads to the gpu each frame(depending how many characters you're rendering on the screen), and issue one draw call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment