mirror of https://github.com/bjornbytes/lovr.git
Compare commits
9 Commits
1f8d37a160
...
d9d54ce348
Author | SHA1 | Date |
---|---|---|
bjorn | d9d54ce348 | |
bjorn | 8b37b25e54 | |
bjorn | a521f11a44 | |
bjorn | d06e0c8b09 | |
bjorn | 0417e9095d | |
bjorn | c327eb103f | |
bjorn | 9e7bd34ab1 | |
bjorn | 4125b1dc7e | |
bjorn | 8e968cecd0 |
|
@ -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"
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 }
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, ®ion);
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue