Compare commits

...

9 Commits

Author SHA1 Message Date
bjorn d9d54ce348 Pass:copy(tally, buffer); 2022-06-30 18:51:03 -07:00
bjorn 8b37b25e54 TIME WIZARD; 2022-06-30 18:47:01 -07:00
bjorn a521f11a44 lovrPassCopyTallyToBuffer; 2022-06-30 18:46:47 -07:00
bjorn d06e0c8b09 gpu: tally waits for query results; 2022-06-30 17:34:30 -07:00
bjorn 0417e9095d gpu: add timestampPeriod limit; 2022-06-30 17:34:30 -07:00
bjorn c327eb103f Tally; 2022-06-30 17:34:29 -07:00
bjorn 9e7bd34ab1 Font:getWidth; 2022-06-30 17:25:47 -07:00
bjorn 4125b1dc7e mv getWrap getLines; Include bearing/advance in width; 2022-06-30 17:07:47 -07:00
bjorn 8e968cecd0 Fixes; 2022-06-30 00:29:52 -07:00
8 changed files with 392 additions and 38 deletions

View File

@ -2,6 +2,7 @@
#include "shaders/unlit.frag.h"
#include "shaders/font.frag.h"
#include "shaders/fill.vert.h"
#include "shaders/timewizard.comp.h"
#include "shaders/lovr.glsl.h"

View File

@ -0,0 +1,27 @@
#version 460
layout(local_size_x = 32) in;
layout(push_constant) uniform Constants {
uint first;
uint count;
uint views;
float period;
};
layout(set = 0, binding = 0) buffer readonly restrict Src { uint src[]; };
layout(set = 0, binding = 1) buffer writeonly restrict Dst { uint dst[]; };
void main() {
uint id = gl_GlobalInvocationID.x;
if (id >= count) return;
uint base = (first + id) * views * 2;
uint total = 0;
for (uint i = 0; i < views; i++) {
total += src[base + views + i] - src[base + i];
}
dst[id] = uint(total * period);
}

View File

@ -106,18 +106,28 @@ static void online(void* context, const char* string, size_t length) {
lua_rawseti(L, -2, index);
}
static int l_lovrFontGetWrap(lua_State* L) {
static int l_lovrFontGetLines(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
uint32_t count;
ColoredString stack;
ColoredString* strings = luax_checkcoloredstrings(L, 2, &count, &stack);
float wrap = luax_checkfloat(L, 3);
lua_newtable(L);
lovrFontGetWrap(font, strings, 1, wrap, online, L);
lovrFontGetLines(font, strings, 1, wrap, online, L);
if (strings != &stack) free(strings);
return 1;
}
static int l_lovrFontGetWidth(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
uint32_t count;
ColoredString stack;
ColoredString* strings = luax_checkcoloredstrings(L, 2, &count, &stack);
float width = lovrFontGetWidth(font, strings, count);
lua_pushnumber(L, width);
return 1;
}
const luaL_Reg lovrFont[] = {
{ "getRasterizer", l_lovrFontGetRasterizer },
{ "getPixelDensity", l_lovrFontGetPixelDensity },
@ -128,6 +138,7 @@ const luaL_Reg lovrFont[] = {
{ "getDescent", l_lovrFontGetDescent },
{ "getHeight", l_lovrFontGetHeight },
{ "getKerning", l_lovrFontGetKerning },
{ "getWrap", l_lovrFontGetWrap },
{ "getWidth", l_lovrFontGetWidth },
{ "getLines", l_lovrFontGetLines },
{ NULL, NULL }
};

View File

@ -706,7 +706,7 @@ static int l_lovrPassCopy(lua_State* L) {
return 0;
}
Image* image = luax_checktype(L, 2, Image);
Image* image = luax_totype(L, 2, Image);
if (image) {
Texture* texture = luax_checktype(L, 3, Texture);
@ -728,7 +728,7 @@ static int l_lovrPassCopy(lua_State* L) {
return 0;
}
Texture* texture = luax_checktype(L, 2, Texture);
Texture* texture = luax_totype(L, 2, Texture);
if (texture) {
Texture* dst = luax_checktype(L, 3, Texture);
@ -750,7 +750,18 @@ static int l_lovrPassCopy(lua_State* L) {
return 0;
}
return luax_typeerror(L, 2, "Blob, Buffer, Image, or Texture");
Tally* tally = luax_totype(L, 2, Tally);
if (tally) {
Buffer* buffer = luax_checktype(L, 3, Buffer);
uint32_t srcIndex = luax_optu32(L, 4, 0);
uint32_t dstOffset = luax_optu32(L, 5, 0);
uint32_t count = luax_optu32(L, 5, ~0u);
lovrPassCopyTallyToBuffer(pass, tally, buffer, srcIndex, dstOffset, count);
return 0;
}
return luax_typeerror(L, 2, "table, Blob, Buffer, Image, Texture, or Tally");
}
static int l_lovrPassBlit(lua_State* L) {

View File

@ -10,6 +10,7 @@ typedef struct gpu_shader gpu_shader;
typedef struct gpu_bundle_pool gpu_bundle_pool;
typedef struct gpu_bundle gpu_bundle;
typedef struct gpu_pipeline gpu_pipeline;
typedef struct gpu_tally gpu_tally;
typedef struct gpu_stream gpu_stream;
size_t gpu_sizeof_buffer(void);
@ -20,6 +21,7 @@ size_t gpu_sizeof_shader(void);
size_t gpu_sizeof_bundle_pool(void);
size_t gpu_sizeof_bundle(void);
size_t gpu_sizeof_pipeline(void);
size_t gpu_sizeof_tally(void);
// Buffer
@ -447,6 +449,22 @@ bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info)
bool gpu_pipeline_init_compute(gpu_pipeline* pipeline, gpu_compute_pipeline_info* info);
void gpu_pipeline_destroy(gpu_pipeline* pipeline);
// Tally
typedef enum {
GPU_TALLY_TIMER,
GPU_TALLY_PIXEL,
GPU_TALLY_PIPELINE
} gpu_tally_type;
typedef struct {
gpu_tally_type type;
uint32_t count;
} gpu_tally_info;
bool gpu_tally_init(gpu_tally* tally, gpu_tally_info* info);
void gpu_tally_destroy(gpu_tally* tally);
// Stream
typedef enum {
@ -547,10 +565,15 @@ void gpu_copy_buffers(gpu_stream* stream, gpu_buffer* src, gpu_buffer* dst, uint
void gpu_copy_textures(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t size[3]);
void gpu_copy_buffer_texture(gpu_stream* stream, gpu_buffer* src, gpu_texture* dst, uint32_t srcOffset, uint32_t dstOffset[4], uint32_t extent[3]);
void gpu_copy_texture_buffer(gpu_stream* stream, gpu_texture* src, gpu_buffer* dst, uint32_t srcOffset[4], uint32_t dstOffset, uint32_t extent[3]);
void gpu_copy_tally_buffer(gpu_stream* stream, gpu_tally* src, gpu_buffer* dst, uint32_t srcIndex, uint32_t dstOffset, uint32_t count, uint32_t stride);
void gpu_clear_buffer(gpu_stream* stream, gpu_buffer* buffer, uint32_t offset, uint32_t size);
void gpu_clear_texture(gpu_stream* stream, gpu_texture* texture, float value[4], uint32_t layer, uint32_t layerCount, uint32_t level, uint32_t levelCount);
void gpu_clear_tally(gpu_stream* stream, gpu_tally* tally, uint32_t index, uint32_t count);
void gpu_blit(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t srcExtent[3], uint32_t dstExtent[3], gpu_filter filter);
void gpu_sync(gpu_stream* stream, gpu_barrier* barriers, uint32_t count);
void gpu_tally_begin(gpu_stream* stream, gpu_tally* tally, uint32_t index);
void gpu_tally_end(gpu_stream* stream, gpu_tally* tally, uint32_t index);
void gpu_tally_mark(gpu_stream* stream, gpu_tally* tally, uint32_t index);
// Entry
@ -615,6 +638,7 @@ typedef struct {
uint32_t pushConstantSize;
uint32_t indirectDrawCount;
uint32_t instances;
float timestampPeriod;
float anisotropy;
float pointSize;
} gpu_limits;

View File

@ -55,6 +55,10 @@ struct gpu_pipeline {
VkPipeline handle;
};
struct gpu_tally {
VkQueryPool handle;
};
struct gpu_stream {
VkCommandBuffer commands;
};
@ -67,6 +71,7 @@ size_t gpu_sizeof_shader() { return sizeof(gpu_shader); }
size_t gpu_sizeof_bundle_pool() { return sizeof(gpu_bundle_pool); }
size_t gpu_sizeof_bundle() { return sizeof(gpu_bundle); }
size_t gpu_sizeof_pipeline() { return sizeof(gpu_pipeline); }
size_t gpu_sizeof_tally() { return sizeof(gpu_tally); }
// Internals
@ -262,6 +267,8 @@ static bool check(bool condition, const char* message);
X(vkCreateQueryPool)\
X(vkDestroyQueryPool)\
X(vkCmdResetQueryPool)\
X(vkCmdBeginQuery)\
X(vkCmdEndQuery)\
X(vkCmdWriteTimestamp)\
X(vkCmdCopyQueryPoolResults)\
X(vkCreateBuffer)\
@ -1350,6 +1357,40 @@ void gpu_pipeline_destroy(gpu_pipeline* pipeline) {
condemn(pipeline->handle, VK_OBJECT_TYPE_PIPELINE);
}
// Tally
bool gpu_tally_init(gpu_tally* tally, gpu_tally_info* info) {
VkQueryType queryTypes[] = {
[GPU_TALLY_TIMER] = VK_QUERY_TYPE_TIMESTAMP,
[GPU_TALLY_PIXEL] = VK_QUERY_TYPE_OCCLUSION,
[GPU_TALLY_PIPELINE] = VK_QUERY_TYPE_PIPELINE_STATISTICS
};
VkQueryPoolCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
.queryType = queryTypes[info->type],
.queryCount = info->count,
.pipelineStatistics = info->type == GPU_TALLY_PIPELINE ? (
VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT |
VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT |
VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT |
VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT |
VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT |
VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT
) : 0
};
VK(vkCreateQueryPool(state.device, &createInfo, NULL, &tally->handle), "Could not create query pool") {
return false;
}
return true;
}
void gpu_tally_destroy(gpu_tally* tally) {
condemn(tally->handle, VK_OBJECT_TYPE_QUERY_POOL);
}
// Stream
gpu_stream* gpu_stream_begin(const char* label) {
@ -1558,6 +1599,10 @@ void gpu_copy_texture_buffer(gpu_stream* stream, gpu_texture* src, gpu_buffer* d
vkCmdCopyImageToBuffer(stream->commands, src->handle, VK_IMAGE_LAYOUT_GENERAL, dst->handle, 1, &region);
}
void gpu_copy_tally_buffer(gpu_stream* stream, gpu_tally* src, gpu_buffer* dst, uint32_t srcIndex, uint32_t dstOffset, uint32_t count, uint32_t stride) {
vkCmdCopyQueryPoolResults(stream->commands, src->handle, srcIndex, count, dst->handle, dstOffset, stride, VK_QUERY_RESULT_WAIT_BIT);
}
void gpu_clear_buffer(gpu_stream* stream, gpu_buffer* buffer, uint32_t offset, uint32_t size) {
vkCmdFillBuffer(stream->commands, buffer->handle, buffer->offset + offset, size, 0);
}
@ -1583,6 +1628,10 @@ void gpu_clear_texture(gpu_stream* stream, gpu_texture* texture, float value[4],
}
}
void gpu_clear_tally(gpu_stream* stream, gpu_tally* tally, uint32_t index, uint32_t count) {
vkCmdResetQueryPool(stream->commands, tally->handle, index, count);
}
void gpu_blit(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t srcExtent[3], uint32_t dstExtent[3], gpu_filter filter) {
VkImageBlit region = {
.srcSubresource = {
@ -1629,6 +1678,18 @@ void gpu_sync(gpu_stream* stream, gpu_barrier* barriers, uint32_t count) {
}
}
void gpu_tally_begin(gpu_stream* stream, gpu_tally* tally, uint32_t index) {
vkCmdBeginQuery(stream->commands, tally->handle, index, 0);
}
void gpu_tally_end(gpu_stream* stream, gpu_tally* tally, uint32_t index) {
vkCmdEndQuery(stream->commands, tally->handle, index);
}
void gpu_tally_mark(gpu_stream* stream, gpu_tally* tally, uint32_t index) {
vkCmdWriteTimestamp(stream->commands, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, tally->handle, index);
}
// Entry
bool gpu_init(gpu_config* config) {
@ -1765,6 +1826,7 @@ bool gpu_init(gpu_config* config) {
config->limits->pushConstantSize = limits->maxPushConstantsSize;
config->limits->indirectDrawCount = limits->maxDrawIndirectCount;
config->limits->instances = multiviewProperties.maxMultiviewInstanceIndex;
config->limits->timestampPeriod = limits->timestampPeriod;
config->limits->anisotropy = limits->maxSamplerAnisotropy;
config->limits->pointSize = limits->pointSizeRange[1];
}
@ -2352,6 +2414,7 @@ static void expunge() {
case VK_OBJECT_TYPE_DESCRIPTOR_POOL: vkDestroyDescriptorPool(state.device, victim->handle, NULL); break;
case VK_OBJECT_TYPE_PIPELINE_LAYOUT: vkDestroyPipelineLayout(state.device, victim->handle, NULL); break;
case VK_OBJECT_TYPE_PIPELINE: vkDestroyPipeline(state.device, victim->handle, NULL); break;
case VK_OBJECT_TYPE_QUERY_POOL: vkDestroyQueryPool(state.device, victim->handle, NULL); break;
case VK_OBJECT_TYPE_RENDER_PASS: vkDestroyRenderPass(state.device, victim->handle, NULL); break;
case VK_OBJECT_TYPE_FRAMEBUFFER: vkDestroyFramebuffer(state.device, victim->handle, NULL); break;
case VK_OBJECT_TYPE_DEVICE_MEMORY: vkFreeMemory(state.device, victim->handle, NULL); break;

View File

@ -150,6 +150,15 @@ struct Font {
uint32_t atlasY;
};
struct Tally {
uint32_t ref;
uint32_t tick;
TallyInfo info;
gpu_tally* gpu;
gpu_buffer* buffer;
uint64_t* masks;
};
typedef struct {
float view[16];
float projection[16];
@ -300,6 +309,7 @@ static struct {
Buffer* defaultBuffer;
Texture* defaultTexture;
Sampler* defaultSamplers[2];
Shader* timeWizard;
Shader* defaultShaders[DEFAULT_SHADER_COUNT];
gpu_vertex_format vertexFormats[VERTEX_FORMAT_COUNT];
Material* defaultMaterial;
@ -521,6 +531,7 @@ void lovrGraphicsDestroy() {
lovrRelease(state.defaultTexture, lovrTextureDestroy);
lovrRelease(state.defaultSamplers[0], lovrSamplerDestroy);
lovrRelease(state.defaultSamplers[1], lovrSamplerDestroy);
lovrRelease(state.timeWizard, lovrShaderDestroy);
for (uint32_t i = 0; i < COUNTOF(state.defaultShaders); i++) {
lovrRelease(state.defaultShaders[i], lovrShaderDestroy);
}
@ -646,10 +657,12 @@ void lovrGraphicsSetBackground(float background[4]) {
Font* lovrGraphicsGetDefaultFont() {
if (!state.defaultFont) {
Rasterizer* rasterizer = lovrRasterizerCreate(NULL, 32);
state.defaultFont = lovrFontCreate(&(FontInfo) {
.rasterizer = lovrRasterizerCreate(NULL, 32),
.rasterizer = rasterizer,
.spread = 4.
});
lovrRelease(rasterizer, lovrRasterizerDestroy);
}
return state.defaultFont;
@ -1624,6 +1637,11 @@ void lovrShaderDestroy(void* ref) {
Shader* shader = ref;
gpu_shader_destroy(shader->gpu);
lovrRelease(shader->parent, lovrShaderDestroy);
free(shader->constants);
free(shader->resources);
free(shader->attributes);
free(shader->flags);
free(shader->flagLookup);
free(shader);
}
@ -1937,10 +1955,12 @@ static Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint, bool* resized) {
// Recompute all glyph uvs after atlas resize
for (size_t i = 0; i < font->glyphs.length; i++) {
Glyph* g = &font->glyphs.data[i];
g->uv[0] = (uint16_t) ((float) g->x / font->atlasWidth * 65535.f + .5f);
g->uv[1] = (uint16_t) ((float) g->y / font->atlasHeight * 65535.f + .5f);
g->uv[2] = (uint16_t) ((float) (g->x + g->box[2] - g->box[0]) / font->atlasWidth * 65535.f + .5f);
g->uv[3] = (uint16_t) ((float) (g->y + g->box[3] - g->box[1]) / font->atlasHeight * 65535.f + .5f);
if (g->box[2] - g->box[0] > 0.f) {
g->uv[0] = (uint16_t) ((float) g->x / font->atlasWidth * 65535.f + .5f);
g->uv[1] = (uint16_t) ((float) g->y / font->atlasHeight * 65535.f + .5f);
g->uv[2] = (uint16_t) ((float) (g->x + g->box[2] - g->box[0]) / font->atlasWidth * 65535.f + .5f);
g->uv[3] = (uint16_t) ((float) (g->y + g->box[3] - g->box[1]) / font->atlasHeight * 65535.f + .5f);
}
}
if (resized) *resized = true;
@ -1983,7 +2003,48 @@ float lovrFontGetKerning(Font* font, uint32_t left, uint32_t right) {
return kerning.f32;
}
void lovrFontGetWrap(Font* font, ColoredString* strings, uint32_t count, float wrap, void (*callback)(void* context, const char* string, size_t length), void* context) {
float lovrFontGetWidth(Font* font, ColoredString* strings, uint32_t count) {
float x = 0.f;
float maxWidth = 0.f;
float space = lovrFontGetGlyph(font, ' ', NULL)->advance;
for (uint32_t i = 0; i < count; i++) {
size_t bytes;
uint32_t codepoint;
uint32_t previous = '\0';
const char* str = strings[i].string;
const char* end = strings[i].string + strings[i].length;
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
if (codepoint == ' ' || codepoint == '\t') {
x += codepoint == '\t' ? space * 4.f : space;
previous = '\0';
str += bytes;
continue;
} else if (codepoint == '\n') {
maxWidth = MAX(maxWidth, x);
x = 0.f;
previous = '\0';
str += bytes;
continue;
} else if (codepoint == '\r') {
str += bytes;
continue;
}
Glyph* glyph = lovrFontGetGlyph(font, codepoint, NULL);
if (previous) x += lovrFontGetKerning(font, previous, codepoint);
previous = codepoint;
x += glyph->advance;
str += bytes;
}
}
return MAX(maxWidth, x) / font->pixelDensity;
}
void lovrFontGetLines(Font* font, ColoredString* strings, uint32_t count, float wrap, void (*callback)(void* context, const char* string, size_t length), void* context) {
size_t totalLength = 0;
for (uint32_t i = 0; i < count; i++) {
totalLength += strings[i].length;
@ -1993,7 +2054,7 @@ void lovrFontGetWrap(Font* font, ColoredString* strings, uint32_t count, float w
char* string = tempAlloc(totalLength + 1);
string[totalLength] = '\0';
for (uint32_t i = 0, cursor = 0; i < count; i++, cursor += strings[i].length) {
for (uint32_t i = 0, cursor = 0; i < count; cursor += strings[i].length, i++) {
memcpy(string + cursor, strings[i].string, strings[i].length);
}
@ -2007,10 +2068,10 @@ void lovrFontGetWrap(Font* font, ColoredString* strings, uint32_t count, float w
const char* lineStart = string;
const char* wordStart = string;
const char* end = string + totalLength;
Glyph* space = lovrFontGetGlyph(font, ' ', NULL);
float space = lovrFontGetGlyph(font, ' ', NULL)->advance;
while ((bytes = utf8_decode(string, end, &codepoint)) > 0) {
if (codepoint == ' ' || codepoint == '\t') {
x += codepoint == '\t' ? space->advance * 4.f : space->advance;
x += codepoint == '\t' ? space * 4.f : space;
nextWordStartX = x;
previous = '\0';
string += bytes;
@ -2038,15 +2099,8 @@ void lovrFontGetWrap(Font* font, ColoredString* strings, uint32_t count, float w
if (previous) x += lovrFontGetKerning(font, previous, codepoint);
previous = codepoint;
// Ignore bearing for the first character on a line (questionable)
if (string == lineStart) {
x -= glyph->box[0];
} else if (string == wordStart) {
nextWordStartX += glyph->box[0]; // So wrapping accounts for bearing of first glyph
}
// Wrap
if (wordStart != lineStart && x + glyph->box[2] > wrap) {
if (wordStart != lineStart && x + glyph->advance > wrap) {
size_t length = wordStart - lineStart;
while (string[length] == ' ' || string[length] == '\t') length--;
callback(context, lineStart, length);
@ -2068,6 +2122,48 @@ void lovrFontGetWrap(Font* font, ColoredString* strings, uint32_t count, float w
tempPop(stack);
}
// Tally
Tally* lovrTallyCreate(TallyInfo* info) {
lovrCheck(info->count > 0, "Tally count must be greater than zero");
lovrCheck(info->count <= 4096, "Maximum Tally count is 4096");
lovrCheck(info->views <= state.limits.renderSize[2], "Tally view count can not exceed the maximum view count");
Tally* tally = calloc(1, sizeof(Tally) + gpu_sizeof_tally());
lovrAssert(tally, "Out of memory");
tally->ref = 1;
tally->tick = state.tick;
tally->info = *info;
tally->gpu = (gpu_tally*) (tally + 1);
tally->masks = calloc((info->count + 63) / 64, sizeof(uint64_t));
lovrAssert(tally->masks, "Out of memory");
uint32_t total = info->count * (info->type == TALLY_TIMER ? 2 * info->views : 1);
gpu_tally_init(tally->gpu, &(gpu_tally_info) {
.type = (gpu_tally_type) info->type,
.count = total
});
if (info->type == TALLY_TIMER) {
tally->buffer = calloc(1, gpu_sizeof_buffer());
lovrAssert(tally->buffer, "Out of memory");
gpu_buffer_init(tally->buffer, &(gpu_buffer_info) {
.size = info->count * 2 * info->views * sizeof(uint32_t)
});
}
return tally;
}
void lovrTallyDestroy(void* ref) {
Tally* tally = ref;
gpu_tally_destroy(tally->gpu);
if (tally->buffer) gpu_buffer_destroy(tally->buffer);
free(tally->buffer);
free(tally->masks);
free(tally);
}
// Pass
Pass* lovrGraphicsGetPass(PassInfo* info) {
@ -3438,9 +3534,9 @@ void lovrPassTorus(Pass* pass, float* transform, uint32_t segmentsT, uint32_t se
}
}
static void aline(GlyphVertex* vertices, uint32_t head, uint32_t tail, HorizontalAlign align) {
static void aline(GlyphVertex* vertices, uint32_t head, uint32_t tail, float width, HorizontalAlign align) {
if (align == ALIGN_LEFT) return;
float shift = align / 2.f * vertices[tail - 1].position.x;
float shift = align / 2.f * width;
for (uint32_t i = head; i < tail; i++) {
vertices[i].position.x -= shift;
}
@ -3448,7 +3544,7 @@ static void aline(GlyphVertex* vertices, uint32_t head, uint32_t tail, Horizonta
void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign) {
font = font ? font : lovrGraphicsGetDefaultFont();
Glyph* space = lovrFontGetGlyph(font, ' ', NULL);
float space = lovrFontGetGlyph(font, ' ', NULL)->advance;
size_t totalLength = 0;
for (uint32_t i = 0; i < count; i++) {
@ -3465,6 +3561,8 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
float x = 0.f;
float y = 0.f;
float wordStartX = 0.f;
float prevWordEndX = 0.f;
float leading = lovrRasterizerGetLeading(font->info.rasterizer) * font->lineSpacing;
float ascent = lovrRasterizerGetAscent(font->info.rasterizer);
float scale = 1.f / font->pixelDensity;
@ -3483,17 +3581,21 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
if (codepoint == ' ' || codepoint == '\t') {
if (previous) prevWordEndX = x;
wordStart = vertexCount;
x += codepoint == '\t' ? space->advance * 4.f : space->advance;
x += codepoint == '\t' ? space * 4.f : space;
wordStartX = x;
previous = '\0';
str += bytes;
continue;
} else if (codepoint == '\n') {
aline(vertices, lineStart, vertexCount, halign);
aline(vertices, lineStart, vertexCount, x, halign);
lineStart = vertexCount;
wordStart = vertexCount;
x = 0.f;
y -= leading;
wordStartX = 0.f;
prevWordEndX = 0.f;
lineCount++;
previous = '\0';
str += bytes;
@ -3517,8 +3619,8 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
previous = codepoint;
// Wrap
if (wrap > 0.f && x + glyph->box[2] > wrap && wordStart != lineStart) {
float dx = wordStart == vertexCount ? x : vertices[wordStart].position.x;
if (wrap > 0.f && x + glyph->advance > wrap && wordStart != lineStart) {
float dx = wordStartX;
float dy = leading;
// Shift the vertices of the overflowing word down a line and back to the beginning
@ -3527,18 +3629,14 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
vertices[v].position.y -= dy;
}
aline(vertices, lineStart, wordStart, halign);
aline(vertices, lineStart, wordStart, prevWordEndX, halign);
lineStart = wordStart;
wordStartX = 0.f;
lineCount++;
x -= dx;
y -= dy;
}
// Ignore bearing for the first character on a line (questionable)
if (vertexCount == lineStart) {
x -= glyph->box[0];
}
// Vertices
float* bb = glyph->box;
uint16_t* uv = glyph->uv;
@ -3555,7 +3653,7 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
}
// Align last line
aline(vertices, lineStart, vertexCount, halign);
aline(vertices, lineStart, vertexCount, x, halign);
mat4_scale(transform, scale, scale, scale);
mat4_translate(transform, 0.f, -ascent + valign / 2.f * (leading * lineCount), 0.f);
@ -3744,6 +3842,74 @@ void lovrPassCopyBufferToBuffer(Pass* pass, Buffer* src, Buffer* dst, uint32_t s
trackBuffer(pass, dst, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_WRITE);
}
void lovrPassCopyTallyToBuffer(Pass* pass, Tally* tally, Buffer* buffer, uint32_t srcIndex, uint32_t dstOffset, uint32_t count) {
if (count == ~0u) count = tally->info.count;
lovrCheck(pass->info.type == PASS_TRANSFER, "This function can only be called on a transfer pass");
lovrCheck(!lovrBufferIsTemporary(buffer), "Temporary buffers can not be copied to");
lovrCheck(srcIndex + count <= tally->info.count, "Tally copy range exceeds the number of slots in the Tally");
lovrCheck(dstOffset + count * 4 <= buffer->size, "Buffer copy range goes past the end of the destination Buffer");
lovrCheck(dstOffset % 4 == 0, "Buffer copy offset must be a multiple of 4");
for (uint32_t i = 0; i < count; i++) {
uint32_t index = srcIndex + i;
lovrCheck(tally->masks[index / 64] & (1 << (index % 64)), "Trying to copy Tally slot %d, but it hasn't been marked yet", index + 1);
}
if (tally->info.type == TALLY_TIMER) {
gpu_copy_tally_buffer(pass->stream, tally->gpu, tally->buffer, srcIndex, 0, count, 4);
// Wait for transfer to finish, then dispatch a compute shader to fixup timestamps
gpu_sync(pass->stream, &(gpu_barrier) {
.prev = GPU_PHASE_TRANSFER,
.next = GPU_PHASE_SHADER_COMPUTE,
.flush = GPU_CACHE_TRANSFER_WRITE,
.clear = GPU_CACHE_STORAGE_READ
}, 1);
if (!state.timeWizard) {
Blob* source = lovrBlobCreate((void*) lovr_shader_timewizard_comp, sizeof(lovr_shader_timewizard_comp), NULL);
state.timeWizard = lovrShaderCreate(&(ShaderInfo) {
.type = SHADER_COMPUTE,
.stages[0] = source,
.label = "Chronophage"
});
lovrRelease(source, lovrBlobDestroy);
}
gpu_pipeline* pipeline = state.pipelines.data[state.timeWizard->computePipeline];
gpu_layout* layout = state.layouts.data[state.timeWizard->layout].gpu;
gpu_shader* shader = state.timeWizard->gpu;
gpu_binding bindings[] = {
[0].buffer = { tally->buffer, 0, ~0u },
[1].buffer = { buffer->gpu, dstOffset, count * sizeof(uint32_t) },
};
gpu_bundle* bundle = getBundle(state.timeWizard->layout);
gpu_bundle_info bundleInfo = { layout, bindings, COUNTOF(bindings) };
gpu_bundle_write(&bundle, &bundleInfo, 1);
struct { uint32_t first, count, views; float period; } constants = {
.first = srcIndex,
.count = count,
.views = tally->info.views,
.period = state.limits.timestampPeriod
};
gpu_compute_begin(pass->stream);
gpu_bind_pipeline(pass->stream, pipeline, true);
gpu_bind_bundle(pass->stream, shader, 0, bundle, NULL, 0);
gpu_push_constants(pass->stream, shader, &constants, sizeof(constants));
gpu_compute(pass->stream, (count + 31) / 32, 0, 0);
gpu_compute_end(pass->stream);
trackBuffer(pass, buffer, GPU_PHASE_SHADER_COMPUTE, GPU_CACHE_STORAGE_WRITE);
} else {
gpu_copy_tally_buffer(pass->stream, tally->gpu, buffer->gpu, srcIndex, dstOffset, count, 4);
trackBuffer(pass, buffer, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_WRITE);
}
}
void lovrPassCopyImageToTexture(Pass* pass, Image* image, Texture* texture, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[4]) {
if (extent[0] == ~0u) extent[0] = MIN(texture->info.width - dstOffset[0], lovrImageGetWidth(image, srcOffset[3]) - srcOffset[0]);
if (extent[1] == ~0u) extent[1] = MIN(texture->info.height - dstOffset[1], lovrImageGetHeight(image, srcOffset[3]) - srcOffset[1]);
@ -3830,6 +3996,34 @@ void lovrPassMipmap(Pass* pass, Texture* texture, uint32_t base, uint32_t count)
trackTexture(pass, texture, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_READ | GPU_CACHE_TRANSFER_WRITE);
}
void lovrPassTick(Pass* pass, Tally* tally, uint32_t index) {
lovrCheck(index < tally->info.count, "Trying to use tally slot #%d, but the tally only has %d slots", index + 1, tally->info.count);
lovrCheck(~tally->masks[index / 64] & (1 << (index % 64)), "Tally slot #%d has already been used", index + 1);
if (tally->tick != state.tick) {
gpu_clear_tally(state.stream, tally->gpu, 0, tally->info.count * 2 * tally->info.views);
memset(tally->masks, 0, (tally->info.count + 63) / 64 * sizeof(uint64_t));
tally->tick = state.tick;
}
if (tally->info.type == TALLY_TIMER) {
gpu_tally_mark(pass->stream, tally->gpu, index * 2 * tally->info.views);
} else {
gpu_tally_begin(pass->stream, tally->gpu, index);
}
}
void lovrPassTock(Pass* pass, Tally* tally, uint32_t index) {
lovrCheck(index < tally->info.count, "Trying to use tally slot #%d, but the tally only has %d slots", index + 1, tally->info.count);
lovrCheck(tally->masks[index / 64] & (1 << (index % 64)), "Tally slot #%d has not been started yet", index + 1);
if (tally->info.type == TALLY_TIMER) {
gpu_tally_mark(pass->stream, tally->gpu, index * 2 * tally->info.views + tally->info.views);
} else {
gpu_tally_end(pass->stream, tally->gpu, index);
}
}
// Helpers
static void* tempAlloc(size_t size) {

View File

@ -14,6 +14,7 @@ typedef struct Sampler Sampler;
typedef struct Shader Shader;
typedef struct Material Material;
typedef struct Font Font;
typedef struct Tally Tally;
typedef struct Pass Pass;
typedef struct {
@ -354,7 +355,26 @@ void lovrFontSetPixelDensity(Font* font, float pixelDensity);
float lovrFontGetLineSpacing(Font* font);
void lovrFontSetLineSpacing(Font* font, float spacing);
float lovrFontGetKerning(Font* font, uint32_t left, uint32_t right);
void lovrFontGetWrap(Font* font, ColoredString* strings, uint32_t count, float wrap, void (*callback)(void* context, const char* string, size_t length), void* context);
float lovrFontGetWidth(Font* font, ColoredString* strings, uint32_t count);
void lovrFontGetLines(Font* font, ColoredString* strings, uint32_t count, float wrap, void (*callback)(void* context, const char* string, size_t length), void* context);
// Tally
typedef enum {
TALLY_TIMER,
TALLY_PIXEL,
TALLY_PIPELINE
} TallyType;
typedef struct {
TallyType type;
uint32_t count;
uint32_t views;
} TallyInfo;
Tally* lovrTallyCreate(TallyInfo* info);
void lovrTallyDestroy(void* ref);
const TallyInfo* lovrTallyGetInfo(Tally* tally);
// Pass
@ -501,7 +521,10 @@ void lovrPassClearBuffer(Pass* pass, Buffer* buffer, uint32_t offset, uint32_t e
void lovrPassClearTexture(Pass* pass, Texture* texture, float value[4], uint32_t layer, uint32_t layerCount, uint32_t level, uint32_t levelCount);
void* lovrPassCopyDataToBuffer(Pass* pass, Buffer* buffer, uint32_t offset, uint32_t extent);
void lovrPassCopyBufferToBuffer(Pass* pass, Buffer* src, Buffer* dst, uint32_t srcOffset, uint32_t dstOffset, uint32_t extent);
void lovrPassCopyTallyToBuffer(Pass* pass, Tally* src, Buffer* dst, uint32_t srcIndex, uint32_t dstOffset, uint32_t count);
void lovrPassCopyImageToTexture(Pass* pass, struct Image* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[3]);
void lovrPassCopyTextureToTexture(Pass* pass, Texture* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[3]);
void lovrPassBlit(Pass* pass, Texture* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t srcExtent[3], uint32_t dstExtent[3], FilterMode filter);
void lovrPassMipmap(Pass* pass, Texture* texture, uint32_t base, uint32_t count);
void lovrPassTick(Pass* pass, Tally* tally, uint32_t index);
void lovrPassTock(Pass* pass, Tally* tally, uint32_t index);