Compare commits

...

3 Commits

Author SHA1 Message Date
bjorn 756b184306 Some font cleanup; 2022-06-25 19:54:13 -07:00
bjorn 02daeb1a2b The font code is pretty I promise; 2022-06-25 15:26:42 -07:00
bjorn 18a31349b4 boot.lua uses headset clip distance; 2022-06-25 14:37:49 -07:00
7 changed files with 295 additions and 224 deletions

View File

@ -139,9 +139,11 @@ function lovr.run()
local headset = lovr.headset and lovr.headset.getTexture()
if headset then
local pass = lovr.graphics.getPass('render', headset)
local near, far = lovr.headset.getClipDistance()
for i = 1, lovr.headset.getViewCount() do
pass:setViewPose(i, lovr.headset.getViewPose(i))
pass:setProjection(i, lovr.headset.getViewAngles(i))
local left, right, up, down = lovr.headset.getViewAngles(i)
pass:setProjection(i, left, right, up, down, near, far)
end
local skip = lovr.draw and lovr.draw(pass)
if not skip then lovr.graphics.submit(pass) end
@ -195,9 +197,11 @@ function lovr.errhand(message, traceback)
local texture = lovr.headset.getTexture()
if texture then
local pass = lovr.graphics.getPass('render', texture)
local near, far = lovr.headset.getClipDistance()
for i = 1, lovr.headset.getViewCount() do
pass:setViewPose(i, lovr.headset.getViewPose(i))
pass:setProjection(i, lovr.headset.getViewAngles(i))
local left, right, up, down = lovr.headset.getViewAngles(i)
pass:setProjection(i, left, right, up, down, near, far)
end
render(pass)
passes[#passes + 1] = pass

View File

@ -46,6 +46,13 @@ static int l_lovrRasterizerHasGlyphs(lua_State* L) {
return 1;
}
static int l_lovrRasterizerGetHeight(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
float height = lovrRasterizerGetHeight(rasterizer);
lua_pushnumber(L, height);
return 1;
}
static int l_lovrRasterizerGetAscent(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
float ascent = lovrRasterizerGetAscent(rasterizer);
@ -222,6 +229,7 @@ const luaL_Reg lovrRasterizer[] = {
{ "getFontSize", l_lovrRasterizerGetFontSize },
{ "getGlyphCount", l_lovrRasterizerGetGlyphCount },
{ "hasGlyphs", l_lovrRasterizerHasGlyphs },
{ "getHeight", l_lovrRasterizerGetHeight },
{ "getAscent", l_lovrRasterizerGetAscent },
{ "getDescent", l_lovrRasterizerGetDescent },
{ "getLineGap", l_lovrRasterizerGetLineGap },

View File

@ -1,5 +1,6 @@
#include "api.h"
#include "graphics/graphics.h"
#include "data/rasterizer.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
@ -25,9 +26,75 @@ static int l_lovrFontSetPixelDensity(lua_State* L) {
return 0;
}
static int l_lovrFontGetLineSpacing(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
float spacing = lovrFontGetLineSpacing(font);
lua_pushnumber(L, spacing);
return 1;
}
static int l_lovrFontSetLineSpacing(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
float spacing = luax_optfloat(L, 2, 1.f);
lovrFontSetLineSpacing(font, spacing);
return 0;
}
static int l_lovrFontGetHeight(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
const FontInfo* info = lovrFontGetInfo(font);
float density = lovrFontGetPixelDensity(font);
float height = lovrRasterizerGetHeight(info->rasterizer);
lua_pushnumber(L, height / density);
return 1;
}
static int l_lovrFontGetAscent(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
const FontInfo* info = lovrFontGetInfo(font);
float density = lovrFontGetPixelDensity(font);
float ascent = lovrRasterizerGetAscent(info->rasterizer);
lua_pushnumber(L, ascent / density);
return 1;
}
static int l_lovrFontGetDescent(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
const FontInfo* info = lovrFontGetInfo(font);
float density = lovrFontGetPixelDensity(font);
float descent = lovrRasterizerGetDescent(info->rasterizer);
lua_pushnumber(L, descent / density);
return 1;
}
static int l_lovrFontGetLineGap(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
const FontInfo* info = lovrFontGetInfo(font);
float density = lovrFontGetPixelDensity(font);
float lineGap = lovrRasterizerGetLineGap(info->rasterizer);
lua_pushnumber(L, lineGap / density);
return 1;
}
static int l_lovrFontGetLeading(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
const FontInfo* info = lovrFontGetInfo(font);
float density = lovrFontGetPixelDensity(font);
float leading = lovrRasterizerGetLeading(info->rasterizer);
lua_pushnumber(L, leading / density);
return 1;
}
const luaL_Reg lovrFont[] = {
{ "getRasterizer", l_lovrFontGetRasterizer },
{ "getPixelDensity", l_lovrFontGetPixelDensity },
{ "setPixelDensity", l_lovrFontSetPixelDensity },
{ "getLineSpacing", l_lovrFontGetLineSpacing },
{ "setLineSpacing", l_lovrFontSetLineSpacing },
{ "getHeight", l_lovrFontGetHeight },
{ "getAscent", l_lovrFontGetAscent },
{ "getDescent", l_lovrFontGetDescent },
{ "getLineGap", l_lovrFontGetLineGap },
{ "getLeading", l_lovrFontGetLeading },
{ NULL, NULL }
};

View File

@ -85,6 +85,10 @@ bool lovrRasterizerHasGlyphs(Rasterizer* rasterizer, const char* str, size_t len
return true;
}
float lovrRasterizerGetHeight(Rasterizer* rasterizer) {
return rasterizer->ascent - rasterizer->descent;
}
float lovrRasterizerGetAscent(Rasterizer* rasterizer) {
return rasterizer->ascent;
}

View File

@ -15,6 +15,7 @@ void lovrRasterizerGetBoundingBox(Rasterizer* rasterizer, float box[4]);
uint32_t lovrRasterizerGetGlyphCount(Rasterizer* rasterizer);
bool lovrRasterizerHasGlyph(Rasterizer* rasterizer, uint32_t codepoint);
bool lovrRasterizerHasGlyphs(Rasterizer* rasterizer, const char* str, size_t length);
float lovrRasterizerGetHeight(Rasterizer* rasterizer);
float lovrRasterizerGetAscent(Rasterizer* rasterizer);
float lovrRasterizerGetDescent(Rasterizer* rasterizer);
float lovrRasterizerGetLineGap(Rasterizer* rasterizer);

View File

@ -139,6 +139,7 @@ struct Font {
map_t glyphLookup;
map_t kerning;
float pixelDensity;
float lineSpacing;
Texture* atlas;
uint32_t atlasWidth;
uint32_t atlasHeight;
@ -1784,6 +1785,9 @@ Font* lovrFontCreate(FontInfo* info) {
font->atlasHeight <<= 1;
}
font->pixelDensity = lovrRasterizerGetHeight(info->rasterizer);
font->lineSpacing = 1.f;
return font;
}
@ -1810,6 +1814,166 @@ void lovrFontSetPixelDensity(Font* font, float pixelDensity) {
font->pixelDensity = pixelDensity;
}
float lovrFontGetLineSpacing(Font* font) {
return font->lineSpacing;
}
void lovrFontSetLineSpacing(Font* font, float spacing) {
font->lineSpacing = spacing;
}
static Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint) {
uint64_t hash = hash64(&codepoint, 4);
uint64_t index = map_get(&font->glyphLookup, hash);
if (index != MAP_NIL) {
return &font->glyphs.data[index];
}
arr_expand(&font->glyphs, 1);
map_set(&font->glyphLookup, hash, font->glyphs.length);
Glyph* glyph = &font->glyphs.data[font->glyphs.length++];
glyph->codepoint = codepoint;
glyph->advance = lovrRasterizerGetGlyphAdvance(font->info.rasterizer, codepoint);
if (lovrRasterizerIsGlyphEmpty(font->info.rasterizer, codepoint)) {
memset(glyph->atlas, 0, sizeof(glyph->atlas));
memset(glyph->box, 0, sizeof(glyph->atlas));
memset(glyph->uv, 0, sizeof(glyph->atlas));
return glyph;
}
lovrRasterizerGetGlyphBoundingBox(font->info.rasterizer, codepoint, glyph->box);
float width = glyph->box[2] - glyph->box[0];
float height = glyph->box[3] - glyph->box[1];
uint32_t pixelWidth = 2 * font->info.padding + (uint32_t) ceilf(width);
uint32_t pixelHeight = 2 * font->info.padding + (uint32_t) ceilf(height);
if (font->atlasX + pixelWidth > font->atlasWidth) {
font->atlasX = font->atlasWidth == font->atlasHeight ? 0 : font->atlasWidth >> 1;
font->atlasY += font->rowHeight;
if (font->atlasY + pixelHeight > font->atlasHeight) {
if (font->atlasWidth == font->atlasHeight) {
font->atlasX = font->atlasWidth;
font->atlasY = 0;
font->atlasWidth <<= 1;
font->rowHeight = 0;
} else {
font->atlasX = 0;
font->atlasY = font->atlasHeight;
font->atlasHeight <<= 1;
font->rowHeight = 0;
}
}
}
glyph->atlas[0] = font->atlasX;
glyph->atlas[1] = font->atlasY;
glyph->atlas[2] = pixelWidth;
glyph->atlas[3] = pixelHeight;
double unused;
// The glyph is centered in its rasterized rectangle (needs to be kept in sync with Rasterizer)
glyph->uv[0] = font->atlasX + font->info.padding + (1.f - modf(width, &unused)) / 2.f;
glyph->uv[1] = font->atlasY + font->info.padding + (1.f - modf(height, &unused)) / 2.f;
glyph->uv[2] = glyph->uv[0] + width;
glyph->uv[3] = glyph->uv[1] + height;
font->atlasX += pixelWidth;
font->rowHeight = MAX(font->rowHeight, font->atlasY + pixelHeight);
return glyph;
}
static void lovrFontUploadNewGlyphs(Font* font, uint32_t start) {
if (start >= font->glyphs.length) {
return;
}
if (!font->atlas || font->atlasWidth > font->atlas->info.width || font->atlasHeight > font->atlas->info.height) {
lovrCheck(font->atlasWidth <= 65536, "Font atlas is too big!");
Texture* atlas = lovrTextureCreate(&(TextureInfo) {
.type = TEXTURE_2D,
.format = FORMAT_RGBA32F,
.width = font->atlasWidth,
.height = font->atlasHeight,
.depth = 1,
.mipmaps = 1,
.samples = 1,
.usage = TEXTURE_SAMPLE | TEXTURE_TRANSFER,
.label = "Font Atlas"
});
float clear[4] = { 0.f, 0.f, 0.f, 0.f };
gpu_clear_texture(state.stream, atlas->gpu, clear, 0, ~0u, 0, ~0u);
// This barrier serves 2 purposes:
// - Ensure new atlas clear is finished/flushed before copying to it
// - Ensure any unsynchronized pending uploads to old atlas finish before copying to new atlas
gpu_barrier barrier;
barrier.prev = GPU_PHASE_TRANSFER;
barrier.next = GPU_PHASE_TRANSFER;
barrier.flush = GPU_CACHE_TRANSFER_WRITE;
barrier.clear = GPU_CACHE_TRANSFER_READ;
gpu_sync(state.stream, &barrier, 1);
if (font->atlas) {
uint32_t srcOffset[4] = { 0, 0, 0, 0 };
uint32_t dstOffset[4] = { 0, 0, 0, 0 };
uint32_t extent[3] = { font->atlas->info.width, font->atlas->info.height, 1 };
gpu_copy_textures(state.stream, font->atlas->gpu, atlas->gpu, srcOffset, dstOffset, extent);
lovrRelease(font->atlas, lovrTextureDestroy);
}
font->atlas = atlas;
lovrRelease(font->material, lovrMaterialDestroy);
font->material = lovrMaterialCreate(&(MaterialInfo) {
.data.color = { 1.f, 1.f, 1.f, 1.f },
.data.uvScale = { 1.f, 1.f },
.data.sdfRange = { font->info.spread / font->atlasWidth, font->info.spread / font->atlasHeight },
.texture = font->atlas
});
}
gpu_buffer* scratchpad = tempAlloc(gpu_sizeof_buffer());
for (uint32_t i = start; i < font->glyphs.length; i++) {
Glyph* glyph = &font->glyphs.data[i];
uint32_t width = glyph->atlas[2];
uint32_t height = glyph->atlas[3];
if (width == 0 || height == 0) {
continue;
}
void* pixels = gpu_map(scratchpad, width * height * 16, 16, GPU_MAP_WRITE);
lovrRasterizerGetGlyphPixels(font->info.rasterizer, glyph->codepoint, pixels, width, height, font->info.spread, font->info.padding);
uint32_t dstOffset[4] = { glyph->atlas[0], glyph->atlas[1], 0, 0 };
uint32_t extent[3] = { width, height, 1 };
gpu_copy_buffer_texture(state.stream, scratchpad, font->atlas->gpu, 0, dstOffset, extent);
}
state.hasGlyphUpload = true;
}
static float lovrFontGetKerning(Font* font, uint32_t previous, uint32_t codepoint) {
uint32_t codepoints[] = { previous, codepoint };
uint64_t hash = hash64(codepoints, sizeof(codepoints));
union { float f32; uint64_t u64; } kerning = { .u64 = map_get(&font->kerning, hash) };
if (kerning.u64 == MAP_NIL) {
kerning.f32 = lovrRasterizerGetKerning(font->info.rasterizer, previous, codepoint);
map_set(&font->kerning, hash, kerning.u64);
}
return kerning.f32;
}
// Pass
Pass* lovrGraphicsGetPass(PassInfo* info) {
@ -3178,28 +3342,31 @@ void lovrPassTorus(Pass* pass, float* transform, uint32_t segmentsT, uint32_t se
}
}
void lovrPassText(Pass* pass, Font* font, const char* text, uint32_t length, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign) {
if (!font) {
font = lovrGraphicsGetDefaultFont();
static void aline(GlyphVertex* vertices, uint32_t head, uint32_t tail, HorizontalAlign align) {
if (align == ALIGN_LEFT) return;
float shift = align / 2.f * vertices[tail - 1].position.x;
for (uint32_t i = head; i < tail; i++) {
vertices[i].position.x -= shift;
}
}
uint32_t originalGlyphsLength = font->glyphs.length;
void lovrPassText(Pass* pass, Font* font, const char* text, uint32_t length, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign) {
font = font ? font : lovrGraphicsGetDefaultFont();
uint32_t originalGlyphCount = font->glyphs.length;
GlyphVertex* vertices = tempAlloc(length * 4 * sizeof(GlyphVertex));
uint32_t vertexCount = 0;
uint32_t glyphCount = 0;
uint32_t lineCount = 1;
// Track vertex indices for align/wrap
uint32_t lineStart = 0;
uint32_t wordStart = 0;
// Cursor
float x = 0.f;
float y = 0.f;
float leading = lovrRasterizerGetLeading(font->info.rasterizer) * .8f;
float ascent = lovrRasterizerGetAscent(font->info.rasterizer) * .8f;
float scale = 1.f / ascent;
float leading = lovrRasterizerGetLeading(font->info.rasterizer) * font->lineSpacing;
float height = lovrRasterizerGetHeight(font->info.rasterizer);
float ascent = lovrRasterizerGetAscent(font->info.rasterizer);
float scale = 1.f / font->pixelDensity;
wrap /= scale;
size_t bytes;
@ -3207,129 +3374,33 @@ void lovrPassText(Pass* pass, Font* font, const char* text, uint32_t length, flo
uint32_t previous = '\0';
const char* end = text + length;
while ((bytes = utf8_decode(text, end, &codepoint)) > 0) {
uint64_t hash = hash64(&codepoint, 4);
uint64_t index = map_get(&font->glyphLookup, hash);
Glyph* glyph;
if (index == MAP_NIL) { // New glyph just dropped
map_set(&font->glyphLookup, hash, font->glyphs.length);
arr_expand(&font->glyphs, 1);
glyph = &font->glyphs.data[font->glyphs.length++];
glyph->codepoint = codepoint;
glyph->advance = lovrRasterizerGetGlyphAdvance(font->info.rasterizer, codepoint);
if (lovrRasterizerIsGlyphEmpty(font->info.rasterizer, codepoint)) {
memset(glyph->atlas, 0, sizeof(glyph->atlas));
memset(glyph->box, 0, sizeof(glyph->atlas));
memset(glyph->uv, 0, sizeof(glyph->atlas));
} else {
lovrRasterizerGetGlyphBoundingBox(font->info.rasterizer, codepoint, glyph->box);
glyph->box[0] -= font->info.padding;
glyph->box[1] -= font->info.padding;
glyph->box[2] += font->info.padding;
glyph->box[3] += font->info.padding;
float width = glyph->box[2] - glyph->box[0];
float height = glyph->box[3] - glyph->box[1];
uint32_t pixelWidth = 2 * font->info.padding + (uint32_t) ceilf(width);
uint32_t pixelHeight = 2 * font->info.padding + (uint32_t) ceilf(height);
if (font->atlasX + pixelWidth > font->atlasWidth) {
font->atlasX = font->atlasWidth == font->atlasHeight ? 0 : font->atlasWidth >> 1;
font->atlasY += font->rowHeight;
if (font->atlasY + pixelHeight > font->atlasHeight) {
if (font->atlasWidth == font->atlasHeight) {
font->atlasX = font->atlasWidth;
font->atlasY = 0;
font->atlasWidth <<= 1;
font->rowHeight = 0;
} else {
font->atlasX = 0;
font->atlasY = font->atlasHeight;
font->atlasHeight <<= 1;
font->rowHeight = 0;
}
}
}
glyph->atlas[0] = font->atlasX;
glyph->atlas[1] = font->atlasY;
glyph->atlas[2] = pixelWidth;
glyph->atlas[3] = pixelHeight;
double unused;
glyph->uv[0] = font->atlasX + font->info.padding + (1.f - modf(width, &unused)) / 2.f;
glyph->uv[1] = font->atlasY + font->info.padding + (1.f - modf(height, &unused)) / 2.f;
glyph->uv[2] = glyph->uv[0] + width;
glyph->uv[3] = glyph->uv[1] + height;
font->atlasX += pixelWidth;
font->rowHeight = MAX(font->rowHeight, font->atlasY + pixelHeight);
}
} else {
glyph = &font->glyphs.data[index];
}
if (codepoint == ' ') {
if (codepoint == ' ' || codepoint == '\t') {
Glyph* glyph = lovrFontGetGlyph(font, ' ');
wordStart = vertexCount;
x += glyph->advance;
previous = '\0';
text += bytes;
continue;
} else if (codepoint == '\t') {
wordStart = vertexCount;
x += lovrRasterizerGetGlyphAdvance(font->info.rasterizer, ' ') * 4.f;
x += codepoint == '\t' ? glyph->advance * 4.f : glyph->advance;
previous = '\0';
text += bytes;
continue;
} else if (codepoint == '\n') {
if (halign != ALIGN_LEFT) {
float lineWidth = vertices[vertexCount - 1].position.x;
float align = (float) halign / 2.f * lineWidth; // Sneakily compute shift ratio from halign
for (uint32_t i = lineStart; i < vertexCount; i++) {
vertices[i].position.x -= align;
}
}
aline(vertices, lineStart, vertexCount, halign);
lineStart = vertexCount;
wordStart = vertexCount;
previous = '\0';
text += bytes;
x = 0.f;
y -= leading;
lineCount++;
x = 0.f;
previous = '\0';
text += bytes;
continue;
}
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
// Keming
if (previous != '\0') {
uint32_t codepoints[] = { previous, codepoint };
hash = hash64(codepoints, sizeof(codepoints));
union { float f32; uint64_t u64; } kerning = { .u64 = map_get(&font->kerning, hash) };
if (kerning.u64 == MAP_NIL) {
kerning.f32 = lovrRasterizerGetKerning(font->info.rasterizer, previous, codepoint);
map_set(&font->kerning, hash, kerning.u64);
}
x += kerning.f32;
}
if (previous) x += lovrFontGetKerning(font, previous, codepoint);
previous = codepoint;
// Wrap
if (wrap > 0.f && x + glyph->box[2] > wrap && wordStart != lineStart) {
if (halign != ALIGN_LEFT) {
float lineWidth = vertices[wordStart - 1].position.x; // Edge of last glyph before this word
float align = (float) halign / 2.f * lineWidth; // Sneakily compute shift ratio from halign
for (uint32_t i = lineStart; i < wordStart; i++) {
vertices[i].position.x -= align;
}
}
float dx = wordStart == vertexCount ? x : vertices[wordStart].position.x;
float dy = leading;
@ -3339,132 +3410,46 @@ void lovrPassText(Pass* pass, Font* font, const char* text, uint32_t length, flo
vertices[i].position.y -= dy;
}
aline(vertices, lineStart, wordStart, halign);
lineStart = wordStart;
lineCount++;
x -= dx;
y -= dy;
}
// Vertices
if (glyph->atlas[2] > 0 && glyph->atlas[3] > 0) {
if (vertexCount == lineStart) {
x -= glyph->box[0];
}
vertices[vertexCount++] = (GlyphVertex) {
.position = { x + glyph->box[0], y + glyph->box[3] },
.uv = { glyph->uv[0], glyph->uv[1] }
};
vertices[vertexCount++] = (GlyphVertex) {
.position = { x + glyph->box[2], y + glyph->box[3] },
.uv = { glyph->uv[2], glyph->uv[1] }
};
vertices[vertexCount++] = (GlyphVertex) {
.position = { x + glyph->box[0], y + glyph->box[1] },
.uv = { glyph->uv[0], glyph->uv[3] }
};
vertices[vertexCount++] = (GlyphVertex) {
.position = { x + glyph->box[2], y + glyph->box[1] },
.uv = { glyph->uv[2], glyph->uv[3] }
};
glyphCount++;
// Ignore bearing for the first character on a line so it lines up perfectly (questionable)
if (vertexCount == lineStart) {
x -= glyph->box[0];
}
// Vertices
float* bb = glyph->box;
float* uv = glyph->uv;
vertices[vertexCount++] = (GlyphVertex) { { x + bb[0], y + bb[3] }, { uv[0], uv[1] } };
vertices[vertexCount++] = (GlyphVertex) { { x + bb[2], y + bb[3] }, { uv[2], uv[1] } };
vertices[vertexCount++] = (GlyphVertex) { { x + bb[0], y + bb[1] }, { uv[0], uv[3] } };
vertices[vertexCount++] = (GlyphVertex) { { x + bb[2], y + bb[1] }, { uv[2], uv[3] } };
glyphCount++;
// Advance
x += glyph->advance;
text += bytes;
}
if (halign != ALIGN_LEFT) { // Align last line
float lineWidth = vertices[vertexCount - 1].position.x;
float align = (float) halign / 2.f * lineWidth;
for (uint32_t i = lineStart; i < vertexCount; i++) {
vertices[i].position.x -= align;
}
}
// Align last line
aline(vertices, lineStart, vertexCount, halign);
for (uint32_t i = 0; i < vertexCount; i++) { // Normalize UVs now that final atlas size is known
// Normalize UVs now that final atlas size is known
for (uint32_t i = 0; i < vertexCount; i++) {
vertices[i].uv.u /= font->atlasWidth;
vertices[i].uv.v /= font->atlasHeight;
}
// If any glyphs were added, resize texture if needed and paste new glyphs (the slow part)
if (font->glyphs.length > originalGlyphsLength) {
if (!font->atlas || font->atlasWidth > font->atlas->info.width || font->atlasHeight > font->atlas->info.height) {
lovrCheck(font->atlasWidth <= 65536, "Font atlas is too big!");
Texture* atlas = lovrTextureCreate(&(TextureInfo) {
.type = TEXTURE_2D,
.format = FORMAT_RGBA32F,
.width = font->atlasWidth,
.height = font->atlasHeight,
.depth = 1,
.mipmaps = 1,
.samples = 1,
.usage = TEXTURE_SAMPLE | TEXTURE_TRANSFER,
.label = "Font Atlas"
});
float clear[4] = { 0.f, 0.f, 0.f, 0.f };
gpu_clear_texture(state.stream, atlas->gpu, clear, 0, ~0u, 0, ~0u);
// This barrier serves 2 purposes:
// - Ensure new atlas clear is finished/flushed before copying to it
// - Ensure any unsynchronized pending uploads to old atlas finish before copying to new atlas
gpu_barrier barrier;
barrier.prev = GPU_PHASE_TRANSFER;
barrier.next = GPU_PHASE_TRANSFER;
barrier.flush = GPU_CACHE_TRANSFER_WRITE;
barrier.clear = GPU_CACHE_TRANSFER_READ;
gpu_sync(state.stream, &barrier, 1);
if (font->atlas) {
uint32_t srcOffset[4] = { 0, 0, 0, 0 };
uint32_t dstOffset[4] = { 0, 0, 0, 0 };
uint32_t extent[3] = { font->atlas->info.width, font->atlas->info.height, 1 };
gpu_copy_textures(state.stream, font->atlas->gpu, atlas->gpu, srcOffset, dstOffset, extent);
lovrRelease(font->atlas, lovrTextureDestroy);
}
font->atlas = atlas;
lovrRelease(font->material, lovrMaterialDestroy);
font->material = lovrMaterialCreate(&(MaterialInfo) {
.data.color = { 1.f, 1.f, 1.f, 1.f },
.data.uvScale = { 1.f, 1.f },
.data.sdfRange = { font->info.spread / font->atlasWidth, font->info.spread / font->atlasHeight },
.texture = font->atlas
});
}
gpu_buffer* scratchpad = tempAlloc(gpu_sizeof_buffer());
for (uint32_t i = originalGlyphsLength; i < font->glyphs.length; i++) {
Glyph* glyph = &font->glyphs.data[i];
uint32_t width = glyph->atlas[2];
uint32_t height = glyph->atlas[3];
if (width == 0 || height == 0) {
continue;
}
void* pixels = gpu_map(scratchpad, width * height * 16, 16, GPU_MAP_WRITE);
lovrRasterizerGetGlyphPixels(font->info.rasterizer, glyph->codepoint, pixels, width, height, font->info.spread, font->info.padding);
uint32_t dstOffset[4] = { glyph->atlas[0], glyph->atlas[1], 0, 0 };
uint32_t extent[3] = { width, height, 1 };
gpu_copy_buffer_texture(state.stream, scratchpad, font->atlas->gpu, 0, dstOffset, extent);
}
state.hasGlyphUpload = true;
}
// Resize atlas, rasterize new glyphs, upload them into atlas, recreate material
lovrFontUploadNewGlyphs(font, originalGlyphCount);
mat4_scale(transform, scale, scale, scale);
float totalHeight = ascent + leading * (lineCount - 1);
float totalHeight = height + leading * (lineCount - 1);
mat4_translate(transform, 0.f, -ascent + valign / 2.f * totalHeight, 0.f);
uint16_t* indices;

View File

@ -334,6 +334,8 @@ void lovrFontDestroy(void* ref);
const FontInfo* lovrFontGetInfo(Font* font);
float lovrFontGetPixelDensity(Font* font);
void lovrFontSetPixelDensity(Font* font, float pixelDensity);
float lovrFontGetLineSpacing(Font* font);
void lovrFontSetLineSpacing(Font* font, float spacing);
// Pass