From ea3a77a73a679e5b590c455b789ca07e60a6d36d Mon Sep 17 00:00:00 2001 From: bjorn Date: Thu, 27 Dec 2018 13:50:42 -0800 Subject: [PATCH] Many batching improvements; Refactoring; --- CMakeLists.txt | 1 + src/api/graphics.c | 138 +++-- src/api/types/font.c | 9 +- src/api/types/mesh.c | 99 +++- src/api/types/model.c | 2 +- src/api/types/shader.c | 3 +- src/graphics/buffer.c | 4 + src/graphics/buffer.h | 16 +- src/graphics/canvas.c | 3 + src/graphics/font.c | 56 +- src/graphics/font.h | 12 +- src/graphics/graphics.c | 1220 ++++++++++++++++++++++++++------------- src/graphics/graphics.h | 181 ++++-- src/graphics/material.c | 15 +- src/graphics/material.h | 2 - src/graphics/mesh.c | 123 ++-- src/graphics/mesh.h | 37 +- src/graphics/model.c | 44 +- src/graphics/opengl.c | 465 +++++++-------- src/graphics/opengl.h | 10 +- src/graphics/shader.c | 83 ++- src/graphics/shader.h | 13 +- src/resources/shaders.c | 10 +- 23 files changed, 1542 insertions(+), 1004 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba5470d6..79c3f8a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -423,6 +423,7 @@ if(LOVR_ENABLE_GRAPHICS) add_definitions(-DLOVR_ENABLE_GRAPHICS) target_sources(lovr PRIVATE src/graphics/animator.c + src/graphics/buffer.c src/graphics/canvas.c src/graphics/font.c src/graphics/graphics.c diff --git a/src/api/graphics.c b/src/api/graphics.c index 14c3a119..0f6d0fc1 100644 --- a/src/api/graphics.c +++ b/src/api/graphics.c @@ -46,7 +46,6 @@ const char* BlendModes[] = { [BLEND_LIGHTEN] = "lighten", [BLEND_DARKEN] = "darken", [BLEND_SCREEN] = "screen", - [BLEND_REPLACE] = "replace", NULL }; @@ -95,8 +94,8 @@ const char* FilterModes[] = { const char* HorizontalAligns[] = { [ALIGN_LEFT] = "left", - [ALIGN_RIGHT] = "right", [ALIGN_CENTER] = "center", + [ALIGN_RIGHT] = "right", NULL }; @@ -177,8 +176,8 @@ const char* UniformAccesses[] = { const char* VerticalAligns[] = { [ALIGN_TOP] = "top", - [ALIGN_BOTTOM] = "bottom", [ALIGN_MIDDLE] = "middle", + [ALIGN_BOTTOM] = "bottom", NULL }; @@ -195,60 +194,61 @@ const char* WrapModes[] = { NULL }; -static uint32_t luax_readvertices(lua_State* L, int index) { - switch (lua_type(L, index)) { - case LUA_TTABLE: { - size_t count = lua_objlen(L, index); - lua_rawgeti(L, index, 1); +static uint32_t luax_getvertexcount(lua_State* L, int index) { + int type = lua_type(L, index); + if (type == LUA_TTABLE) { + size_t count = lua_objlen(L, index); + lua_rawgeti(L, index, 1); + int tableType = lua_type(L, -1); + lua_pop(L, 1); + return tableType == LUA_TNUMBER ? count / 3 : count; + } else if (type == LUA_TNUMBER) { + return (lua_gettop(L) - index + 1) / 3; + } else { + return lua_gettop(L) - index + 1; + } +} +static void luax_readvertices(lua_State* L, int index, float* vertices, uint32_t count) { + switch (lua_type(L, index)) { + case LUA_TTABLE: + lua_rawgeti(L, index, 1); if (lua_type(L, -1) == LUA_TNUMBER) { lua_pop(L, 1); - float* vertices = lovrGraphicsGetVertexPointer(count / 3); - for (size_t i = 1; i <= count; i += 3) { - for (int j = 0; j < 3; j++) { - lua_rawgeti(L, index, i + j); + for (uint32_t i = 0; i < count; i++) { + for (int j = 1; j <= 3; j++) { + lua_rawgeti(L, index, 3 * i + j); vertices[j] = lua_tonumber(L, -1); lua_pop(L, 1); } vertices += 8; } - return count / 3; } else { lua_pop(L, 1); - float* vertices = lovrGraphicsGetVertexPointer(count); - for (size_t i = 1; i <= count; i++) { - lua_rawgeti(L, index, i); + for (uint32_t i = 0; i < count; i++) { + lua_rawgeti(L, index, i + 1); vec3_init(vertices, luax_checkmathtype(L, -1, MATH_VEC3, NULL)); lua_pop(L, 1); vertices += 8; } - return count; } - } + break; - case LUA_TNUMBER: { - int top = lua_gettop(L); - uint32_t count = (top - index + 1) / 3; - float* vertices = lovrGraphicsGetVertexPointer(count); - for (int i = index; i <= top; i += 3) { + case LUA_TNUMBER: + for (uint32_t i = 0; i < count; i++) { for (int j = 0; j < 3; j++) { - vertices[j] = lua_tonumber(L, i + j); + vertices[j] = lua_tonumber(L, index + 3 * i + j); } vertices += 8; } - return count; - } + break; - default: { - int top = lua_gettop(L); - uint32_t count = top - index + 1; - float* vertices = lovrGraphicsGetVertexPointer(count); - for (int i = index; i <= top; i++) { - vec3_init(vertices, luax_checkmathtype(L, i, MATH_VEC3, NULL)); + default: + for (uint32_t i = 0; i < count; i++) { + vec3_init(vertices, luax_checkmathtype(L, index + i, MATH_VEC3, NULL)); vertices += 8; } - return count; - } + break; } } @@ -426,7 +426,7 @@ static int l_lovrGraphicsGetBlendMode(lua_State* L) { } static int l_lovrGraphicsSetBlendMode(lua_State* L) { - BlendMode mode = luaL_checkoption(L, 1, NULL, BlendModes); + BlendMode mode = lua_isnoneornil(L, 1) ? BLEND_NONE : luaL_checkoption(L, 1, NULL, BlendModes); BlendAlphaMode alphaMode = luaL_checkoption(L, 2, "alphamultiply", BlendAlphaModes); lovrGraphicsSetBlendMode(mode, alphaMode); return 0; @@ -711,14 +711,18 @@ static int l_lovrGraphicsFlush(lua_State* L) { } static int l_lovrGraphicsPoints(lua_State* L) { - uint32_t count = luax_readvertices(L, 1); - lovrGraphicsPoints(count); + float* vertices; + uint32_t count = luax_getvertexcount(L, 1); + lovrGraphicsPoints(count, &vertices); + luax_readvertices(L, 1, vertices, count); return 0; } static int l_lovrGraphicsLine(lua_State* L) { - uint32_t count = luax_readvertices(L, 1); - lovrGraphicsLine(count); + float* vertices; + uint32_t count = luax_getvertexcount(L, 1); + lovrGraphicsLine(count, &vertices); + luax_readvertices(L, 1, vertices, count); return 0; } @@ -731,13 +735,11 @@ static int l_lovrGraphicsTriangle(lua_State* L) { style = luaL_checkoption(L, 1, NULL, DrawStyles); } - float points[9]; - int top = lua_gettop(L); - lovrAssert(top >= 10, "Expected 3 points to make a triangle, got %d\n", (top - 1) / 3); - for (int i = 0; i < 9; i++) { - points[i] = luaL_checknumber(L, i + 2); - } - lovrGraphicsTriangle(style, material, points); + float* vertices; + uint32_t count = luax_getvertexcount(L, 2); + lovrAssert(count % 3 == 0, "Triangle vertex count must be a multiple of 3"); + lovrGraphicsTriangle(style, material, count, &vertices); + luax_readvertices(L, 2, vertices, count); return 0; } @@ -785,17 +787,17 @@ static int l_lovrGraphicsArc(lua_State* L) { } else { style = luaL_checkoption(L, 1, NULL, DrawStyles); } - ArcMode arcMode = ARC_MODE_PIE; + ArcMode mode = ARC_MODE_PIE; int index = 2; if (lua_type(L, index) == LUA_TSTRING) { - arcMode = luaL_checkoption(L, index++, NULL, ArcModes); + mode = luaL_checkoption(L, index++, NULL, ArcModes); } float transform[16]; index = luax_readmat4(L, index, transform, 1, NULL); - float theta1 = luaL_optnumber(L, index++, 0); - float theta2 = luaL_optnumber(L, index++, 2 * M_PI); - int segments = luaL_optinteger(L, index, 64) * (MIN(fabsf(theta2 - theta1), 2 * M_PI) / (2 * M_PI)); - lovrGraphicsArc(style, arcMode, material, transform, theta1, theta2, segments); + float r1 = luaL_optnumber(L, index++, 0); + float r2 = luaL_optnumber(L, index++, 2 * M_PI); + int segments = luaL_optinteger(L, index, 64) * (MIN(fabsf(r2 - r1), 2 * M_PI) / (2 * M_PI)); + lovrGraphicsArc(style, mode, material, transform, r1, r2, segments); return 0; } @@ -953,9 +955,14 @@ static int l_lovrGraphicsNewShaderBlock(lua_State* L) { lua_pop(L, 1); } - ShaderBlock* block = lovrShaderBlockCreate(&uniforms, type, usage); + lovrAssert(type != BLOCK_STORAGE || lovrGraphicsGetSupported()->computeShaders, "Writable ShaderBlocks are not supported on this system"); + size_t size = lovrShaderComputeUniformLayout(&uniforms); + Buffer* buffer = lovrBufferCreate(size, NULL, type == BLOCK_STORAGE ? BUFFER_SHADER_STORAGE : BUFFER_UNIFORM, usage, false); + ShaderBlock* block = lovrShaderBlockCreate(type, buffer, &uniforms); luax_pushobject(L, block); vec_deinit(&uniforms); + lovrRelease(buffer); + lovrRelease(block); return 1; } @@ -1156,17 +1163,9 @@ static int l_lovrGraphicsNewMesh(lua_State* L) { DrawMode mode = luaL_checkoption(L, drawModeIndex, "fan", DrawModes); BufferUsage usage = luaL_checkoption(L, drawModeIndex + 1, "dynamic", BufferUsages); bool readable = lua_toboolean(L, drawModeIndex + 2); - Mesh* mesh = lovrMeshCreate(count, format, mode, usage, readable); - - if (dataIndex) { - VertexPointer vertices = { .raw = lovrMeshMapVertices(mesh, 0) }; - luax_loadvertices(L, dataIndex, lovrMeshGetVertexFormat(mesh), vertices); - lovrMeshFlushVertices(mesh, 0, count * format.stride); - } else if (vertexData) { - void* vertices = lovrMeshMapVertices(mesh, 0); - memcpy(vertices, vertexData->blob.data, vertexData->count * vertexData->format.stride); - lovrMeshFlushVertices(mesh, 0, count * format.stride); - } + size_t bufferSize = count * format.stride; + Buffer* vertexBuffer = lovrBufferCreate(bufferSize, NULL, BUFFER_VERTEX, usage, readable); + Mesh* mesh = lovrMeshCreate(mode, format, vertexBuffer); lovrMeshAttachAttribute(mesh, "lovrDrawID", &(MeshAttribute) { .buffer = lovrGraphicsGetIdentityBuffer(), @@ -1177,6 +1176,17 @@ static int l_lovrGraphicsNewMesh(lua_State* L) { .enabled = true }); + if (dataIndex) { + VertexPointer vertices = { .raw = lovrBufferMap(vertexBuffer, 0) }; + luax_loadvertices(L, dataIndex, &format, vertices); + } else if (vertexData) { + void* vertices = lovrBufferMap(vertexBuffer, 0); + memcpy(vertices, vertexData->blob.data, vertexData->count * vertexData->format.stride); + } + + lovrBufferFlush(vertexBuffer, 0, count * format.stride); + lovrRelease(vertexBuffer); + luax_pushobject(L, mesh); lovrRelease(mesh); return 1; diff --git a/src/api/types/font.c b/src/api/types/font.c index 7d9997d3..178549ea 100644 --- a/src/api/types/font.c +++ b/src/api/types/font.c @@ -3,9 +3,14 @@ int l_lovrFontGetWidth(lua_State* L) { Font* font = luax_checktype(L, 1, Font); - const char* string = luaL_checkstring(L, 2); + size_t length; + const char* string = luaL_checklstring(L, 2, &length); float wrap = luaL_optnumber(L, 3, 0); - lua_pushnumber(L, lovrFontGetWidth(font, string, wrap)); + float width; + uint32_t lineCount; + uint32_t glyphCount; + lovrFontMeasure(font, string, length, wrap, &width, &lineCount, &glyphCount); + lua_pushnumber(L, width); return 1; } diff --git a/src/api/types/mesh.c b/src/api/types/mesh.c index e5c1b80c..936f60cf 100644 --- a/src/api/types/mesh.c +++ b/src/api/types/mesh.c @@ -76,11 +76,22 @@ int l_lovrMeshDraw(lua_State* L) { float transform[16]; int index = luax_readmat4(L, 2, transform, 1, NULL); int instances = luaL_optinteger(L, index, 1); - lovrGraphicsDraw(&(DrawRequest) { + uint32_t vertexCount = lovrMeshGetVertexCount(mesh); + uint32_t indexCount = lovrMeshGetIndexCount(mesh); + uint32_t defaultCount = indexCount > 0 ? indexCount : vertexCount; + uint32_t rangeStart, rangeCount; + lovrMeshGetDrawRange(mesh, &rangeStart, &rangeCount); + lovrGraphicsBatch(&(BatchRequest) { + .type = BATCH_MESH, + .params.mesh = { + .object = mesh, + .mode = lovrMeshGetDrawMode(mesh), + .rangeStart = rangeStart, + .rangeCount = rangeCount ? rangeCount : defaultCount, + .instances = instances + }, .transform = transform, - .mesh = mesh, - .material = lovrMeshGetMaterial(mesh), - .instances = instances + .material = lovrMeshGetMaterial(mesh) }); return 0; } @@ -113,47 +124,51 @@ int l_lovrMeshGetVertexCount(lua_State* L) { int l_lovrMeshGetVertex(lua_State* L) { Mesh* mesh = luax_checktype(L, 1, Mesh); int index = luaL_checkint(L, 2) - 1; - lovrAssert(lovrMeshIsReadable(mesh), "Mesh:getVertex can only be used if the Mesh was created with the readable flag"); + Buffer* buffer = lovrMeshGetVertexBuffer(mesh); + lovrAssert(lovrBufferIsReadable(buffer), "Mesh:getVertex can only be used if the Mesh was created with the readable flag"); VertexFormat* format = lovrMeshGetVertexFormat(mesh); - VertexPointer vertex = { .raw = lovrMeshMapVertices(mesh, index * format->stride) }; + VertexPointer vertex = { .raw = lovrBufferMap(buffer, index * format->stride) }; return luax_pushvertex(L, &vertex, format); } int l_lovrMeshSetVertex(lua_State* L) { Mesh* mesh = luax_checktype(L, 1, Mesh); - int index = luaL_checkint(L, 2) - 1; + uint32_t index = luaL_checkinteger(L, 2) - 1; lovrAssert(index >= 0 && index < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex index: %d", index + 1); + Buffer* buffer = lovrMeshGetVertexBuffer(mesh); VertexFormat* format = lovrMeshGetVertexFormat(mesh); - VertexPointer vertex = { .raw = lovrMeshMapVertices(mesh, index * format->stride) }; + VertexPointer vertex = { .raw = lovrBufferMap(buffer, index * format->stride) }; luax_setvertex(L, 3, &vertex, format); - lovrMeshFlushVertices(mesh, index * format->stride, format->stride); + lovrMeshMarkVertices(mesh, index * format->stride, (index + 1) * format->stride); return 0; } int l_lovrMeshGetVertexAttribute(lua_State* L) { Mesh* mesh = luax_checktype(L, 1, Mesh); - int vertexIndex = luaL_checkint(L, 2) - 1; - int attributeIndex = luaL_checkint(L, 3) - 1; + uint32_t vertexIndex = luaL_checkinteger(L, 2) - 1; + int attributeIndex = luaL_checkinteger(L, 3) - 1; + Buffer* buffer = lovrMeshGetVertexBuffer(mesh); VertexFormat* format = lovrMeshGetVertexFormat(mesh); - lovrAssert(lovrMeshIsReadable(mesh), "Mesh:getVertex can only be used if the Mesh was created with the readable flag"); + lovrAssert(lovrBufferIsReadable(buffer), "Mesh:getVertex can only be used if the Mesh was created with the readable flag"); lovrAssert(vertexIndex >= 0 && vertexIndex < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex: %d", vertexIndex + 1); lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid mesh attribute: %d", attributeIndex + 1); Attribute attribute = format->attributes[attributeIndex]; - VertexPointer vertex = { .raw = lovrMeshMapVertices(mesh, vertexIndex * format->stride + attribute.offset) }; + VertexPointer vertex = { .raw = lovrBufferMap(buffer, vertexIndex * format->stride + attribute.offset) }; return luax_pushvertexattribute(L, &vertex, attribute); } int l_lovrMeshSetVertexAttribute(lua_State* L) { Mesh* mesh = luax_checktype(L, 1, Mesh); - int vertexIndex = luaL_checkint(L, 2) - 1; - int attributeIndex = luaL_checkint(L, 3) - 1; + uint32_t vertexIndex = luaL_checkinteger(L, 2) - 1; + int attributeIndex = luaL_checkinteger(L, 3) - 1; VertexFormat* format = lovrMeshGetVertexFormat(mesh); lovrAssert(vertexIndex >= 0 && vertexIndex < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex: %d", vertexIndex + 1); lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid mesh attribute: %d", attributeIndex + 1); Attribute attribute = format->attributes[attributeIndex]; - VertexPointer vertex = { .raw = lovrMeshMapVertices(mesh, vertexIndex * format->stride + attribute.offset) }; + Buffer* buffer = lovrMeshGetVertexBuffer(mesh); + VertexPointer vertex = { .raw = lovrBufferMap(buffer, vertexIndex * format->stride + attribute.offset) }; luax_setvertexattribute(L, 4, &vertex, attribute); - lovrMeshFlushVertices(mesh, vertexIndex * format->stride + attribute.offset, attribute.size); + lovrMeshMarkVertices(mesh, vertexIndex * format->stride + attribute.offset, vertexIndex * format->stride + attribute.offset + attribute.size); return 0; } @@ -173,12 +188,13 @@ int l_lovrMeshSetVertices(lua_State* L) { lovrAssert(sameFormat, "Mesh and VertexData must have the same format to copy vertices"); } - uint32_t start = luaL_optnumber(L, 3, 1) - 1; + uint32_t start = luaL_optinteger(L, 3, 1) - 1; uint32_t count = luaL_optinteger(L, 4, sourceSize); lovrAssert(start + count <= capacity, "Overflow in Mesh:setVertices: Mesh can only hold %d vertices", capacity); lovrAssert(count <= sourceSize, "Cannot set %d vertices on Mesh: source only has %d vertices", count, sourceSize); - VertexPointer vertices = { .raw = lovrMeshMapVertices(mesh, start * format->stride) }; + Buffer* buffer = lovrMeshGetVertexBuffer(mesh); + VertexPointer vertices = { .raw = lovrBufferMap(buffer, start * format->stride) }; if (vertexData) { memcpy(vertices.raw, vertexData->blob.data, count * format->stride); @@ -191,22 +207,25 @@ int l_lovrMeshSetVertices(lua_State* L) { } } - lovrMeshFlushVertices(mesh, start * format->stride, count * format->stride); + lovrMeshMarkVertices(mesh, start * format->stride, (start + count) * format->stride); return 0; } int l_lovrMeshGetVertexMap(lua_State* L) { Mesh* mesh = luax_checktype(L, 1, Mesh); - uint32_t count; - size_t size; - IndexPointer indices = { .raw = lovrMeshReadIndices(mesh, &count, &size) }; + Buffer* buffer = lovrMeshGetIndexBuffer(mesh); + uint32_t count = lovrMeshGetIndexCount(mesh); + size_t size = lovrMeshGetIndexSize(mesh); - if (count == 0 || !indices.raw) { + if (!buffer || count == 0 || size == 0) { lua_pushnil(L); return 1; } + lovrAssert(lovrBufferIsReadable(buffer), "Mesh:getVertexMap can only be used if the Mesh was created with the readable flag"); + IndexPointer indices = { .raw = lovrBufferMap(buffer, 0) }; + if (lua_istable(L, 2)) { lua_settop(L, 2); } else if (lua_isuserdata(L, 2)) { @@ -232,7 +251,7 @@ int l_lovrMeshSetVertexMap(lua_State* L) { Mesh* mesh = luax_checktype(L, 1, Mesh); if (lua_isnoneornil(L, 2)) { - lovrMeshMapIndices(mesh, 0, 0, 0); + lovrMeshSetIndexBuffer(mesh, NULL, 0, 0); return 0; } @@ -241,14 +260,33 @@ int l_lovrMeshSetVertexMap(lua_State* L) { size_t size = luaL_optinteger(L, 3, 4); lovrAssert(size == 2 || size == 4, "Size of Mesh indices should be 2 bytes or 4 bytes"); uint32_t count = blob->size / size; - void* indices = lovrMeshMapIndices(mesh, count, size, 0); - memcpy(indices, blob->data, blob->size); + Buffer* indexBuffer = lovrMeshGetIndexBuffer(mesh); + if (!indexBuffer || count * size > lovrBufferGetSize(indexBuffer)) { + Buffer* vertexBuffer = lovrMeshGetVertexBuffer(mesh); + BufferUsage usage = vertexBuffer ? lovrBufferGetUsage(vertexBuffer) : USAGE_DYNAMIC; + bool readable = vertexBuffer ? lovrBufferIsReadable(vertexBuffer) : false; + indexBuffer = lovrBufferCreate(blob->size, blob->data, BUFFER_INDEX, usage, readable); + lovrMeshSetIndexBuffer(mesh, indexBuffer, count, size); + } else { + void* indices = lovrBufferMap(indexBuffer, 0); + memcpy(indices, blob->data, blob->size); + lovrBufferFlush(indexBuffer, 0, blob->size); + } } else { luaL_checktype(L, 2, LUA_TTABLE); uint32_t count = lua_objlen(L, 2); uint32_t vertexCount = lovrMeshGetVertexCount(mesh); size_t size = vertexCount > USHRT_MAX ? sizeof(uint32_t) : sizeof(uint16_t); - IndexPointer indices = { .raw = lovrMeshMapIndices(mesh, count, size, 0) }; + + Buffer* indexBuffer = lovrMeshGetIndexBuffer(mesh); + if (!indexBuffer || count * size > lovrBufferGetSize(indexBuffer)) { + Buffer* vertexBuffer = lovrMeshGetVertexBuffer(mesh); + BufferUsage usage = vertexBuffer ? lovrBufferGetUsage(vertexBuffer) : USAGE_DYNAMIC; + bool readable = vertexBuffer ? lovrBufferIsReadable(vertexBuffer) : false; + indexBuffer = lovrBufferCreate(count * size, NULL, BUFFER_INDEX, usage, readable); + } + + IndexPointer indices = { .raw = lovrBufferMap(indexBuffer, 0) }; for (uint32_t i = 0; i < count; i++) { lua_rawgeti(L, 2, i + 1); @@ -269,9 +307,10 @@ int l_lovrMeshSetVertexMap(lua_State* L) { lua_pop(L, 1); } - } - lovrMeshFlushIndices(mesh); + lovrMeshSetIndexBuffer(mesh, indexBuffer, count, size); + lovrBufferFlush(indexBuffer, 0, count * size); + } return 0; } diff --git a/src/api/types/model.c b/src/api/types/model.c index a6510af7..775a4591 100644 --- a/src/api/types/model.c +++ b/src/api/types/model.c @@ -5,7 +5,7 @@ int l_lovrModelDraw(lua_State* L) { Model* model = luax_checktype(L, 1, Model); float transform[16]; - int index = luax_readmat4(L, 3, transform, 1, NULL); + int index = luax_readmat4(L, 2, transform, 1, NULL); int instances = luaL_optinteger(L, index, 1); lovrModelDraw(model, transform, instances); return 0; diff --git a/src/api/types/shader.c b/src/api/types/shader.c index f0cd46a8..6eccc7ec 100644 --- a/src/api/types/shader.c +++ b/src/api/types/shader.c @@ -248,7 +248,8 @@ int l_lovrShaderSendBlock(lua_State* L) { const char* name = luaL_checkstring(L, 2); ShaderBlock* block = luax_checktype(L, 3, ShaderBlock); UniformAccess access = luaL_checkoption(L, 4, "readwrite", UniformAccesses); - lovrShaderSetBlock(shader, name, block, access); + Buffer* buffer = lovrShaderBlockGetBuffer(block); + lovrShaderSetBlock(shader, name, buffer, 0, lovrBufferGetSize(buffer), access); return 0; } diff --git a/src/graphics/buffer.c b/src/graphics/buffer.c index 5c72e290..409030dc 100644 --- a/src/graphics/buffer.c +++ b/src/graphics/buffer.c @@ -4,6 +4,10 @@ size_t lovrBufferGetSize(Buffer* buffer) { return buffer->size; } +bool lovrBufferIsReadable(Buffer* buffer) { + return buffer->readable; +} + BufferUsage lovrBufferGetUsage(Buffer* buffer) { return buffer->usage; } diff --git a/src/graphics/buffer.h b/src/graphics/buffer.h index 734ede91..ed37fa33 100644 --- a/src/graphics/buffer.h +++ b/src/graphics/buffer.h @@ -5,6 +5,15 @@ #pragma once +typedef enum { + BUFFER_VERTEX, + BUFFER_INDEX, + BUFFER_UNIFORM, + BUFFER_SHADER_STORAGE, + BUFFER_GENERIC, + MAX_BUFFER_TYPES +} BufferType; + typedef enum { USAGE_STATIC, USAGE_DYNAMIC, @@ -15,16 +24,17 @@ typedef struct { Ref ref; void* data; size_t size; + bool readable; + BufferType type; BufferUsage usage; GPU_BUFFER_FIELDS } Buffer; -Buffer* lovrBufferInit(Buffer* buffer, size_t size, void* data, BufferUsage usage, bool readable); +Buffer* lovrBufferInit(Buffer* buffer, size_t size, void* data, BufferType type, BufferUsage usage, bool readable); #define lovrBufferCreate(...) lovrBufferInit(lovrAlloc(Buffer), __VA_ARGS__) void lovrBufferDestroy(void* ref); size_t lovrBufferGetSize(Buffer* buffer); +bool lovrBufferIsReadable(Buffer* buffer); BufferUsage lovrBufferGetUsage(Buffer* buffer); void* lovrBufferMap(Buffer* buffer, size_t offset); void lovrBufferFlush(Buffer* buffer, size_t offset, size_t size); -void lovrBufferLock(Buffer* buffer); -void lovrBufferUnlock(Buffer* buffer); diff --git a/src/graphics/canvas.c b/src/graphics/canvas.c index 093d5334..3feac043 100644 --- a/src/graphics/canvas.c +++ b/src/graphics/canvas.c @@ -1,4 +1,5 @@ #include "graphics/canvas.h" +#include "graphics/graphics.h" const Attachment* lovrCanvasGetAttachments(Canvas* canvas, int* count) { if (count) *count = canvas->attachmentCount; @@ -13,6 +14,8 @@ void lovrCanvasSetAttachments(Canvas* canvas, Attachment* attachments, int count return; } + lovrGraphicsFlushCanvas(canvas); + for (int i = 0; i < count; i++) { Texture* texture = attachments[i].texture; int width = lovrTextureGetWidth(texture, attachments[i].level); diff --git a/src/graphics/font.c b/src/graphics/font.c index c85dfe37..49015fb9 100644 --- a/src/graphics/font.c +++ b/src/graphics/font.c @@ -68,7 +68,7 @@ Rasterizer* lovrFontGetRasterizer(Font* font) { return font->rasterizer; } -void lovrFontRender(Font* font, const char* str, size_t length, float wrap, HorizontalAlign halign, VerticalAlign valign, float* vertices, float* offsety, uint32_t* vertexCount) { +void lovrFontRender(Font* font, const char* str, size_t length, float wrap, HorizontalAlign halign, float* vertices, uint16_t* indices, uint16_t baseVertex) { FontAtlas* atlas = &font->atlas; float cx = 0; @@ -83,17 +83,16 @@ void lovrFontRender(Font* font, const char* str, size_t length, float wrap, Hori unsigned int codepoint; size_t bytes; - float* cursor = vertices; + float* vertexCursor = vertices; + uint16_t* indexCursor = indices; float* lineStart = vertices; - int lineCount = 1; - *vertexCount = 0; + uint16_t I = baseVertex; while ((bytes = utf8_decode(str, end, &codepoint)) > 0) { // Newlines if (codepoint == '\n' || (wrap && cx * scale > wrap && codepoint == ' ')) { - lineStart = lovrFontAlignLine(lineStart, cursor, cx, halign); - lineCount++; + lineStart = lovrFontAlignLine(lineStart, vertexCursor, cx, halign); cx = 0; cy -= font->rasterizer->height * font->lineHeight; previous = '\0'; @@ -118,7 +117,7 @@ void lovrFontRender(Font* font, const char* str, size_t length, float wrap, Hori // Start over if texture was repacked if (u != atlas->width || v != atlas->height) { - lovrFontRender(font, start, length, wrap, halign, valign, vertices, offsety, vertexCount); + lovrFontRender(font, start, length, wrap, halign, vertices, indices, baseVertex); return; } @@ -133,18 +132,18 @@ void lovrFontRender(Font* font, const char* str, size_t length, float wrap, Hori float s2 = (glyph->x + glyph->tw) / u; float t2 = glyph->y / v; - float quad[48] = { + memcpy(vertexCursor, (float[32]) { x1, y1, 0, 0, 0, 0, s1, t1, x1, y2, 0, 0, 0, 0, s1, t2, x2, y1, 0, 0, 0, 0, s2, t1, - x2, y1, 0, 0, 0, 0, s2, t1, - x1, y2, 0, 0, 0, 0, s1, t2, x2, y2, 0, 0, 0, 0, s2, t2 - }; + }, 32 * sizeof(float)); - memcpy(cursor, quad, 6 * 8 * sizeof(float)); - cursor += 48; - *vertexCount += 6; + memcpy(indexCursor, (uint16_t[6]) { I + 0, I + 1, I + 2, I + 2, I + 1, I + 3 }, 6 * sizeof(uint16_t)); + + vertexCursor += 32; + indexCursor += 6; + I += 4; } // Advance cursor @@ -153,30 +152,24 @@ void lovrFontRender(Font* font, const char* str, size_t length, float wrap, Hori } // Align the last line - lovrFontAlignLine(lineStart, cursor, cx, halign); - - // Calculate vertical offset - if (valign == ALIGN_MIDDLE) { - *offsety = lineCount * font->rasterizer->height * font->lineHeight * .5f; - } else if (valign == ALIGN_BOTTOM) { - *offsety = lineCount * font->rasterizer->height * font->lineHeight; - } else { - *offsety = 0; - } + lovrFontAlignLine(lineStart, vertexCursor, cx, halign); } -float lovrFontGetWidth(Font* font, const char* str, float wrap) { - float width = 0; +void lovrFontMeasure(Font* font, const char* str, size_t length, float wrap, float* width, uint32_t* lineCount, uint32_t* glyphCount) { float x = 0; - const char* end = str + strlen(str); + const char* end = str + length; size_t bytes; unsigned int previous = '\0'; unsigned int codepoint; float scale = 1 / font->pixelDensity; + *width = 0.f; + *lineCount = 0; + *glyphCount = 0; while ((bytes = utf8_decode(str, end, &codepoint)) > 0) { if (codepoint == '\n' || (wrap && x * scale > wrap && codepoint == ' ')) { - width = MAX(width, x * scale); + *width = MAX(*width, x * scale); + (*lineCount)++; x = 0; previous = '\0'; str += bytes; @@ -192,12 +185,17 @@ float lovrFontGetWidth(Font* font, const char* str, float wrap) { } Glyph* glyph = lovrFontGetGlyph(font, codepoint); + + if (glyph->w > 0 && glyph->h > 0) { + (*glyphCount)++; + } + x += glyph->advance + lovrFontGetKerning(font, previous, codepoint); previous = codepoint; str += bytes; } - return MAX(width, x * scale); + *width = MAX(*width, x * scale); } float lovrFontGetHeight(Font* font) { diff --git a/src/graphics/font.h b/src/graphics/font.h index 3308e6eb..00bba60d 100644 --- a/src/graphics/font.h +++ b/src/graphics/font.h @@ -8,14 +8,14 @@ typedef enum { ALIGN_LEFT, - ALIGN_RIGHT, - ALIGN_CENTER + ALIGN_CENTER, + ALIGN_RIGHT } HorizontalAlign; typedef enum { ALIGN_TOP, - ALIGN_BOTTOM, - ALIGN_MIDDLE + ALIGN_MIDDLE, + ALIGN_BOTTOM } VerticalAlign; typedef struct { @@ -42,8 +42,8 @@ Font* lovrFontInit(Font* font, Rasterizer* rasterizer); #define lovrFontCreate(...) lovrFontInit(lovrAlloc(Font), __VA_ARGS__) void lovrFontDestroy(void* ref); Rasterizer* lovrFontGetRasterizer(Font* font); -void lovrFontRender(Font* font, const char* str, size_t length, float wrap, HorizontalAlign halign, VerticalAlign valign, float* vertices, float* offsety, uint32_t* vertexCount); -float lovrFontGetWidth(Font* font, const char* string, float wrap); +void lovrFontRender(Font* font, const char* str, size_t length, float wrap, HorizontalAlign halign, float* vertices, uint16_t* indices, uint16_t baseVertex); +void lovrFontMeasure(Font* font, const char* string, size_t length, float wrap, float* width, uint32_t* lineCount, uint32_t* glyphCount); float lovrFontGetHeight(Font* font); float lovrFontGetAscent(Font* font); float lovrFontGetDescent(Font* font); diff --git a/src/graphics/graphics.c b/src/graphics/graphics.c index bbd73ed9..b991606a 100644 --- a/src/graphics/graphics.c +++ b/src/graphics/graphics.c @@ -6,9 +6,9 @@ #include "util.h" #include "lib/math.h" #include "lib/stb/stb_image.h" -#define _USE_MATH_DEFINES #include #include +#define _USE_MATH_DEFINES #include static GraphicsState state; @@ -30,20 +30,100 @@ static void onResizeWindow(int width, int height) { state.height = height; } -static bool batchable(DrawRequest* a) { - DrawRequest* b = &state.batch; - if (a->instances > 1) return false; - if (a->mesh != b->mesh) return false; - if (!a->mesh && a->mode != b->mode) return false; - if (!a->mesh && !!a->index.count != !!b->index.count) return false; - if (a->shader != b->shader) return false; - if (a->material != b->material) return false; - if (!a->material && a->diffuseTexture != b->diffuseTexture) return false; - if (!a->material && a->environmentMap != b->environmentMap) return false; - if (a->mono != b->mono) return false; - if (!!a->pose != !!b->pose || (a->pose && memcmp(a->pose, b->pose, MAX_BONES * 16 * sizeof(float)))) return false; - if (a->material && lovrMaterialIsDirty(a->material)) return false; - return true; +static const size_t BUFFER_COUNTS[] = { + [STREAM_VERTEX] = 1 << 16, + [STREAM_INDEX] = 1 << 16, + [STREAM_DRAW_ID] = 1 << 16, + [STREAM_DRAW_DATA] = 256 * MAX_BATCHES * 2 +}; + +static const size_t BUFFER_STRIDES[] = { + [STREAM_VERTEX] = 8 * sizeof(float), + [STREAM_INDEX] = sizeof(uint16_t), + [STREAM_DRAW_ID] = sizeof(uint8_t), + [STREAM_DRAW_DATA] = sizeof(DrawData) +}; + +static const BufferType BUFFER_TYPES[] = { + [STREAM_VERTEX] = BUFFER_VERTEX, + [STREAM_INDEX] = BUFFER_INDEX, + [STREAM_DRAW_ID] = BUFFER_GENERIC, + [STREAM_DRAW_DATA] = BUFFER_UNIFORM +}; + +static void lovrGraphicsInitBuffers() { + for (int i = 0; i < MAX_BUFFER_ROLES; i++) { + state.buffers[i] = lovrBufferCreate(BUFFER_COUNTS[i] * BUFFER_STRIDES[i], NULL, BUFFER_TYPES[i], USAGE_STREAM, false); + } + + // Compute the max number of draws per batch, since the hard cap of 256 won't always fit in a UBO + size_t maxBlockSize = lovrGpuGetLimits()->blockSize; + state.maxDraws = MIN(maxBlockSize / sizeof(DrawData) / 64 * 64, 256); + + // The identity buffer is used for autoinstanced meshes and instanced primitives and maps the + // instance ID to a vertex attribute. Its contents never change, so they are initialized here. + state.identityBuffer = lovrBufferCreate(256, NULL, BUFFER_VERTEX, USAGE_STATIC, false); + uint8_t* id = lovrBufferMap(state.identityBuffer, 0); + for (int i = 0; i < 256; i++) id[i] = i; + lovrBufferFlush(state.identityBuffer, 0, 256); + + MeshAttribute position = { + .buffer = state.buffers[STREAM_VERTEX], + .offset = 0, + .stride = BUFFER_STRIDES[STREAM_VERTEX], + .type = ATTR_FLOAT, + .components = 3, + .enabled = true + }; + + MeshAttribute normal = { + .buffer = state.buffers[STREAM_VERTEX], + .offset = 3 * sizeof(float), + .stride = BUFFER_STRIDES[STREAM_VERTEX], + .type = ATTR_FLOAT, + .components = 3, + .enabled = true + }; + + MeshAttribute texCoord = { + .buffer = state.buffers[STREAM_VERTEX], + .offset = 3 * sizeof(float) + 3 * sizeof(float), + .stride = BUFFER_STRIDES[STREAM_VERTEX], + .type = ATTR_FLOAT, + .components = 2, + .enabled = true + }; + + MeshAttribute drawId = { + .buffer = state.buffers[STREAM_DRAW_ID], + .type = ATTR_BYTE, + .components = 1, + .integer = true, + .enabled = true + }; + + MeshAttribute identity = { + .buffer = state.identityBuffer, + .type = ATTR_BYTE, + .components = 1, + .divisor = 1, + .integer = true, + .enabled = true + }; + + VertexFormat empty = { .count = 0 }; + + state.mesh = lovrMeshCreate(DRAW_TRIANGLES, empty, NULL); + lovrMeshAttachAttribute(state.mesh, "lovrPosition", &position); + lovrMeshAttachAttribute(state.mesh, "lovrNormal", &normal); + lovrMeshAttachAttribute(state.mesh, "lovrTexCoord", &texCoord); + lovrMeshAttachAttribute(state.mesh, "lovrDrawID", &drawId); + + state.instancedMesh = lovrMeshCreate(DRAW_TRIANGLES, empty, NULL); + lovrMeshAttachAttribute(state.instancedMesh, "lovrPosition", &position); + lovrMeshAttachAttribute(state.instancedMesh, "lovrNormal", &normal); + lovrMeshAttachAttribute(state.instancedMesh, "lovrTexCoord", &texCoord); + lovrMeshAttachAttribute(state.instancedMesh, "lovrDrawID", &identity); } // Base @@ -61,12 +141,17 @@ void lovrGraphicsDestroy() { for (int i = 0; i < MAX_DEFAULT_SHADERS; i++) { lovrRelease(state.defaultShaders[i]); } + for (int i = 0; i < MAX_BUFFER_ROLES; i++) { + lovrRelease(state.buffers[i]); + for (int j = 0; j < MAX_LOCKS; j++) { + lovrGpuDestroyLock(state.locks[i][j]); + } + } + lovrRelease(state.mesh); + lovrRelease(state.instancedMesh); + lovrRelease(state.identityBuffer); lovrRelease(state.defaultMaterial); lovrRelease(state.defaultFont); - lovrRelease(state.defaultMesh); - lovrRelease(state.vertexMap); - lovrRelease(state.identityBuffer); - lovrRelease(state.block); lovrGpuDestroy(); memset(&state, 0, sizeof(GraphicsState)); } @@ -94,34 +179,7 @@ void lovrGraphicsSetWindow(WindowFlags* flags) { lovrPlatformOnWindowResize(onResizeWindow); lovrPlatformGetFramebufferSize(&state.width, &state.height); lovrGpuInit(state.gammaCorrect, lovrGetProcAddress); - - VertexFormat format; - vertexFormatInit(&format); - vertexFormatAppend(&format, "lovrPosition", ATTR_FLOAT, 3); - vertexFormatAppend(&format, "lovrNormal", ATTR_FLOAT, 3); - vertexFormatAppend(&format, "lovrTexCoord", ATTR_FLOAT, 2); - state.defaultMesh = lovrMeshCreate(MAX_VERTICES, format, DRAW_TRIANGLES, USAGE_STREAM, false); - - state.vertexMap = lovrBufferCreate(MAX_VERTICES * sizeof(uint8_t), NULL, USAGE_STREAM, false); - lovrMeshAttachAttribute(state.defaultMesh, "lovrDrawID", &(MeshAttribute) { - .buffer = state.vertexMap, - .type = ATTR_BYTE, - .components = 1, - .integer = true, - .enabled = true - }); - - vec_uniform_t uniforms; - vec_init(&uniforms); - vec_push(&uniforms, ((Uniform) { .name = "lovrModels", .type = UNIFORM_MATRIX, .components = 4, .count = MAX_BATCH_SIZE })); - vec_push(&uniforms, ((Uniform) { .name = "lovrColors", .type = UNIFORM_FLOAT, .components = 4, .count = MAX_BATCH_SIZE })); - state.block = lovrShaderBlockCreate(&uniforms, BLOCK_UNIFORM, USAGE_STREAM); - vec_deinit(&uniforms); - - uint8_t identity[MAX_BATCH_SIZE]; - for (int i = 0; i < MAX_BATCH_SIZE; i++) { identity[i] = i; } - state.identityBuffer = lovrBufferCreate(sizeof(identity), identity, USAGE_STATIC, false); - + lovrGraphicsInitBuffers(); lovrGraphicsReset(); state.initialized = true; } @@ -157,6 +215,34 @@ void lovrGraphicsSetCamera(Camera* camera, bool clear) { } } +void* lovrGraphicsMapBuffer(BufferRole role, uint32_t count) { + Buffer* buffer = state.buffers[role]; + size_t limit = BUFFER_COUNTS[role]; + lovrAssert(count <= limit, "Whoa there! Tried to get %d elements from a buffer that only has %d elements.", count, limit); + + if (state.cursors[role] + count > limit) { + lovrGraphicsFlush(); + state.cursors[role] = 0; + + // Locks are placed as late as possible, causing the last lock to never get placed. Whenever we + // wrap around a buffer, we gotta place that last missing lock. + state.locks[role][MAX_LOCKS - 1] = lovrGpuLock(); + } + + // Wait on any pending locks for the mapped region(s) + int firstLock = state.cursors[role] / (BUFFER_COUNTS[role] / MAX_LOCKS); + int lastLock = MIN(state.cursors[role] + count, BUFFER_COUNTS[role] - 1) / (BUFFER_COUNTS[role] / MAX_LOCKS); + for (int i = firstLock; i <= lastLock; i++) { + if (state.locks[role][i]) { + lovrGpuUnlock(state.locks[role][i]); + lovrGpuDestroyLock(state.locks[role][i]); + state.locks[role][i] = NULL; + } + } + + return lovrBufferMap(buffer, state.cursors[role] * BUFFER_STRIDES[role]); +} + Buffer* lovrGraphicsGetIdentityBuffer() { return state.identityBuffer; } @@ -375,343 +461,683 @@ void lovrGraphicsSetProjection(mat4 projection) { // Rendering -float* lovrGraphicsGetVertexPointer(uint32_t count) { - lovrAssert(count <= MAX_VERTICES, "Hey now! Up to %d vertices are allowed per primitive", MAX_VERTICES); - - if (state.vertexCursor + count > MAX_VERTICES) { - lovrGraphicsFlush(); - state.batchVertex = state.vertexCursor = 0; - } - - return lovrMeshMapVertices(state.defaultMesh, state.vertexCursor * 8 * sizeof(float)); -} - -uint16_t* lovrGraphicsGetIndexPointer(uint32_t count) { - lovrAssert(count <= MAX_INDICES, "Whoa there! Up to %d indices are allowed per primitive", MAX_INDICES); - - if (state.indexCursor + count > MAX_INDICES) { - lovrGraphicsFlush(); - state.batchIndex = state.indexCursor = 0; - } - - return lovrMeshMapIndices(state.defaultMesh, count ? MAX_INDICES : 0, sizeof(uint16_t), state.indexCursor * sizeof(uint16_t)); -} - void lovrGraphicsClear(Color* color, float* depth, int* stencil) { + if (color || depth || stencil) { + lovrGraphicsFlush(); + } + if (color) gammaCorrectColor(color); lovrGpuClear(state.canvas ? state.canvas : state.camera.canvas, color, depth, stencil); } void lovrGraphicsDiscard(bool color, bool depth, bool stencil) { + if (color || depth || stencil) { + lovrGraphicsFlush(); + } + lovrGpuDiscard(state.canvas ? state.canvas : state.camera.canvas, color, depth, stencil); } +void lovrGraphicsBatch(BatchRequest* req) { + + // Resolve objects + Canvas* canvas = state.canvas ? state.canvas : state.camera.canvas; + Shader* shader = state.shader ? state.shader : (state.defaultShaders[req->shader] ? state.defaultShaders[req->shader] : (state.defaultShaders[req->shader] = lovrShaderCreateDefault(req->shader))); + Pipeline* pipeline = req->pipeline ? req->pipeline : &state.pipeline; + Material* material = req->material ? req->material : (state.defaultMaterial ? state.defaultMaterial : (state.defaultMaterial = lovrMaterialCreate())); + + if (!req->material) { + lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, req->diffuseTexture); + lovrMaterialSetTexture(material, TEXTURE_ENVIRONMENT_MAP, req->environmentMap); + } + + if (req->type == BATCH_MESH) { + float* pose = req->params.mesh.pose ? req->params.mesh.pose : (float[]) MAT4_IDENTITY; + size_t count = req->params.mesh.pose ? (MAX_BONES * 16) : 16; + lovrShaderSetMatrices(shader, "lovrPose", pose, 0, count); + } + + // Try to find an existing batch to use + Batch* batch = NULL; + if (req->type != BATCH_LINES && (req->type != BATCH_MESH || req->params.mesh.instances == 1)) { + for (int i = state.batchCount - 1; i >= 0; i--) { + Batch* b = &state.batches[i]; + + if (b->type != req->type) { continue; } + + BatchParams* p = &req->params; + BatchParams* q = &b->params; + switch (req->type) { + case BATCH_TRIANGLES: + if (p->triangles.style != q->triangles.style) { continue; } + break; + case BATCH_BOX: + if (p->box.style != q->box.style) { continue; } + break; + case BATCH_ARC: + if (p->arc.style != q->arc.style || p->arc.mode != q->arc.mode) { continue; } + else if (p->arc.r1 != q->arc.r1 || p->arc.r2 != q->arc.r2 || p->arc.segments != q->arc.segments) { continue; } + break; + case BATCH_SPHERE: + if (p->sphere.segments != q->sphere.segments) { continue; } + break; + case BATCH_MESH: + if (p->mesh.object != q->mesh.object) { continue; } + else if (p->mesh.mode != q->mesh.mode) { continue; } + else if (p->mesh.rangeStart != q->mesh.rangeStart || p->mesh.rangeCount != q->mesh.rangeCount) { continue; } + break; + default: break; + } + + if (b->canvas == canvas && b->shader == shader && !memcmp(&b->pipeline, pipeline, sizeof(Pipeline)) && b->material == material) { + batch = b; + break; + } + + // Draws can't be reordered when blending is on + if (b->pipeline.blendMode != BLEND_NONE || pipeline->blendMode != BLEND_NONE) { + break; + } + + // Draws can't be reordered when the depth test is off + if (b->pipeline.depthTest == COMPARE_NONE || pipeline->depthTest == COMPARE_NONE) { + break; + } + + // Draws with streaming vertices must be sequential, since buffers are append-only + if (b->vertexCount > 0 && req->vertexCount > 0) { + break; + } + } + } + + size_t streamRequirements[] = { + [STREAM_VERTEX] = req->vertexCount, + [STREAM_INDEX] = req->indexCount, + [STREAM_DRAW_ID] = req->vertexCount, + [STREAM_DRAW_DATA] = 0 + }; + + if (!batch) { + streamRequirements[STREAM_DRAW_DATA] = state.maxDraws; + if (state.batchCount >= MAX_BATCHES) { + lovrGraphicsFlush(); + } + } + + for (int i = 0; i < MAX_BUFFER_ROLES; i++) { + if (streamRequirements[i] > 0 && state.cursors[i] + streamRequirements[i] > BUFFER_COUNTS[i]) { + size_t oldCursor = state.cursors[i]; + lovrGraphicsFlush(); + state.locks[i][MAX_LOCKS - 1] = lovrGpuLock(); + state.cursors[i] = state.cursors[i] >= oldCursor ? 0 : state.cursors[i]; + batch = NULL; + streamRequirements[STREAM_DRAW_DATA] = state.maxDraws; + i = 0; + } else { + streamRequirements[i] = 0; + } + } + + // Start a new batch + if (!batch) { + DrawData* drawData = lovrGraphicsMapBuffer(STREAM_DRAW_DATA, state.maxDraws); + batch = &state.batches[state.batchCount++]; + *batch = (Batch) { + .type = req->type, + .params = req->params, + .canvas = canvas, + .shader = shader, + .pipeline = *pipeline, + .material = material, + .vertexStart = req->vertexCount > 0 ? state.cursors[STREAM_VERTEX] : 0, + .vertexCount = 0, + .indexStart = req->indexCount > 0 ? state.cursors[STREAM_INDEX] : 0, + .indexCount = 0, + .drawStart = state.cursors[STREAM_DRAW_DATA], + .drawCount = 0, + .drawData = drawData + }; + + state.cursors[STREAM_DRAW_DATA] += state.maxDraws; + } + + // Transform + if (req->transform) { + float transform[16]; + mat4_multiply(mat4_init(transform, state.transforms[state.transform]), req->transform); + memcpy(batch->drawData[batch->drawCount].transform, transform, 16 * sizeof(float)); + } else { + memcpy(batch->drawData[batch->drawCount].transform, state.transforms[state.transform], 16 * sizeof(float)); + } + + // Color + Color color = state.color; + gammaCorrectColor(&color); + batch->drawData[batch->drawCount].color = color; + + // Handle streams + uint8_t* ids = NULL; + if (req->vertexCount > 0) { + *(req->vertices) = lovrGraphicsMapBuffer(STREAM_VERTEX, req->vertexCount); + ids = lovrGraphicsMapBuffer(STREAM_DRAW_ID, req->vertexCount); + memset(ids, batch->drawCount, req->vertexCount * sizeof(uint8_t)); + + if (req->indexCount > 0) { + *(req->indices) = lovrGraphicsMapBuffer(STREAM_INDEX, req->indexCount); + *(req->baseVertex) = state.cursors[STREAM_VERTEX]; + } + + batch->vertexCount += req->vertexCount; + batch->indexCount += req->indexCount; + + state.cursors[STREAM_VERTEX] += req->vertexCount; + state.cursors[STREAM_DRAW_ID] += req->vertexCount; + state.cursors[STREAM_INDEX] += req->indexCount; + } + + if (++batch->drawCount >= state.maxDraws) { + lovrGraphicsFlush(); + } +} + void lovrGraphicsFlush() { - if (state.batchSize == 0) { + if (state.batchCount == 0) { return; } - // Resolve objects - DrawRequest* draw = &state.batch; - Mesh* mesh = draw->mesh ? draw->mesh : state.defaultMesh; - Canvas* canvas = state.canvas ? state.canvas : state.camera.canvas; - Material* material = draw->material ? draw->material : (state.defaultMaterial ? state.defaultMaterial : (state.defaultMaterial = lovrMaterialCreate())); - Shader* shader = state.shader ? state.shader : (state.defaultShaders[draw->shader] ? state.defaultShaders[draw->shader] : (state.defaultShaders[draw->shader] = lovrShaderCreateDefault(draw->shader))); - Pipeline* pipeline = draw->pipeline ? draw->pipeline : &state.pipeline; + // Prevent infinite flushing >_> + int batchCount = state.batchCount; + state.batchCount = 0; - if (!draw->material) { - lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, draw->diffuseTexture); - lovrMaterialSetTexture(material, TEXTURE_ENVIRONMENT_MAP, draw->environmentMap); - } + for (int b = 0; b < batchCount; b++) { + Batch* batch = &state.batches[b]; + BatchParams* params = &batch->params; - // Camera - lovrShaderSetMatrices(shader, "lovrViews", state.camera.viewMatrix[0], 0, 32); - lovrShaderSetMatrices(shader, "lovrProjections", state.camera.projection[0], 0, 32); + // Resolve geometry + Mesh* mesh = NULL; + DrawMode drawMode; + bool instanced = batch->drawCount >= 4; + int instances = instanced ? batch->drawCount : 1; + uint32_t vertexCount = 0; + uint32_t indexCount = 0; + switch (batch->type) { + case BATCH_POINTS: mesh = state.mesh; drawMode = DRAW_POINTS; break; + case BATCH_LINES: mesh = state.mesh; drawMode = DRAW_LINE_STRIP; break; + case BATCH_TRIANGLES: + mesh = state.mesh; + drawMode = params->triangles.style == STYLE_LINE ? DRAW_LINE_LOOP : DRAW_TRIANGLES; + break; - // Pose - if (draw->pose) { - lovrShaderSetMatrices(shader, "lovrPose", draw->pose, 0, MAX_BONES * 16); - } else { - lovrShaderSetMatrices(shader, "lovrPose", (float[16]) MAT4_IDENTITY, 0, 16); - } + case BATCH_PLANE: + vertexCount = 4; + indexCount = params->plane.style == STYLE_LINE ? 0 : 6; + mesh = instanced ? state.instancedMesh : state.mesh; + drawMode = params->plane.style == STYLE_LINE ? DRAW_LINE_LOOP : DRAW_TRIANGLES; + break; - // Point size - lovrShaderSetFloats(shader, "lovrPointSize", &state.pointSize, 0, 1); + case BATCH_BOX: + vertexCount = params->box.style == STYLE_LINE ? 8 : 24; + indexCount = params->box.style == STYLE_LINE ? 24 : 36; + mesh = instanced ? state.instancedMesh : state.mesh; + drawMode = params->box.style == STYLE_LINE ? DRAW_LINES : DRAW_TRIANGLES; + break; - // Buffers and Mesh - int vertexCount = state.vertexCursor - state.batchVertex; - int indexCount = state.indexCursor - state.batchIndex; - lovrBufferFlush(lovrShaderBlockGetBuffer(state.block), 0, MAX_BATCH_SIZE * (16 * sizeof(float) + 4 * sizeof(float))); - lovrBufferFlush(state.vertexMap, state.batchVertex, vertexCount); + case BATCH_ARC: { + bool hasCenterPoint = params->arc.mode == ARC_MODE_PIE && fabsf(params->arc.r1 - params->arc.r2) < 2 * M_PI; + vertexCount = params->arc.segments + 1 + hasCenterPoint; + mesh = instanced ? state.instancedMesh : state.mesh; + drawMode = params->arc.style == STYLE_LINE ? (params->arc.mode == ARC_MODE_OPEN ? DRAW_LINE_STRIP : DRAW_LINE_LOOP) : DRAW_TRIANGLE_FAN; + break; + } - if (!draw->mesh) { - if (indexCount > 0) { - lovrMeshSetDrawRange(state.defaultMesh, state.batchIndex, indexCount); - } else { - lovrMeshSetDrawRange(state.defaultMesh, state.batchVertex, vertexCount); + case BATCH_SPHERE: { + int segments = params->sphere.segments; + vertexCount = (segments + 1) * (segments + 1); + indexCount = segments * segments * 6; + mesh = instanced ? state.instancedMesh : state.mesh; + drawMode = DRAW_TRIANGLES; + break; + } + + case BATCH_SKYBOX: + vertexCount = 4; + instanced = false; + instances = 1; + mesh = state.mesh; + drawMode = DRAW_TRIANGLE_STRIP; + break; + + case BATCH_TEXT: + mesh = state.mesh; + drawMode = DRAW_TRIANGLES; + break; + + case BATCH_FILL: + vertexCount = 4; + instanced = false; + instances = 1; + mesh = state.mesh; + drawMode = DRAW_TRIANGLE_STRIP; + break; + + case BATCH_MESH: + mesh = params->mesh.object; + drawMode = lovrMeshGetDrawMode(mesh); + if (params->mesh.instances > 1) { + lovrMeshSetAttributeEnabled(mesh, "lovrDrawID", false); + instances = params->mesh.instances; + } else { + lovrMeshSetAttributeEnabled(mesh, "lovrDrawID", true); + instances = batch->drawCount; + } + break; + + default: break; } - lovrMeshSetDrawMode(state.defaultMesh, draw->mode); - lovrMeshFlushVertices(state.defaultMesh, state.batchVertex * 8 * sizeof(float), vertexCount * 8 * sizeof(float)); - lovrMeshFlushIndices(state.defaultMesh); - } + // Write geometry + if (vertexCount > 0) { + int n = instanced ? 1 : batch->drawCount; + float* vertices = lovrGraphicsMapBuffer(STREAM_VERTEX, vertexCount * n); + uint16_t* indices = lovrGraphicsMapBuffer(STREAM_INDEX, indexCount * n); + uint16_t I = (uint16_t) state.cursors[STREAM_VERTEX]; - lovrMaterialBind(material, shader); - lovrShaderSetBlock(shader, "lovrDrawData", state.block, ACCESS_READ); + batch->vertexStart = state.cursors[STREAM_VERTEX]; + batch->indexStart = state.cursors[STREAM_INDEX]; + batch->vertexCount = vertexCount * n; + batch->indexCount = indexCount * n; - lovrGpuDraw(&(DrawCommand) { - .mesh = mesh, - .shader = shader, - .canvas = canvas, - .pipeline = *pipeline, - .instances = draw->instances, - .width = canvas ? lovrCanvasGetWidth(canvas) : state.width, - .height = canvas ? lovrCanvasGetHeight(canvas) : state.height, - .stereo = !draw->mono && (canvas ? lovrCanvasIsStereo(canvas) : state.camera.stereo) - }, 1); - - state.batchSize = 0; - state.batchVertex = state.vertexCursor; - state.batchIndex = state.indexCursor; -} - -void lovrGraphicsDraw(DrawRequest* draw) { - if (state.batchSize > 0 && !batchable(draw)) { - lovrGraphicsFlush(); - } - - if (state.batchSize == 0) { - memcpy(&state.batch, draw, sizeof(DrawRequest)); - } else if (draw->mesh && draw->instances <= 1) { - state.batch.instances++; - } - - // Geometry - if (!draw->mesh) { - if (draw->vertex.data) { - void* vertices = lovrGraphicsGetVertexPointer(draw->vertex.count); - memcpy(vertices, draw->vertex.data, draw->vertex.count * 8 * sizeof(float)); - } - - if (draw->index.count) { - // TODO conditionally use BaseVertex when it is supported to avoid this - if (draw->index.data) { - for (uint32_t i = 0; i < draw->index.count; i++) { - draw->index.data[i] += state.vertexCursor; + if (!instanced) { + uint8_t* ids = lovrGraphicsMapBuffer(STREAM_DRAW_ID, batch->vertexCount); + for (int i = 0; i < n; i++) { + memset(ids, i, vertexCount * sizeof(uint8_t)); + ids += vertexCount; } } - if (draw->index.data) { - uint16_t* indices = lovrGraphicsGetIndexPointer(draw->index.count); - memcpy(indices, draw->index.data, draw->index.count * sizeof(uint16_t)); - } + state.cursors[STREAM_VERTEX] += batch->vertexCount; + state.cursors[STREAM_INDEX] += batch->indexCount; + state.cursors[STREAM_DRAW_ID] += batch->vertexCount; - state.indexCursor += draw->index.count; - } else { - lovrMeshMapIndices(state.defaultMesh, 0, 0, 0); + switch (batch->type) { + case BATCH_PLANE: + if (params->plane.style == STYLE_LINE) { + for (int i = 0; i < n; i++) { + memcpy(vertices, (float[32]) { + -.5, .5, 0, 0, 0, 0, 0, 0, + .5, .5, 0, 0, 0, 0, 0, 0, + .5, -.5, 0, 0, 0, 0, 0, 0, + -.5, -.5, 0, 0, 0, 0, 0, 0 + }, 32 * sizeof(float)); + vertices += 32; + } + } else { + for (int i = 0; i < n; i++) { + memcpy(vertices, (float[32]) { + -.5, .5, 0, 0, 0, -1, 0, 1, + -.5, -.5, 0, 0, 0, -1, 0, 0, + .5, .5, 0, 0, 0, -1, 1, 1, + .5, -.5, 0, 0, 0, -1, 1, 0 + }, 32 * sizeof(float)); + vertices += 32; + + memcpy(indices, (uint16_t[6]) { + I + 0, I + 1, I + 2, I + 2, I + 1, I + 3 + }, 6 * sizeof(uint16_t)); + I += vertexCount; + indices += 6; + } + } + break; + + case BATCH_BOX: + if (params->box.style == STYLE_LINE) { + for (int i = 0; i < n; i++) { + memcpy(vertices, (float[64]) { + -.5, .5, -.5, 0, 0, 0, 0, 0, // Front + .5, .5, -.5, 0, 0, 0, 0, 0, + .5, -.5, -.5, 0, 0, 0, 0, 0, + -.5, -.5, -.5, 0, 0, 0, 0, 0, + -.5, .5, .5, 0, 0, 0, 0, 0, // Back + .5, .5, .5, 0, 0, 0, 0, 0, + .5, -.5, .5, 0, 0, 0, 0, 0, + -.5, -.5, .5, 0, 0, 0, 0, 0 + }, 64 * sizeof(float)); + vertices += 64; + + memcpy(indices, (uint16_t[24]) { + I + 0, I + 1, I + 1, I + 2, I + 2, I + 3, I + 3, I + 0, // Front + I + 4, I + 5, I + 5, I + 6, I + 6, I + 7, I + 7, I + 4, // Back + I + 0, I + 4, I + 1, I + 5, I + 2, I + 6, I + 3, I + 7 // Connections + }, 24 * sizeof(uint16_t)); + I += vertexCount; + indices += 24; + } + } else { + for (int i = 0; i < n; i++) { + memcpy(vertices, (float[192]) { + -.5, -.5, -.5, 0, 0, -1, 0, 0, // Front + -.5, .5, -.5, 0, 0, -1, 0, 1, + .5, -.5, -.5, 0, 0, -1, 1, 0, + .5, .5, -.5, 0, 0, -1, 1, 1, + .5, .5, -.5, 1, 0, 0, 0, 1, // Right + .5, .5, .5, 1, 0, 0, 1, 1, + .5, -.5, -.5, 1, 0, 0, 0, 0, + .5, -.5, .5, 1, 0, 0, 1, 0, + .5, -.5, .5, 0, 0, 1, 0, 0, // Back + .5, .5, .5, 0, 0, 1, 0, 1, + -.5, -.5, .5, 0, 0, 1, 1, 0, + -.5, .5, .5, 0, 0, 1, 1, 1, + -.5, .5, .5, -1, 0, 0, 0, 1, // Left + -.5, .5, -.5, -1, 0, 0, 1, 1, + -.5, -.5, .5, -1, 0, 0, 0, 0, + -.5, -.5, -.5, -1, 0, 0, 1, 0, + -.5, -.5, -.5, 0, -1, 0, 0, 0, // Bottom + .5, -.5, -.5, 0, -1, 0, 1, 0, + -.5, -.5, .5, 0, -1, 0, 0, 1, + .5, -.5, .5, 0, -1, 0, 1, 1, + -.5, .5, -.5, 0, 1, 0, 0, 1, // Top + -.5, .5, .5, 0, 1, 0, 0, 0, + .5, .5, -.5, 0, 1, 0, 1, 1, + .5, .5, .5, 0, 1, 0, 1, 0 + }, 192 * sizeof(float)); + vertices += 192; + + memcpy(indices, (uint16_t[36]) { + I + 0, I + 1, I + 2, I + 2, I + 1, I + 3, + I + 4, I + 5, I + 6, I + 6, I + 5, I + 7, + I + 8, I + 9, I + 10, I + 10, I + 9, I + 11, + I + 12, I + 13, I + 14, I + 14, I + 13, I + 15, + I + 16, I + 17, I + 18, I + 18, I + 17, I + 19, + I + 20, I + 21, I + 22, I + 22, I + 21, I + 23 + }, 36 * sizeof(uint16_t)); + I += vertexCount; + indices += 36; + } + } + break; + + case BATCH_ARC: { + float r1 = params->arc.r1; + float r2 = params->arc.r2; + int segments = params->arc.segments; + bool hasCenterPoint = params->arc.mode == ARC_MODE_PIE && fabsf(r1 - r2) < 2 * M_PI; + + for (int i = 0; i < n; i++) { + if (hasCenterPoint) { + memcpy(vertices, ((float[]) { 0, 0, 0, 0, 0, 1, .5, .5 }), 8 * sizeof(float)); + vertices += 8; + } + + float theta = r1; + float angleShift = (r2 - r1) / (float) segments; + + for (int i = 0; i <= segments; i++) { + float x = cos(theta) * .5; + float y = sin(theta) * .5; + memcpy(vertices, ((float[]) { x, y, 0, 0, 0, 1, x + .5, 1 - (y + .5) }), 8 * sizeof(float)); + vertices += 8; + theta += angleShift; + } + } + break; + } + + case BATCH_SPHERE: { + int segments = params->sphere.segments; + for (int i = 0; i < n; i++) { + for (int j = 0; j <= segments; j++) { + float v = j / (float) segments; + float sinV = sin(v * M_PI); + float cosV = cos(v * M_PI); + for (int k = 0; k <= segments; k++) { + float u = k / (float) segments; + float x = sin(u * 2 * M_PI) * sinV; + float y = cosV; + float z = -cos(u * 2 * M_PI) * sinV; + memcpy(vertices, ((float[8]) { x, y, z, x, y, z, u, 1 - v }), 8 * sizeof(float)); + vertices += 8; + } + } + + for (int j = 0; j < segments; j++) { + uint16_t offset0 = j * (segments + 1) + I; + uint16_t offset1 = (j + 1) * (segments + 1) + I; + for (int k = 0; k < segments; k++) { + uint16_t i0 = offset0 + k; + uint16_t i1 = offset1 + k; + memcpy(indices, ((uint16_t[]) { i0, i1, i0 + 1, i1, i1 + 1, i0 + 1 }), 6 * sizeof(uint16_t)); + indices += 6; + } + } + + I += vertexCount; + } + break; + } + + case BATCH_SKYBOX: + for (int i = 0; i < n; i++) { + memcpy(vertices, (float[32]) { + -1, 1, 1, 0, 0, 0, 0, 0, + -1, -1, 1, 0, 0, 0, 0, 0, + 1, 1, 1, 0, 0, 0, 0, 0, + 1, -1, 1, 0, 0, 0, 0, 0 + }, 32 * sizeof(float)); + vertices += 32; + } + break; + + case BATCH_FILL: + for (int i = 0; i < n; i++) { + float u = params->fill.u; + float v = params->fill.v; + float w = params->fill.w; + float h = params->fill.h; + memcpy(vertices, (float[32]) { + -1, 1, 0, 0, 0, 0, u, v + h, + -1, -1, 0, 0, 0, 0, u, v, + 1, 1, 0, 0, 0, 0, u + w, v + h, + 1, -1, 0, 0, 0, 0, u + w, v + }, 32 * sizeof(float)); + vertices += 32; + } + break; + + default: break; + } } - void* vertexMap = lovrBufferMap(state.vertexMap, state.vertexCursor * sizeof(uint8_t)); - memset(vertexMap, state.batchSize, draw->vertex.count * sizeof(uint8_t)); - state.vertexCursor += draw->vertex.count; - } + uint32_t rangeStart, rangeCount; + if (batch->type == BATCH_MESH) { + rangeStart = batch->params.mesh.rangeStart; + rangeCount = batch->params.mesh.rangeCount; + } else { + rangeStart = batch->indexCount ? batch->indexStart : batch->vertexStart; + rangeCount = batch->indexCount ? batch->indexCount : batch->vertexCount; + if (batch->indexCount > 0) { + lovrMeshSetIndexBuffer(mesh, state.buffers[STREAM_INDEX], BUFFER_COUNTS[STREAM_INDEX], sizeof(uint16_t)); + } else { + lovrMeshSetIndexBuffer(mesh, NULL, 0, 0); + } + } - // Transform and color - struct { float model[MAX_BATCH_SIZE][16]; float color[MAX_BATCH_SIZE][4]; } *drawData; - drawData = lovrBufferMap(lovrShaderBlockGetBuffer(state.block), 0); + // Uniforms + lovrMaterialBind(batch->material, batch->shader); + lovrShaderSetMatrices(batch->shader, "lovrViews", state.camera.viewMatrix[0], 0, 32); + lovrShaderSetMatrices(batch->shader, "lovrProjections", state.camera.projection[0], 0, 32); - memcpy(drawData->model[state.batchSize], state.transforms[state.transform], 16 * sizeof(float)); - if (draw->transform) { mat4_multiply(drawData->model[state.batchSize], draw->transform); } + if (drawMode == DRAW_POINTS) { + lovrShaderSetFloats(batch->shader, "lovrPointSize", &state.pointSize, 0, 1); + } - memcpy(drawData->color[state.batchSize], (float*) &state.color, 4 * sizeof(float)); + // Flush vertex buffer + if (batch->vertexCount > 0) { + size_t stride = BUFFER_STRIDES[STREAM_VERTEX]; + lovrBufferFlush(state.buffers[STREAM_VERTEX], batch->vertexStart * stride, batch->vertexCount * stride); - if (++state.batchSize >= MAX_BATCH_SIZE) { - lovrGraphicsFlush(); + if (!instanced) { + lovrBufferFlush(state.buffers[STREAM_DRAW_ID], batch->vertexStart, batch->vertexCount); + } + } + + // Flush index buffer + if (batch->indexCount > 0) { + size_t stride = BUFFER_STRIDES[STREAM_INDEX]; + lovrBufferFlush(state.buffers[STREAM_INDEX], batch->indexStart * stride, batch->indexCount * stride); + } + + // Flush draw data buffer + size_t drawDataOffset = batch->drawStart * BUFFER_STRIDES[STREAM_DRAW_DATA]; + size_t drawDataSize = batch->drawCount * BUFFER_STRIDES[STREAM_DRAW_DATA]; + lovrBufferFlush(state.buffers[STREAM_DRAW_DATA], drawDataOffset, drawDataSize); + lovrShaderSetBlock(batch->shader, "lovrDrawData", state.buffers[STREAM_DRAW_DATA], drawDataOffset, state.maxDraws * BUFFER_STRIDES[STREAM_DRAW_DATA], ACCESS_READ); + + // Draw! + lovrGpuDraw(&(DrawCommand) { + .mesh = mesh, + .shader = batch->shader, + .canvas = batch->canvas, + .pipeline = batch->pipeline, + .drawMode = drawMode, + .instances = instances, + .rangeStart = rangeStart, + .rangeCount = rangeCount, + .width = batch->canvas ? lovrCanvasGetWidth(batch->canvas) : state.width, + .height = batch->canvas ? lovrCanvasGetHeight(batch->canvas) : state.height, + .stereo = batch->type != BATCH_FILL && (batch->canvas ? lovrCanvasIsStereo(batch->canvas) : state.camera.stereo) + }); + + // Pop lock and drop it + + if (batch->vertexCount > 0) { + size_t lockSize = BUFFER_COUNTS[STREAM_VERTEX] / MAX_LOCKS + 1; + int firstLock = batch->vertexStart / lockSize; + int lastLock = (batch->vertexStart + batch->vertexCount) / lockSize; + for (int i = firstLock; i < lastLock; i++) { + state.locks[STREAM_VERTEX][i] = lovrGpuLock(); + if (!instanced) { + state.locks[STREAM_DRAW_ID][i] = lovrGpuLock(); + } + } + } + + if (batch->indexCount > 0) { + size_t lockSize = BUFFER_COUNTS[STREAM_INDEX] / MAX_LOCKS + 1; + int firstLock = batch->indexStart / lockSize; + int lastLock = (batch->indexStart + batch->indexCount) / lockSize; + for (int i = firstLock; i < lastLock; i++) { + state.locks[STREAM_INDEX][i] = lovrGpuLock(); + } + } + + if (batch->drawCount > 0) { + size_t lockSize = BUFFER_COUNTS[STREAM_DRAW_DATA] / MAX_LOCKS; + int firstLock = batch->drawStart / lockSize; + int lastLock = MIN(batch->drawStart + state.maxDraws, BUFFER_COUNTS[STREAM_DRAW_DATA] - 1) / lockSize; + for (int i = firstLock; i < lastLock; i++) { + state.locks[STREAM_DRAW_DATA][i] = lovrGpuLock(); + } + } } } -void lovrGraphicsPoints(uint32_t count) { - lovrGraphicsDraw(&(DrawRequest) { - .mode = DRAW_POINTS, - .vertex.count = count +void lovrGraphicsFlushCanvas(Canvas* canvas) { + for (int i = state.batchCount - 1; i >= 0; i--) { + if (state.batches[i].canvas == canvas) { + lovrGraphicsFlush(); + return; + } + } +} + +void lovrGraphicsFlushShader(Shader* shader) { + for (int i = state.batchCount - 1; i >= 0; i--) { + if (state.batches[i].shader == shader) { + lovrGraphicsFlush(); + return; + } + } +} + +void lovrGraphicsFlushMaterial(Material* material) { + for (int i = state.batchCount - 1; i >= 0; i--) { + if (state.batches[i].material == material) { + lovrGraphicsFlush(); + return; + } + } +} + +void lovrGraphicsFlushMesh(Mesh* mesh) { + for (int i = state.batchCount - 1; i >= 0; i--) { + if (state.batches[i].type == BATCH_MESH && state.batches[i].params.mesh.object == mesh) { + lovrGraphicsFlush(); + return; + } + } +} + +void lovrGraphicsPoints(uint32_t count, float** vertices) { + lovrGraphicsBatch(&(BatchRequest) { + .type = BATCH_POINTS, + .vertexCount = count, + .vertices = vertices }); } -void lovrGraphicsLine(uint32_t count) { - lovrGraphicsDraw(&(DrawRequest) { - .mode = DRAW_LINE_STRIP, - .vertex.count = count +void lovrGraphicsLine(uint32_t count, float** vertices) { + lovrGraphicsBatch(&(BatchRequest) { + .type = BATCH_LINES, + .vertexCount = count, + .vertices = vertices }); } -void lovrGraphicsTriangle(DrawStyle style, Material* material, float points[9]) { - if (style == STYLE_LINE) { - lovrGraphicsDraw(&(DrawRequest) { - .material = material, - .mode = DRAW_LINE_LOOP, - .vertex.count = 3, - .vertex.data = (float[]) { - points[0], points[1], points[2], 0, 0, 0, 0, 0, - points[3], points[4], points[5], 0, 0, 0, 0, 0, - points[6], points[7], points[8], 0, 0, 0, 0, 0 - } - }); - } else { - float normal[3]; - vec3_cross(vec3_init(normal, &points[0]), &points[3]); - lovrGraphicsDraw(&(DrawRequest) { - .material = material, - .mode = DRAW_TRIANGLES, - .vertex.count = 3, - .vertex.data = (float[]) { - points[0], points[1], points[2], normal[0], normal[1], normal[2], 0, 0, - points[3], points[4], points[5], normal[0], normal[1], normal[2], 0, 0, - points[6], points[7], points[8], normal[0], normal[1], normal[2], 0, 0 - } - }); - } +void lovrGraphicsTriangle(DrawStyle style, Material* material, uint32_t count, float** vertices) { + lovrGraphicsBatch(&(BatchRequest) { + .type = BATCH_TRIANGLES, + .params.triangles.style = style, + .material = material, + .vertexCount = count, + .vertices = vertices + }); } void lovrGraphicsPlane(DrawStyle style, Material* material, mat4 transform) { - if (style == STYLE_LINE) { - lovrGraphicsDraw(&(DrawRequest) { - .transform = transform, - .material = material, - .mode = DRAW_LINE_LOOP, - .vertex.count = 4, - .vertex.data = (float[]) { - -.5, .5, 0, 0, 0, 0, 0, 0, - .5, .5, 0, 0, 0, 0, 0, 0, - .5, -.5, 0, 0, 0, 0, 0, 0, - -.5, -.5, 0, 0, 0, 0, 0, 0 - } - }); - } else { - lovrGraphicsDraw(&(DrawRequest) { - .transform = transform, - .material = material, - .mode = DRAW_TRIANGLES, - .vertex.count = 4, - .vertex.data = (float[]) { - -.5, .5, 0, 0, 0, -1, 0, 1, - -.5, -.5, 0, 0, 0, -1, 0, 0, - .5, .5, 0, 0, 0, -1, 1, 1, - .5, -.5, 0, 0, 0, -1, 1, 0 - }, - .index.count = 6, - .index.data = (uint16_t[]) { 0, 1, 2, 2, 1, 3 } - }); - } + lovrGraphicsBatch(&(BatchRequest) { + .type = BATCH_PLANE, + .params.plane.style = style, + .material = material, + .transform = transform + }); } void lovrGraphicsBox(DrawStyle style, Material* material, mat4 transform) { - if (style == STYLE_LINE) { - lovrGraphicsDraw(&(DrawRequest) { - .transform = transform, - .material = material, - .mode = DRAW_LINES, - .vertex.count = 8, - .vertex.data = (float[]) { - // Front - -.5, .5, -.5, 0, 0, 0, 0, 0, - .5, .5, -.5, 0, 0, 0, 0, 0, - .5, -.5, -.5, 0, 0, 0, 0, 0, - -.5, -.5, -.5, 0, 0, 0, 0, 0, - // Back - -.5, .5, .5, 0, 0, 0, 0, 0, - .5, .5, .5, 0, 0, 0, 0, 0, - .5, -.5, .5, 0, 0, 0, 0, 0, - -.5, -.5, .5, 0, 0, 0, 0, 0 - }, - .index.count = 24, - .index.data = (uint16_t[]) { - 0, 1, 1, 2, 2, 3, 3, 0, // Front - 4, 5, 5, 6, 6, 7, 7, 4, // Back - 0, 4, 1, 5, 2, 6, 3, 7 // Connections - } - }); - } else { - lovrGraphicsDraw(&(DrawRequest) { - .transform = transform, - .material = material, - .mode = DRAW_TRIANGLES, - .vertex.count = 24, - .vertex.data = (float[]) { - // Front - -.5, -.5, -.5, 0, 0, -1, 0, 0, - -.5, .5, -.5, 0, 0, -1, 0, 1, - .5, -.5, -.5, 0, 0, -1, 1, 0, - .5, .5, -.5, 0, 0, -1, 1, 1, - // Right - .5, .5, -.5, 1, 0, 0, 0, 1, - .5, .5, .5, 1, 0, 0, 1, 1, - .5, -.5, -.5, 1, 0, 0, 0, 0, - .5, -.5, .5, 1, 0, 0, 1, 0, - // Back - .5, -.5, .5, 0, 0, 1, 0, 0, - .5, .5, .5, 0, 0, 1, 0, 1, - -.5, -.5, .5, 0, 0, 1, 1, 0, - -.5, .5, .5, 0, 0, 1, 1, 1, - // Left - -.5, .5, .5, -1, 0, 0, 0, 1, - -.5, .5, -.5, -1, 0, 0, 1, 1, - -.5, -.5, .5, -1, 0, 0, 0, 0, - -.5, -.5, -.5, -1, 0, 0, 1, 0, - // Bottom - -.5, -.5, -.5, 0, -1, 0, 0, 0, - .5, -.5, -.5, 0, -1, 0, 1, 0, - -.5, -.5, .5, 0, -1, 0, 0, 1, - .5, -.5, .5, 0, -1, 0, 1, 1, - // Top - -.5, .5, -.5, 0, 1, 0, 0, 1, - -.5, .5, .5, 0, 1, 0, 0, 0, - .5, .5, -.5, 0, 1, 0, 1, 1, - .5, .5, .5, 0, 1, 0, 1, 0 - }, - .index.count = 36, - .index.data = (uint16_t[]) { - 0, 1, 2, 2, 1, 3, - 4, 5, 6, 6, 5, 7, - 8, 9, 10, 10, 9, 11, - 12, 13, 14, 14, 13, 15, - 16, 17, 18, 18, 17, 19, - 20, 21, 22, 22, 21, 23 - } - }); - } + lovrGraphicsBatch(&(BatchRequest) { + .type = BATCH_BOX, + .params.box.style = style, + .material = material, + .transform = transform + }); } -void lovrGraphicsArc(DrawStyle style, ArcMode arcMode, Material* material, mat4 transform, float theta1, float theta2, int segments) { - if (fabsf(theta1 - theta2) >= 2 * M_PI) { - theta1 = 0; - theta2 = 2 * M_PI; +void lovrGraphicsArc(DrawStyle style, ArcMode mode, Material* material, mat4 transform, float r1, float r2, int segments) { + if (fabsf(r1 - r2) >= 2 * M_PI) { + r1 = 0; + r2 = 2 * M_PI; } - bool hasCenterPoint = arcMode == ARC_MODE_PIE && fabsf(theta1 - theta2) < 2 * M_PI; - uint32_t count = segments + 1 + hasCenterPoint; - float* vertices = lovrGraphicsGetVertexPointer(count); - - if (hasCenterPoint) { - memcpy(vertices, ((float[]) { 0, 0, 0, 0, 0, 1, .5, .5 }), 8 * sizeof(float)); - vertices += 8; - } - - float theta = theta1; - float angleShift = (theta2 - theta1) / (float) segments; - - for (int i = 0; i <= segments; i++) { - float x = cos(theta) * .5; - float y = sin(theta) * .5; - memcpy(vertices, ((float[]) { x, y, 0, 0, 0, 1, x + .5, 1 - (y + .5) }), 8 * sizeof(float)); - vertices += 8; - theta += angleShift; - } - - lovrGraphicsDraw(&(DrawRequest) { - .transform = transform, + lovrGraphicsBatch(&(BatchRequest) { + .type = BATCH_ARC, + .params.arc.r1 = r1, + .params.arc.r2 = r2, + .params.arc.mode = mode, + .params.arc.style = style, + .params.arc.segments = segments, .material = material, - .mode = style == STYLE_LINE ? (arcMode == ARC_MODE_OPEN ? DRAW_LINE_STRIP : DRAW_LINE_LOOP) : DRAW_TRIANGLE_FAN, - .vertex.count = count + .transform = transform }); } @@ -720,6 +1146,7 @@ void lovrGraphicsCircle(DrawStyle style, Material* material, mat4 transform, int } void lovrGraphicsCylinder(Material* material, float x1, float y1, float z1, float x2, float y2, float z2, float r1, float r2, bool capped, int segments) { + /* float axis[3] = { x1 - x2, y1 - y2, z1 - z2 }; float n[3] = { x1 - x2, y1 - y2, z1 - z2 }; float p[3]; @@ -727,10 +1154,11 @@ void lovrGraphicsCylinder(Material* material, float x1, float y1, float z1, floa uint32_t vertexCount = ((capped && r1) * (segments + 2) + (capped && r2) * (segments + 2) + 2 * (segments + 1)); uint32_t indexCount = 3 * segments * ((capped && r1) + (capped && r2) + 2); + float* vertices = lovrGraphicsMapBuffer(STREAM_VERTEX, vertexCount); + uint16_t* indices = lovrGraphicsMapBuffer(STREAM_INDEX, indexCount); + uint16_t baseVertex = (uint16_t) state.buffers[STREAM_VERTEX].cursor; - float* vertices = lovrGraphicsGetVertexPointer(vertexCount); - uint16_t* indices = lovrGraphicsGetIndexPointer(indexCount); - float* baseVertex = vertices; + float* v = vertices; vec3_init(p, n); @@ -773,28 +1201,28 @@ void lovrGraphicsCylinder(Material* material, float x1, float y1, float z1, floa } // Top - int topOffset = (segments + 1) * 2 + state.vertexCursor; + int topOffset = (segments + 1) * 2 + baseVertex; if (capped && r1 != 0) { PUSH_CYLINDER_VERTEX(x1, y1, z1, axis[0], axis[1], axis[2]); for (int i = 0; i <= segments; i++) { int j = i * 2 * 8; - PUSH_CYLINDER_VERTEX(baseVertex[j + 0], baseVertex[j + 1], baseVertex[j + 2], axis[0], axis[1], axis[2]); + PUSH_CYLINDER_VERTEX(v[j + 0], v[j + 1], v[j + 2], axis[0], axis[1], axis[2]); } } // Bottom - int bottomOffset = (segments + 1) * 2 + (1 + segments + 1) * (capped && r1 != 0) + state.vertexCursor; + int bottomOffset = (segments + 1) * 2 + (1 + segments + 1) * (capped && r1 != 0) + baseVertex; if (capped && r2 != 0) { PUSH_CYLINDER_VERTEX(x2, y2, z1, -axis[0], -axis[1], -axis[2]); for (int i = 0; i <= segments; i++) { int j = i * 2 * 8 + 8; - PUSH_CYLINDER_VERTEX(baseVertex[j + 0], baseVertex[j + 1], baseVertex[j + 2], -axis[0], -axis[1], -axis[2]); + PUSH_CYLINDER_VERTEX(v[j + 0], v[j + 1], v[j + 2], -axis[0], -axis[1], -axis[2]); } } // Indices for (int i = 0; i < segments; i++) { - int j = 2 * i + state.vertexCursor; + int j = 2 * i + baseVertex; PUSH_CYLINDER_TRIANGLE(j, j + 1, j + 2); PUSH_CYLINDER_TRIANGLE(j + 1, j + 3, j + 2); @@ -812,47 +1240,18 @@ void lovrGraphicsCylinder(Material* material, float x1, float y1, float z1, floa lovrGraphicsDraw(&(DrawRequest) { .material = material, .mode = DRAW_TRIANGLES, - .vertex.count = vertexCount, - .index.count = indexCount + .vertexCount = vertexCount, + .indexCount = indexCount }); + */ } void lovrGraphicsSphere(Material* material, mat4 transform, int segments) { - uint32_t vertexCount = (segments + 1) * (segments + 1); - uint32_t indexCount = segments * segments * 6; - float* vertices = lovrGraphicsGetVertexPointer(vertexCount); - uint16_t* indices = lovrGraphicsGetIndexPointer(indexCount); - - for (int i = 0; i <= segments; i++) { - float v = i / (float) segments; - for (int j = 0; j <= segments; j++) { - float u = j / (float) segments; - - float x = sin(u * 2 * M_PI) * sin(v * M_PI); - float y = cos(v * M_PI); - float z = -cos(u * 2 * M_PI) * sin(v * M_PI); - memcpy(vertices, ((float[]) { x, y, z, x, y, z, u, 1 - v }), 8 * sizeof(float)); - vertices += 8; - } - } - - for (int i = 0; i < segments; i++) { - uint16_t offset0 = i * (segments + 1); - uint16_t offset1 = (i + 1) * (segments + 1); - for (int j = 0; j < segments; j++) { - uint16_t i0 = offset0 + j + state.vertexCursor; - uint16_t i1 = offset1 + j + state.vertexCursor; - memcpy(indices, ((uint16_t[]) { i0, i1, i0 + 1, i1, i1 + 1, i0 + 1 }), 6 * sizeof(uint16_t)); - indices += 6; - } - } - - lovrGraphicsDraw(&(DrawRequest) { - .transform = transform, + lovrGraphicsBatch(&(BatchRequest) { + .type = BATCH_SPHERE, + .params.sphere.segments = segments, .material = material, - .mode = DRAW_TRIANGLES, - .vertex.count = vertexCount, - .index.count = indexCount + .transform = transform }); } @@ -863,45 +1262,51 @@ void lovrGraphicsSkybox(Texture* texture, float angle, float ax, float ay, float Pipeline pipeline = state.pipeline; pipeline.winding = WINDING_COUNTERCLOCKWISE; - lovrGraphicsDraw(&(DrawRequest) { + float transform[16] = MAT4_IDENTITY; + mat4_rotate(transform, angle, ax, ay, az); + + lovrGraphicsBatch(&(BatchRequest) { + .type = BATCH_SKYBOX, .shader = type == TEXTURE_CUBE ? SHADER_CUBE : SHADER_PANO, - .diffuseTexture = type == TEXTURE_2D ? texture : NULL, - .environmentMap = type == TEXTURE_CUBE ? texture : NULL, .pipeline = &pipeline, - .mode = DRAW_TRIANGLE_STRIP, - .vertex.count = 4, - .vertex.data = (float[]) { - -1, 1, 1, 0, 0, 0, 0, 0, - -1, -1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, 0, - 1, -1, 1, 0, 0, 0, 0, 0 - } + .transform = transform, + .diffuseTexture = type == TEXTURE_2D ? texture : NULL, + .environmentMap = type == TEXTURE_CUBE ? texture : NULL }); } void lovrGraphicsPrint(const char* str, size_t length, mat4 transform, float wrap, HorizontalAlign halign, VerticalAlign valign) { + float width; + uint32_t lineCount; + uint32_t glyphCount; Font* font = lovrGraphicsGetFont(); - float scale = 1 / font->pixelDensity; - float offsety; - uint32_t vertexCount; - uint32_t maxVertices = length * 6; - float* vertices = lovrGraphicsGetVertexPointer(maxVertices); - lovrFontRender(font, str, length, wrap, halign, valign, vertices, &offsety, &vertexCount); + lovrFontMeasure(font, str, length, wrap, &width, &lineCount, &glyphCount); + + float scale = 1.f / font->pixelDensity; + float offsetY = (lineCount * font->rasterizer->height * font->lineHeight) * (valign / 2.f); + mat4_scale(transform, scale, scale, scale); + mat4_translate(transform, 0.f, offsetY, 0.f); Pipeline pipeline = state.pipeline; - pipeline.alphaSampling = true; + pipeline.blendMode = pipeline.blendMode == BLEND_NONE ? BLEND_ALPHA : pipeline.blendMode; - mat4_scale(transform, scale, scale, scale); - mat4_translate(transform, 0.f, offsety, 0.f); - - lovrGraphicsDraw(&(DrawRequest) { + float* vertices; + uint16_t* indices; + uint16_t baseVertex; + lovrGraphicsBatch(&(BatchRequest) { + .type = BATCH_TEXT, .shader = SHADER_FONT, - .diffuseTexture = font->texture, .pipeline = &pipeline, .transform = transform, - .mode = DRAW_TRIANGLES, - .vertex.count = vertexCount + .diffuseTexture = font->texture, + .vertexCount = glyphCount * 4, + .indexCount = glyphCount * 6, + .vertices = &vertices, + .indices = &indices, + .baseVertex = &baseVertex }); + + lovrFontRender(font, str, length, wrap, halign, vertices, indices, baseVertex); } void lovrGraphicsFill(Texture* texture, float u, float v, float w, float h) { @@ -909,18 +1314,11 @@ void lovrGraphicsFill(Texture* texture, float u, float v, float w, float h) { pipeline.depthTest = COMPARE_NONE; pipeline.depthWrite = false; - lovrGraphicsDraw(&(DrawRequest) { - .mono = true, + lovrGraphicsBatch(&(BatchRequest) { + .type = BATCH_FILL, + .params.fill = { .u = u, .v = v, .w = w, .h = h }, .shader = SHADER_FILL, - .diffuseTexture = texture, .pipeline = &pipeline, - .mode = DRAW_TRIANGLE_STRIP, - .vertex.count = 4, - .vertex.data = (float[]) { - -1, 1, 0, 0, 0, 0, u, v + h, - -1, -1, 0, 0, 0, 0, u, v, - 1, 1, 0, 0, 0, 0, u + w, v + h, - 1, -1, 0, 0, 0, 0, u + w, v - } + .diffuseTexture = texture }); } diff --git a/src/graphics/graphics.h b/src/graphics/graphics.h index 9e7de846..058da929 100644 --- a/src/graphics/graphics.h +++ b/src/graphics/graphics.h @@ -13,9 +13,8 @@ #pragma once #define MAX_TRANSFORMS 64 -#define MAX_VERTICES (1 << 16) -#define MAX_INDICES (1 << 16) -#define MAX_BATCH_SIZE 192 // Enough to fit in any UBO +#define MAX_BATCHES 16 +#define MAX_LOCKS 4 typedef void (*StencilCallback)(void* userdata); @@ -33,7 +32,7 @@ typedef enum { BLEND_LIGHTEN, BLEND_DARKEN, BLEND_SCREEN, - BLEND_REPLACE + BLEND_NONE } BlendMode; typedef enum { @@ -70,27 +69,6 @@ typedef enum { WINDING_COUNTERCLOCKWISE } Winding; -typedef struct { - bool computeShaders; - bool singlepass; -} GpuFeatures; - -typedef struct { - bool initialized; - float pointSizes[2]; - int textureSize; - int textureMSAA; - float textureAnisotropy; - int blockSize; -} GpuLimits; - -typedef struct { - int shaderSwitches; - int drawCalls; -} GpuStats; - -// Internal - typedef struct { bool stereo; Canvas* canvas; @@ -99,34 +77,87 @@ typedef struct { } Camera; typedef struct { - BlendMode blendMode : 3; - BlendAlphaMode blendAlphaMode : 1; - CompareMode depthTest : 3; + float transform[16]; + Color color; +} DrawData; + +typedef struct { + bool alphaSampling : 1; + uint8_t blendMode : 3; // BlendMode + uint8_t blendAlphaMode : 1; // BlendAlphaMode + bool culling : 1; + uint8_t depthTest : 3; // CompareMode bool depthWrite : 1; uint8_t lineWidth : 8; uint8_t stencilValue: 8; - CompareMode stencilMode : 3; - bool alphaSampling : 1; - bool culling : 1; - Winding winding : 1; + uint8_t stencilMode : 3; // CompareMode + uint8_t winding : 1; // Winding bool wireframe : 1; } Pipeline; +typedef enum { + STREAM_VERTEX, + STREAM_INDEX, + STREAM_DRAW_ID, + STREAM_DRAW_DATA, + MAX_BUFFER_ROLES +} BufferRole; + +typedef enum { + BATCH_POINTS, + BATCH_LINES, + BATCH_TRIANGLES, + BATCH_PLANE, + BATCH_BOX, + BATCH_ARC, + BATCH_SPHERE, + BATCH_SKYBOX, + BATCH_TEXT, + BATCH_FILL, + BATCH_MESH +} BatchType; + +typedef union { + struct { DrawStyle style; } triangles; + struct { DrawStyle style; } plane; + struct { DrawStyle style; } box; + struct { DrawStyle style; ArcMode mode; float r1; float r2; int segments; } arc; + struct { int segments; } sphere; + struct { float u; float v; float w; float h; } fill; + struct { Mesh* object; DrawMode mode; uint32_t rangeStart; uint32_t rangeCount; uint32_t instances; float* pose; } mesh; +} BatchParams; + typedef struct { - Mesh* mesh; - DrawMode mode; - struct { uint32_t count; float* data; } vertex; - struct { uint32_t count; uint16_t* data; } index; + BatchType type; + BatchParams params; DefaultShader shader; + Pipeline* pipeline; + Material* material; Texture* diffuseTexture; Texture* environmentMap; - Material* material; - Pipeline* pipeline; mat4 transform; - float* pose; - int instances; - bool mono; -} DrawRequest; + uint32_t vertexCount; + uint32_t indexCount; + float** vertices; + uint16_t** indices; + uint16_t* baseVertex; +} BatchRequest; + +typedef struct { + BatchType type; + BatchParams params; + Canvas* canvas; + Shader* shader; + Pipeline pipeline; + Material* material; + uint32_t vertexStart; + uint32_t vertexCount; + uint32_t indexStart; + uint32_t indexCount; + uint32_t drawStart; + uint32_t drawCount; + DrawData* drawData; +} Batch; typedef struct { bool initialized; @@ -137,7 +168,6 @@ typedef struct { Shader* defaultShaders[MAX_DEFAULT_SHADERS]; Material* defaultMaterial; Font* defaultFont; - Mesh* defaultMesh; TextureFilter defaultFilter; float transforms[MAX_TRANSFORMS][16]; int transform; @@ -148,15 +178,15 @@ typedef struct { Pipeline pipeline; float pointSize; Shader* shader; - DrawRequest batch; - int batchVertex; - int batchIndex; - int batchSize; - int vertexCursor; - int indexCursor; - ShaderBlock* block; - Buffer* vertexMap; + uint32_t maxDraws; + Mesh* mesh; + Mesh* instancedMesh; Buffer* identityBuffer; + Buffer* buffers[MAX_BUFFER_ROLES]; + size_t cursors[MAX_BUFFER_ROLES]; + void* locks[MAX_BUFFER_ROLES][MAX_LOCKS]; + Batch batches[MAX_BATCHES]; + uint8_t batchCount; } GraphicsState; // Base @@ -167,6 +197,7 @@ void lovrGraphicsSetWindow(WindowFlags* flags); int lovrGraphicsGetWidth(); int lovrGraphicsGetHeight(); void lovrGraphicsSetCamera(Camera* camera, bool clear); +void* lovrGraphicsMapBuffer(BufferRole role, uint32_t count); Buffer* lovrGraphicsGetIdentityBuffer(); #define lovrGraphicsGetSupported lovrGpuGetSupported #define lovrGraphicsGetLimits lovrGpuGetLimits @@ -217,18 +248,20 @@ void lovrGraphicsMatrixTransform(mat4 transform); void lovrGraphicsSetProjection(mat4 projection); // Rendering -float* lovrGraphicsGetVertexPointer(uint32_t count); -uint16_t* lovrGraphicsGetIndexPointer(uint32_t count); void lovrGraphicsClear(Color* color, float* depth, int* stencil); void lovrGraphicsDiscard(bool color, bool depth, bool stencil); +void lovrGraphicsBatch(BatchRequest* req); void lovrGraphicsFlush(); -void lovrGraphicsDraw(DrawRequest* draw); -void lovrGraphicsPoints(uint32_t count); -void lovrGraphicsLine(uint32_t count); -void lovrGraphicsTriangle(DrawStyle style, Material* material, float points[9]); +void lovrGraphicsFlushCanvas(Canvas* canvas); +void lovrGraphicsFlushShader(Shader* shader); +void lovrGraphicsFlushMaterial(Material* material); +void lovrGraphicsFlushMesh(Mesh* mesh); +void lovrGraphicsPoints(uint32_t count, float** vertices); +void lovrGraphicsLine(uint32_t count, float** vertices); +void lovrGraphicsTriangle(DrawStyle style, Material* material, uint32_t count, float** vertices); void lovrGraphicsPlane(DrawStyle style, Material* material, mat4 transform); void lovrGraphicsBox(DrawStyle style, Material* material, mat4 transform); -void lovrGraphicsArc(DrawStyle style, ArcMode, Material* material, mat4 transform, float theta1, float theta2, int segments); +void lovrGraphicsArc(DrawStyle style, ArcMode mode, Material* material, mat4 transform, float r1, float r2, int segments); void lovrGraphicsCircle(DrawStyle style, Material* material, mat4 transform, int segments); void lovrGraphicsCylinder(Material* material, float x1, float y1, float z1, float x2, float y2, float z2, float r1, float r2, bool capped, int segments); void lovrGraphicsSphere(Material* material, mat4 transform, int segments); @@ -238,14 +271,37 @@ void lovrGraphicsFill(Texture* texture, float u, float v, float w, float h); #define lovrGraphicsStencil lovrGpuStencil #define lovrGraphicsCompute lovrGpuCompute -// GPU API +// GPU + +typedef struct { + bool computeShaders; + bool singlepass; +} GpuFeatures; + +typedef struct { + bool initialized; + float pointSizes[2]; + int textureSize; + int textureMSAA; + float textureAnisotropy; + int blockSize; + int blockAlign; +} GpuLimits; + +typedef struct { + int shaderSwitches; + int drawCalls; +} GpuStats; typedef struct { Mesh* mesh; - Shader* shader; Canvas* canvas; + Shader* shader; Pipeline pipeline; + DrawMode drawMode; uint32_t instances; + uint32_t rangeStart; + uint32_t rangeCount; uint32_t width : 15; uint32_t height : 15; bool stereo : 1; @@ -256,10 +312,13 @@ void lovrGpuDestroy(); void lovrGpuClear(Canvas* canvas, Color* color, float* depth, int* stencil); void lovrGpuCompute(Shader* shader, int x, int y, int z); void lovrGpuDiscard(Canvas* canvas, bool color, bool depth, bool stencil); -void lovrGpuDraw(DrawCommand* commands, int count); +void lovrGpuDraw(DrawCommand* draw); void lovrGpuStencil(StencilAction action, int replaceValue, StencilCallback callback, void* userdata); void lovrGpuPresent(); void lovrGpuDirtyTexture(); +void* lovrGpuLock(); +void lovrGpuUnlock(void* lock); +void lovrGpuDestroyLock(void* lock); const GpuFeatures* lovrGpuGetSupported(); const GpuLimits* lovrGpuGetLimits(); const GpuStats* lovrGpuGetStats(); diff --git a/src/graphics/material.c b/src/graphics/material.c index 2adc8dbb..bfd7fb51 100644 --- a/src/graphics/material.c +++ b/src/graphics/material.c @@ -1,4 +1,5 @@ #include "graphics/material.h" +#include "graphics/graphics.h" #include "resources/shaders.h" #include #include @@ -41,12 +42,6 @@ void lovrMaterialBind(Material* material, Shader* shader) { } lovrShaderSetMatrices(shader, "lovrMaterialTransform", material->transform, 0, 9); - - material->dirty = false; -} - -bool lovrMaterialIsDirty(Material* material) { - return material->dirty; } float lovrMaterialGetScalar(Material* material, MaterialScalar scalarType) { @@ -55,8 +50,8 @@ float lovrMaterialGetScalar(Material* material, MaterialScalar scalarType) { void lovrMaterialSetScalar(Material* material, MaterialScalar scalarType, float value) { if (material->scalars[scalarType] != value) { + lovrGraphicsFlushMaterial(material); material->scalars[scalarType] = value; - material->dirty = true; } } @@ -66,8 +61,8 @@ Color lovrMaterialGetColor(Material* material, MaterialColor colorType) { void lovrMaterialSetColor(Material* material, MaterialColor colorType, Color color) { if (memcmp(&material->colors[colorType], &color, 4 * sizeof(float))) { + lovrGraphicsFlushMaterial(material); material->colors[colorType] = color; - material->dirty = true; } } @@ -77,10 +72,10 @@ Texture* lovrMaterialGetTexture(Material* material, MaterialTexture textureType) void lovrMaterialSetTexture(Material* material, MaterialTexture textureType, Texture* texture) { if (material->textures[textureType] != texture) { + lovrGraphicsFlushMaterial(material); lovrRetain(texture); lovrRelease(material->textures[textureType]); material->textures[textureType] = texture; - material->dirty = true; } } @@ -93,6 +88,7 @@ void lovrMaterialGetTransform(Material* material, float* ox, float* oy, float* s } void lovrMaterialSetTransform(Material* material, float ox, float oy, float sx, float sy, float angle) { + lovrGraphicsFlushMaterial(material); float c = cos(angle); float s = sin(angle); material->transform[0] = c * sx; @@ -104,5 +100,4 @@ void lovrMaterialSetTransform(Material* material, float ox, float oy, float sx, material->transform[6] = ox; material->transform[7] = oy; material->transform[8] = 1; - material->dirty = true; } diff --git a/src/graphics/material.h b/src/graphics/material.h index 06130048..27727576 100644 --- a/src/graphics/material.h +++ b/src/graphics/material.h @@ -34,14 +34,12 @@ typedef struct { Color colors[MAX_MATERIAL_COLORS]; Texture* textures[MAX_MATERIAL_TEXTURES]; float transform[9]; - bool dirty; } Material; Material* lovrMaterialInit(Material* material); #define lovrMaterialCreate() lovrMaterialInit(lovrAlloc(Material)) void lovrMaterialDestroy(void* ref); void lovrMaterialBind(Material* material, Shader* shader); -bool lovrMaterialIsDirty(Material* material); float lovrMaterialGetScalar(Material* material, MaterialScalar scalarType); void lovrMaterialSetScalar(Material* material, MaterialScalar scalarType, float value); Color lovrMaterialGetColor(Material* material, MaterialColor colorType); diff --git a/src/graphics/mesh.c b/src/graphics/mesh.c index d817400a..3889805d 100644 --- a/src/graphics/mesh.c +++ b/src/graphics/mesh.c @@ -1,8 +1,50 @@ #include "graphics/mesh.h" +#include "graphics/graphics.h" + +VertexFormat* lovrMeshGetVertexFormat(Mesh* mesh) { + return &mesh->format; +} + +Buffer* lovrMeshGetVertexBuffer(Mesh* mesh) { + return mesh->vertexBuffer; +} + +Buffer* lovrMeshGetIndexBuffer(Mesh* mesh) { + return mesh->indexBuffer; +} + +void lovrMeshSetIndexBuffer(Mesh* mesh, Buffer* buffer, uint32_t indexCount, size_t indexSize) { + if (mesh->indexBuffer != buffer || mesh->indexCount != indexCount || mesh->indexSize != indexSize) { + lovrGraphicsFlushMesh(mesh); + lovrRetain(buffer); + lovrRelease(mesh->indexBuffer); + mesh->indexBuffer = buffer; + mesh->indexCount = indexCount; + mesh->indexSize = indexSize; + } +} + +uint32_t lovrMeshGetVertexCount(Mesh* mesh) { + return mesh->vertexCount; +} + +uint32_t lovrMeshGetIndexCount(Mesh* mesh) { + return mesh->indexCount; +} + +size_t lovrMeshGetIndexSize(Mesh* mesh) { + return mesh->indexSize; +} + +void lovrMeshMarkVertices(Mesh* mesh, size_t start, size_t end) { + mesh->flushStart = MIN(mesh->flushStart, start); + mesh->flushEnd = MAX(mesh->flushEnd, end); +} void lovrMeshAttachAttribute(Mesh* mesh, const char* name, MeshAttribute* attribute) { lovrAssert(!map_get(&mesh->attributes, name), "Mesh already has an attribute named '%s'", name); lovrAssert(attribute->divisor >= 0, "Divisor can't be negative"); + lovrGraphicsFlushMesh(mesh); map_set(&mesh->attributes, name, *attribute); lovrRetain(attribute->buffer); } @@ -10,7 +52,8 @@ void lovrMeshAttachAttribute(Mesh* mesh, const char* name, MeshAttribute* attrib void lovrMeshDetachAttribute(Mesh* mesh, const char* name) { MeshAttribute* attribute = map_get(&mesh->attributes, name); lovrAssert(attribute, "No attached attribute '%s' was found", name); - lovrAssert(attribute->buffer != mesh->vbo, "Attribute '%s' was not attached from another Mesh", name); + lovrAssert(attribute->buffer != mesh->vertexBuffer, "Attribute '%s' was not attached from another Mesh", name); + lovrGraphicsFlushMesh(mesh); lovrRelease(attribute->buffer); map_remove(&mesh->attributes, name); } @@ -19,26 +62,6 @@ MeshAttribute* lovrMeshGetAttribute(Mesh* mesh, const char* name) { return map_get(&mesh->attributes, name); } -VertexFormat* lovrMeshGetVertexFormat(Mesh* mesh) { - return &mesh->format; -} - -bool lovrMeshIsReadable(Mesh* mesh) { - return mesh->readable; -} - -DrawMode lovrMeshGetDrawMode(Mesh* mesh) { - return mesh->mode; -} - -void lovrMeshSetDrawMode(Mesh* mesh, DrawMode mode) { - mesh->mode = mode; -} - -int lovrMeshGetVertexCount(Mesh* mesh) { - return mesh->count; -} - bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name) { MeshAttribute* attribute = map_get(&mesh->attributes, name); lovrAssert(attribute, "Mesh does not have an attribute named '%s'", name); @@ -48,19 +71,30 @@ bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name) { void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enable) { MeshAttribute* attribute = map_get(&mesh->attributes, name); lovrAssert(attribute, "Mesh does not have an attribute named '%s'", name); - attribute->enabled = enable; + if (attribute->enabled != enable) { + lovrGraphicsFlushMesh(mesh); + attribute->enabled = enable; + } +} + +DrawMode lovrMeshGetDrawMode(Mesh* mesh) { + return mesh->mode; +} + +void lovrMeshSetDrawMode(Mesh* mesh, DrawMode mode) { + mesh->mode = mode; } void lovrMeshGetDrawRange(Mesh* mesh, uint32_t* start, uint32_t* count) { - *start = mesh->rangeStart; - *count = mesh->rangeCount; + *start = mesh->drawStart; + *count = mesh->drawCount; } void lovrMeshSetDrawRange(Mesh* mesh, uint32_t start, uint32_t count) { - uint32_t limit = mesh->indexCount > 0 ? mesh->indexCount : mesh->count; + uint32_t limit = mesh->indexSize > 0 ? mesh->indexCount : mesh->vertexCount; lovrAssert(start + count <= limit, "Invalid mesh draw range [%d, %d]", start + 1, start + count + 1); - mesh->rangeStart = start; - mesh->rangeCount = count; + mesh->drawStart = start; + mesh->drawCount = count; } Material* lovrMeshGetMaterial(Mesh* mesh) { @@ -72,38 +106,3 @@ void lovrMeshSetMaterial(Mesh* mesh, Material* material) { lovrRelease(mesh->material); mesh->material = material; } - -void* lovrMeshMapVertices(Mesh* mesh, size_t offset) { - return lovrBufferMap(mesh->vbo, offset); -} - -void lovrMeshFlushVertices(Mesh* mesh, size_t offset, size_t size) { - lovrBufferFlush(mesh->vbo, offset, size); -} - -void* lovrMeshMapIndices(Mesh* mesh, uint32_t count, size_t indexSize, size_t offset) { - mesh->indexSize = indexSize; - mesh->indexCount = count; - - if (count == 0) { - return NULL; - } - - if (mesh->indexCapacity < indexSize * count) { - mesh->indexCapacity = nextPo2(indexSize * count); - lovrRelease(mesh->ibo); - mesh->ibo = lovrBufferCreate(mesh->indexCapacity, NULL, mesh->usage, mesh->readable); - } - - return lovrBufferMap(mesh->ibo, offset); -} - -void lovrMeshFlushIndices(Mesh* mesh) { - if (mesh->indexCount > 0) { - lovrBufferFlush(mesh->ibo, 0, mesh->indexCount * mesh->indexSize); - } -} - -void* lovrMeshReadIndices(Mesh* mesh, uint32_t* count, size_t* indexSize) { - return *count = mesh->indexCount, *indexSize = mesh->indexSize, lovrBufferMap(mesh->ibo, 0); -} diff --git a/src/graphics/mesh.h b/src/graphics/mesh.h index eacc19a4..b806bc87 100644 --- a/src/graphics/mesh.h +++ b/src/graphics/mesh.h @@ -34,43 +34,42 @@ typedef enum { typedef struct { Ref ref; - uint32_t count; DrawMode mode; VertexFormat format; - bool readable; - BufferUsage usage; - Buffer* vbo; - Buffer* ibo; + Buffer* vertexBuffer; + Buffer* indexBuffer; + uint32_t vertexCount; uint32_t indexCount; size_t indexSize; - size_t indexCapacity; - uint32_t rangeStart; - uint32_t rangeCount; + size_t flushStart; + size_t flushEnd; + uint32_t drawStart; + uint32_t drawCount; Material* material; map_attribute_t attributes; MeshAttribute layout[MAX_ATTRIBUTES]; GPU_MESH_FIELDS } Mesh; -Mesh* lovrMeshInit(Mesh* mesh, uint32_t count, VertexFormat format, DrawMode drawMode, BufferUsage usage, bool readable); +Mesh* lovrMeshInit(Mesh* mesh, DrawMode mode, VertexFormat format, Buffer* vertexBuffer); #define lovrMeshCreate(...) lovrMeshInit(lovrAlloc(Mesh), __VA_ARGS__) void lovrMeshDestroy(void* ref); +VertexFormat* lovrMeshGetVertexFormat(Mesh* mesh); +Buffer* lovrMeshGetVertexBuffer(Mesh* mesh); +Buffer* lovrMeshGetIndexBuffer(Mesh* mesh); +void lovrMeshSetIndexBuffer(Mesh* mesh, Buffer* buffer, uint32_t indexCount, size_t indexSize); +uint32_t lovrMeshGetVertexCount(Mesh* mesh); +uint32_t lovrMeshGetIndexCount(Mesh* mesh); +size_t lovrMeshGetIndexSize(Mesh* mesh); +void lovrMeshMarkVertices(Mesh* mesh, size_t start, size_t end); void lovrMeshAttachAttribute(Mesh* mesh, const char* name, MeshAttribute* attribute); void lovrMeshDetachAttribute(Mesh* mesh, const char* name); MeshAttribute* lovrMeshGetAttribute(Mesh* mesh, const char* name); -VertexFormat* lovrMeshGetVertexFormat(Mesh* mesh); -bool lovrMeshIsReadable(Mesh* mesh); -DrawMode lovrMeshGetDrawMode(Mesh* mesh); -void lovrMeshSetDrawMode(Mesh* mesh, DrawMode mode); -int lovrMeshGetVertexCount(Mesh* mesh); bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name); void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enabled); +DrawMode lovrMeshGetDrawMode(Mesh* mesh); +void lovrMeshSetDrawMode(Mesh* mesh, DrawMode mode); void lovrMeshGetDrawRange(Mesh* mesh, uint32_t* start, uint32_t* count); void lovrMeshSetDrawRange(Mesh* mesh, uint32_t start, uint32_t count); Material* lovrMeshGetMaterial(Mesh* mesh); void lovrMeshSetMaterial(Mesh* mesh, Material* material); -void* lovrMeshMapVertices(Mesh* mesh, size_t offset); -void lovrMeshFlushVertices(Mesh* mesh, size_t offset, size_t size); -void* lovrMeshMapIndices(Mesh* mesh, uint32_t count, size_t indexSize, size_t offset); -void lovrMeshFlushIndices(Mesh* mesh); -void* lovrMeshReadIndices(Mesh* mesh, uint32_t* count, size_t* indexSize); diff --git a/src/graphics/model.c b/src/graphics/model.c index c45e90e5..2239a937 100644 --- a/src/graphics/model.c +++ b/src/graphics/model.c @@ -38,13 +38,18 @@ static void renderNode(Model* model, int nodeIndex, int instances) { lovrMeshSetMaterial(model->mesh, model->materials[primitive->material]); } - lovrMeshSetDrawRange(model->mesh, primitive->drawStart, primitive->drawCount); - lovrGraphicsDraw(&(DrawRequest) { + lovrGraphicsBatch(&(BatchRequest) { + .type = BATCH_MESH, + .params.mesh = { + .object = model->mesh, + .mode = DRAW_TRIANGLES, + .rangeStart = primitive->drawStart, + .rangeCount = primitive->drawCount, + .pose = (float*) model->pose, + .instances = instances + }, .transform = model->nodeTransforms[nodeIndex], - .mesh = model->mesh, - .material = lovrMeshGetMaterial(model->mesh), - .instances = instances, - .pose = (float*) model->pose + .material = lovrMeshGetMaterial(model->mesh) }); } } @@ -59,14 +64,26 @@ Model* lovrModelInit(Model* model, ModelData* modelData) { model->modelData = modelData; model->aabbDirty = true; - model->mesh = lovrMeshCreate(modelData->vertexData->count, modelData->vertexData->format, DRAW_TRIANGLES, USAGE_STATIC, false); - void* vertices = lovrMeshMapVertices(model->mesh, 0); - memcpy(vertices, modelData->vertexData->blob.data, modelData->vertexData->count * modelData->vertexData->format.stride); - lovrMeshFlushVertices(model->mesh, 0, modelData->vertexData->count * modelData->vertexData->format.stride); + VertexFormat* format = &modelData->vertexData->format; + size_t vboSize = modelData->vertexData->count * format->stride; + Buffer* vertexBuffer = lovrBufferCreate(vboSize, modelData->vertexData->blob.data, BUFFER_VERTEX, USAGE_STATIC, false); + model->mesh = lovrMeshCreate(DRAW_TRIANGLES, *format, vertexBuffer); + lovrRelease(vertexBuffer); - void* indices = lovrMeshMapIndices(model->mesh, modelData->indexCount, modelData->indexSize, 0); - memcpy(indices, modelData->indices.raw, modelData->indexCount * modelData->indexSize); - lovrMeshFlushIndices(model->mesh); + size_t indexSize = modelData->indexSize; + uint32_t indexCount = modelData->indexCount; + Buffer* indexBuffer = lovrBufferCreate(indexCount * indexSize, modelData->indices.raw, BUFFER_INDEX, USAGE_STATIC, false); + lovrMeshSetIndexBuffer(model->mesh, indexBuffer, indexCount, indexSize); + lovrRelease(indexBuffer); + + lovrMeshAttachAttribute(model->mesh, "lovrDrawID", &(MeshAttribute) { + .buffer = lovrGraphicsGetIdentityBuffer(), + .type = ATTR_BYTE, + .components = 1, + .divisor = 1, + .integer = true, + .enabled = true + }); if (modelData->textures.length > 0) { model->textures = calloc(modelData->textures.length, sizeof(Texture*)); @@ -178,7 +195,6 @@ void lovrModelDraw(Model* model, mat4 transform, int instances) { lovrGraphicsMatrixTransform(transform); renderNode(model, 0, instances); lovrGraphicsPop(); - lovrGraphicsFlush(); } Animator* lovrModelGetAnimator(Model* model) { diff --git a/src/graphics/opengl.c b/src/graphics/opengl.c index 3b3be705..af502ce9 100644 --- a/src/graphics/opengl.c +++ b/src/graphics/opengl.c @@ -28,6 +28,7 @@ #define LOVR_SHADER_TANGENT 4 #define LOVR_SHADER_BONES 5 #define LOVR_SHADER_BONE_WEIGHTS 6 +#define LOVR_SHADER_DRAW_ID 7 typedef enum { BARRIER_BLOCK, @@ -38,9 +39,16 @@ typedef enum { MAX_BARRIERS } Barrier; +typedef struct { + uint32_t buffer; + size_t offset; + size_t size; +} BlockBuffer; + static struct { Texture* defaultTexture; bool alphaToCoverage; + bool blendEnabled; BlendMode blendMode; BlendAlphaMode blendAlphaMode; bool culling; @@ -55,15 +63,13 @@ static struct { Winding winding; bool wireframe; uint32_t framebuffer; - uint32_t indexBuffer; uint32_t program; + uint32_t vertexArray; + uint32_t buffers[MAX_BUFFER_TYPES]; + BlockBuffer blockBuffers[2][MAX_BLOCK_BUFFERS]; int activeTexture; Texture* textures[MAX_TEXTURES]; Image images[MAX_IMAGES]; - uint32_t blockBuffers[2][MAX_BLOCK_BUFFERS]; - uint32_t vertexArray; - uint32_t vertexBuffer; - uint32_t genericBuffer; float viewports[2][4]; vec_void_t incoherents[MAX_BARRIERS]; bool srgb; @@ -188,6 +194,17 @@ static bool isTextureFormatDepth(TextureFormat format) { } } +static GLenum convertBufferType(BufferType type) { + switch (type) { + case BUFFER_VERTEX: return GL_ARRAY_BUFFER; + case BUFFER_INDEX: return GL_ELEMENT_ARRAY_BUFFER; + case BUFFER_UNIFORM: return GL_UNIFORM_BUFFER; + case BUFFER_SHADER_STORAGE: return GL_SHADER_STORAGE_BUFFER; + case BUFFER_GENERIC: return GL_COPY_WRITE_BUFFER; + default: lovrThrow("Unreachable"); return 0; + } +} + static GLenum convertBufferUsage(BufferUsage usage) { switch (usage) { case USAGE_STATIC: return GL_STATIC_DRAW; @@ -308,8 +325,8 @@ static void lovrGpuSync(uint8_t flags) { if (i == BARRIER_BLOCK) { for (int j = 0; j < state.incoherents[i].length; j++) { - ShaderBlock* block = state.incoherents[i].data[j]; - block->incoherent &= ~(1 << i); + Buffer* buffer = state.incoherents[i].data[j]; + buffer->incoherent &= ~(1 << i); } } else { for (int j = 0; j < state.incoherents[i].length; j++) { @@ -359,10 +376,43 @@ static void lovrGpuBindFramebuffer(uint32_t framebuffer) { } } -static void lovrGpuBindIndexBuffer(uint32_t indexBuffer) { - if (state.indexBuffer != indexBuffer) { - state.indexBuffer = indexBuffer; - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); +static void lovrGpuUseProgram(uint32_t program) { + if (state.program != program) { + state.program = program; + glUseProgram(program); + state.stats.shaderSwitches++; + } +} + +static void lovrGpuBindVertexArray(uint32_t vertexArray) { + if (state.vertexArray != vertexArray) { + state.vertexArray = vertexArray; + glBindVertexArray(vertexArray); + } +} + +static void lovrGpuBindBuffer(BufferType type, uint32_t buffer) { + if (state.buffers[type] != buffer) { + state.buffers[type] = buffer; + glBindBuffer(convertBufferType(type), buffer); + } +} + +static void lovrGpuBindBlockBuffer(BlockType type, uint32_t buffer, int slot, size_t offset, size_t size) { + lovrAssert(offset % state.limits.blockAlign == 0, "Block buffer offset must be aligned to %d", state.limits.blockAlign); +#ifdef EMSCRIPTEN + lovrAssert(type == BLOCK_UNIFORM, "Writable blocks are not supported on this system"); + GLenum target = GL_UNIFORM_BUFFER; +#else + GLenum target = type == BLOCK_UNIFORM ? GL_UNIFORM_BUFFER : GL_SHADER_STORAGE_BUFFER; +#endif + + BlockBuffer* block = &state.blockBuffers[type][slot]; + if (block->buffer != buffer || block->offset != offset || block->size != size) { + block->buffer = buffer; + block->offset = offset; + block->size = size; + glBindBufferRange(target, slot, buffer, offset, size); } } @@ -407,49 +457,6 @@ static void lovrGpuBindImage(Image* image, int slot) { } #endif -static void lovrGpuBindBlockBuffer(BlockType type, uint32_t buffer, int slot) { -#ifdef EMSCRIPTEN - lovrAssert(type == BLOCK_UNIFORM, "Writable ShaderBlocks are not supported on this system"); - GLenum target = GL_UNIFORM_BUFFER; -#else - GLenum target = type == BLOCK_UNIFORM ? GL_UNIFORM_BUFFER : GL_SHADER_STORAGE_BUFFER; -#endif - - if (state.blockBuffers[type][slot] != buffer) { - state.blockBuffers[type][slot] = buffer; - glBindBufferBase(target, slot, buffer); - } -} - -static void lovrGpuBindVertexArray(uint32_t vertexArray) { - if (state.vertexArray != vertexArray) { - state.vertexArray = vertexArray; - glBindVertexArray(vertexArray); - } -} - -static void lovrGpuBindVertexBuffer(uint32_t vertexBuffer) { - if (state.vertexBuffer != vertexBuffer) { - state.vertexBuffer = vertexBuffer; - glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); - } -} - -static void lovrGpuBindGenericBuffer(uint32_t buffer) { - if (state.genericBuffer != buffer) { - state.genericBuffer = buffer; - glBindBuffer(GL_COPY_WRITE_BUFFER, buffer); - } -} - -static void lovrGpuUseProgram(uint32_t program) { - if (state.program != program) { - state.program = program; - glUseProgram(program); - state.stats.shaderSwitches++; - } -} - static void lovrGpuBindMesh(Mesh* mesh, Shader* shader, int divisorMultiplier) { const char* key; map_iter_t iter = map_iter(&mesh->attachments); @@ -458,8 +465,16 @@ static void lovrGpuBindMesh(Mesh* mesh, Shader* shader, int divisorMultiplier) { memset(layout, 0, MAX_ATTRIBUTES * sizeof(MeshAttribute)); lovrGpuBindVertexArray(mesh->vao); - if (mesh->indexCount > 0) { - lovrGpuBindIndexBuffer(mesh->ibo->id); + + if (mesh->indexBuffer && mesh->indexCount > 0 && mesh->ibo != mesh->indexBuffer->id) { + mesh->ibo = mesh->indexBuffer->id; + state.buffers[BUFFER_INDEX] = mesh->ibo; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->ibo); + } + + if (mesh->flushEnd > 0) { + lovrBufferFlush(mesh->vertexBuffer, mesh->flushStart, mesh->flushEnd - mesh->flushStart); + mesh->flushStart = mesh->flushEnd = 0; } while ((key = map_next(&mesh->attributes, &iter)) != NULL) { @@ -501,7 +516,7 @@ static void lovrGpuBindMesh(Mesh* mesh, Shader* shader, int divisorMultiplier) { previous.stride != current.stride; if (changed) { - lovrGpuBindVertexBuffer(current.buffer->id); + lovrGpuBindBuffer(BUFFER_VERTEX, current.buffer->id); int count = current.components; int stride = current.stride; GLvoid* offset = (GLvoid*) current.offset; @@ -604,51 +619,60 @@ static void lovrGpuBindPipeline(Pipeline* pipeline) { state.blendMode = pipeline->blendMode; state.blendAlphaMode = pipeline->blendAlphaMode; - GLenum srcRGB = state.blendMode == BLEND_MULTIPLY ? GL_DST_COLOR : GL_ONE; - if (srcRGB == GL_ONE && state.blendAlphaMode == BLEND_ALPHA_MULTIPLY) { - srcRGB = GL_SRC_ALPHA; - } + if (state.blendMode == BLEND_NONE) { + if (state.blendEnabled) { + state.blendEnabled = false; + glDisable(GL_BLEND); + } + } else { + if (!state.blendEnabled) { + state.blendEnabled = true; + glEnable(GL_BLEND); + } - switch (state.blendMode) { - case BLEND_ALPHA: - glBlendEquation(GL_FUNC_ADD); - glBlendFuncSeparate(srcRGB, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - break; + GLenum srcRGB = state.blendMode == BLEND_MULTIPLY ? GL_DST_COLOR : GL_ONE; + if (srcRGB == GL_ONE && state.blendAlphaMode == BLEND_ALPHA_MULTIPLY) { + srcRGB = GL_SRC_ALPHA; + } - case BLEND_ADD: - glBlendEquation(GL_FUNC_ADD); - glBlendFuncSeparate(srcRGB, GL_ONE, GL_ZERO, GL_ONE); - break; + switch (state.blendMode) { + case BLEND_ALPHA: + glBlendEquation(GL_FUNC_ADD); + glBlendFuncSeparate(srcRGB, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + break; - case BLEND_SUBTRACT: - glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); - glBlendFuncSeparate(srcRGB, GL_ONE, GL_ZERO, GL_ONE); - break; + case BLEND_ADD: + glBlendEquation(GL_FUNC_ADD); + glBlendFuncSeparate(srcRGB, GL_ONE, GL_ZERO, GL_ONE); + break; - case BLEND_MULTIPLY: - glBlendEquation(GL_FUNC_ADD); - glBlendFuncSeparate(srcRGB, GL_ZERO, GL_DST_COLOR, GL_ZERO); - break; + case BLEND_SUBTRACT: + glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); + glBlendFuncSeparate(srcRGB, GL_ONE, GL_ZERO, GL_ONE); + break; - case BLEND_LIGHTEN: - glBlendEquation(GL_MAX); - glBlendFuncSeparate(srcRGB, GL_ZERO, GL_ONE, GL_ZERO); - break; + case BLEND_MULTIPLY: + glBlendEquation(GL_FUNC_ADD); + glBlendFuncSeparate(srcRGB, GL_ZERO, GL_DST_COLOR, GL_ZERO); + break; - case BLEND_DARKEN: - glBlendEquation(GL_MIN); - glBlendFuncSeparate(srcRGB, GL_ZERO, GL_ONE, GL_ZERO); - break; + case BLEND_LIGHTEN: + glBlendEquation(GL_MAX); + glBlendFuncSeparate(srcRGB, GL_ZERO, GL_ONE, GL_ZERO); + break; - case BLEND_SCREEN: - glBlendEquation(GL_FUNC_ADD); - glBlendFuncSeparate(srcRGB, GL_ONE_MINUS_SRC_COLOR, GL_ONE, GL_ONE_MINUS_SRC_COLOR); - break; + case BLEND_DARKEN: + glBlendEquation(GL_MIN); + glBlendFuncSeparate(srcRGB, GL_ZERO, GL_ONE, GL_ZERO); + break; - case BLEND_REPLACE: - glBlendEquation(GL_FUNC_ADD); - glBlendFuncSeparate(srcRGB, GL_ZERO, GL_ONE, GL_ZERO); - break; + case BLEND_SCREEN: + glBlendEquation(GL_FUNC_ADD); + glBlendFuncSeparate(srcRGB, GL_ONE_MINUS_SRC_COLOR, GL_ONE, GL_ONE_MINUS_SRC_COLOR); + break; + + case BLEND_NONE: lovrThrow("Unreachable"); break; + } } } @@ -678,8 +702,8 @@ static void lovrGpuBindPipeline(Pipeline* pipeline) { } // Depth write - if (state.depthWrite != pipeline->depthWrite) { - state.depthWrite = pipeline->depthWrite; + if (state.depthWrite != (pipeline->depthWrite && !state.stencilWriting)) { + state.depthWrite = pipeline->depthWrite && !state.stencilWriting; glDepthMask(state.depthWrite); } @@ -848,14 +872,14 @@ static void lovrGpuBindShader(Shader* shader) { for (BlockType type = BLOCK_UNIFORM; type <= BLOCK_STORAGE; type++) { vec_foreach_ptr(&shader->blocks[type], block, i) { if (block->source) { + if (type == BLOCK_STORAGE && block->access != ACCESS_READ) { + block->source->incoherent |= (1 << BARRIER_BLOCK); + vec_push(&state.incoherents[BARRIER_BLOCK], block->source); + } - // If the Shader can write to the block, mark it as incoherent - bool writable = type == BLOCK_STORAGE && block->access != ACCESS_READ; - block->source->incoherent |= writable ? (1 << BARRIER_BLOCK) : 0; - vec_push(&state.incoherents[BARRIER_BLOCK], block->source); - lovrGpuBindBlockBuffer(type, block->source->buffer->id, block->slot); + lovrGpuBindBlockBuffer(type, block->source->id, block->slot, block->offset, block->size); } else { - lovrGpuBindBlockBuffer(type, 0, block->slot); + lovrGpuBindBlockBuffer(type, 0, block->slot, 0, 0); } } } @@ -900,17 +924,19 @@ void lovrGpuInit(bool srgb, getProcAddressProc getProcAddress) { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &state.limits.textureSize); glGetIntegerv(GL_MAX_SAMPLES, &state.limits.textureMSAA); glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &state.limits.blockSize); + glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &state.limits.blockAlign); glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &state.limits.textureAnisotropy); - glEnable(GL_BLEND); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); state.srgb = srgb; state.alphaToCoverage = false; glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + state.blendEnabled = true; state.blendMode = BLEND_ALPHA; state.blendAlphaMode = BLEND_ALPHA_MULTIPLY; + glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); @@ -990,6 +1016,7 @@ void lovrGpuCompute(Shader* shader, int x, int y, int z) { #else lovrAssert(GLAD_GL_ARB_compute_shader, "Compute shaders are not supported on this system"); lovrAssert(shader->type == SHADER_COMPUTE, "Attempt to use a non-compute shader for a compute operation"); + lovrGraphicsFlush(); lovrGpuBindShader(shader); glDispatchCompute(x, y, z); #endif @@ -1021,52 +1048,45 @@ void lovrGpuDiscard(Canvas* canvas, bool color, bool depth, bool stencil) { #endif } -void lovrGpuDraw(DrawCommand* commands, int count) { - for (int i = 0; i < count; i++) { - DrawCommand* draw = &commands[i]; +void lovrGpuDraw(DrawCommand* draw) { + int viewCount = 1 + draw->stereo; + int drawCount = state.features.singlepass ? 1 : viewCount; + int viewsPerDraw = state.features.singlepass ? viewCount : 1; + int instances = MAX(draw->instances, 1) * viewsPerDraw; - int viewCount = 1 + draw->stereo; - int drawCount = state.features.singlepass ? 1 : viewCount; - int viewsPerDraw = state.features.singlepass ? viewCount : 1; - int instances = draw->instances * viewsPerDraw; + float w = draw->width / (float) viewCount; + float h = draw->height; + float viewports[2][4] = { { 0, 0, w, h }, { w, 0, w, h } }; + lovrShaderSetInts(draw->shader, "lovrViewportCount", &viewCount, 0, 1); - float w = draw->width / (float) viewCount; - float h = draw->height; - float viewports[2][4] = { { 0, 0, w, h }, { w, 0, w, h } }; - lovrShaderSetInts(draw->shader, "lovrViewportCount", &viewCount, 0, 1); + lovrGpuBindCanvas(draw->canvas, true); + lovrGpuBindPipeline(&draw->pipeline); + lovrGpuBindMesh(draw->mesh, draw->shader, viewsPerDraw); - lovrGpuBindMesh(draw->mesh, draw->shader, viewsPerDraw); - lovrGpuBindCanvas(draw->canvas, true); - lovrGpuBindPipeline(&draw->pipeline); + for (int i = 0; i < drawCount; i++) { + lovrGpuSetViewports(&viewports[i][0], viewsPerDraw); + lovrShaderSetInts(draw->shader, "lovrViewportIndex", &i, 0, 1); + lovrGpuBindShader(draw->shader); - for (int i = 0; i < drawCount; i++) { - lovrGpuSetViewports(&viewports[i][0], viewsPerDraw); - lovrShaderSetInts(draw->shader, "lovrViewportIndex", &i, 0, 1); - lovrGpuBindShader(draw->shader); - - Mesh* mesh = draw->mesh; - GLenum mode = convertDrawMode(mesh->mode); - if (mesh->indexCount > 0) { - size_t count = mesh->rangeCount ? mesh->rangeCount : mesh->indexCount; - GLenum indexType = mesh->indexSize == sizeof(uint16_t) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; - GLvoid* offset = (GLvoid*) (mesh->rangeStart * mesh->indexSize); - if (instances > 1) { - glDrawElementsInstanced(mode, count, indexType, offset, instances); - } else { - glDrawElements(mode, count, indexType, offset); - } + Mesh* mesh = draw->mesh; + GLenum mode = convertDrawMode(draw->drawMode); + if (mesh->indexCount > 0) { + GLenum indexType = mesh->indexSize == sizeof(uint16_t) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + GLvoid* offset = (GLvoid*) (draw->rangeStart * mesh->indexSize); + if (instances > 1) { + glDrawElementsInstanced(mode, draw->rangeCount, indexType, offset, instances); } else { - size_t start = mesh->rangeStart; - size_t count = mesh->rangeCount ? mesh->rangeCount : mesh->count; - if (instances > 1) { - glDrawArraysInstanced(mode, start, count, instances); - } else { - glDrawArrays(mode, start, count); - } + glDrawElements(mode, draw->rangeCount, indexType, offset); + } + } else { + if (instances > 1) { + glDrawArraysInstanced(mode, draw->rangeStart, draw->rangeCount, instances); + } else { + glDrawArrays(mode, draw->rangeStart, draw->rangeCount); } - - state.stats.drawCalls++; } + + state.stats.drawCalls++; } } @@ -1079,7 +1099,7 @@ void lovrGpuPresent() { } void lovrGpuStencil(StencilAction action, int replaceValue, StencilCallback callback, void* userdata) { - state.depthWrite = false; + lovrGraphicsFlush(); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); if (!state.stencilEnabled) { @@ -1102,6 +1122,7 @@ void lovrGpuStencil(StencilAction action, int replaceValue, StencilCallback call state.stencilWriting = true; callback(userdata); + lovrGraphicsFlush(); state.stencilWriting = false; glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); @@ -1112,6 +1133,24 @@ void lovrGpuDirtyTexture() { state.textures[state.activeTexture] = NULL; } +void* lovrGpuLock() { + return (void*) glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); +} + +void lovrGpuUnlock(void* lock) { + if (!lock) return; + GLsync sync = (GLsync) lock; + if (glClientWaitSync(sync, 0, 0) == GL_TIMEOUT_EXPIRED) { + while (glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1E9) == GL_TIMEOUT_EXPIRED) { + continue; + } + } +} + +void lovrGpuDestroyLock(void* lock) { + if (lock) glDeleteSync((GLsync) lock); +} + const GpuFeatures* lovrGpuGetSupported() { return &state.features; } @@ -1242,6 +1281,7 @@ void lovrTextureAllocate(Texture* texture, int width, int height, int depth, Tex } void lovrTextureReplacePixels(Texture* texture, TextureData* textureData, int x, int y, int slice, int mipmap) { + lovrGraphicsFlush(); lovrAssert(texture->allocated, "Texture is not allocated"); #ifndef EMSCRIPTEN @@ -1308,6 +1348,7 @@ void lovrTextureReplacePixels(Texture* texture, TextureData* textureData, int x, } void lovrTextureSetFilter(Texture* texture, TextureFilter filter) { + lovrGraphicsFlush(); float anisotropy = filter.mode == FILTER_ANISOTROPIC ? MAX(filter.anisotropy, 1.) : 1.; lovrGpuBindTexture(texture, 0); texture->filter = filter; @@ -1344,6 +1385,7 @@ void lovrTextureSetFilter(Texture* texture, TextureFilter filter) { } void lovrTextureSetWrap(Texture* texture, TextureWrap wrap) { + lovrGraphicsFlush(); texture->wrap = wrap; lovrGpuBindTexture(texture, 0); glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, convertWrapMode(wrap.s)); @@ -1416,7 +1458,7 @@ void lovrCanvasResolve(Canvas* canvas) { return; } - lovrGraphicsFlush(); + lovrGraphicsFlushCanvas(canvas); if (canvas->flags.msaa) { int w = canvas->width; @@ -1454,6 +1496,7 @@ void lovrCanvasResolve(Canvas* canvas) { } TextureData* lovrCanvasNewTextureData(Canvas* canvas, int index) { + lovrGraphicsFlushCanvas(canvas); lovrGpuBindCanvas(canvas, false); #ifndef EMSCRIPTEN @@ -1479,21 +1522,24 @@ TextureData* lovrCanvasNewTextureData(Canvas* canvas, int index) { // Buffer -Buffer* lovrBufferInit(Buffer* buffer, size_t size, void* data, BufferUsage usage, bool readable) { +Buffer* lovrBufferInit(Buffer* buffer, size_t size, void* data, BufferType type, BufferUsage usage, bool readable) { buffer->size = size; + buffer->readable = readable; + buffer->type = type; buffer->usage = usage; glGenBuffers(1, &buffer->id); - lovrGpuBindGenericBuffer(buffer->id); + lovrGpuBindBuffer(type, buffer->id); + GLenum glType = convertBufferType(type); #ifndef EMSCRIPTEN if (GLAD_GL_ARB_buffer_storage) { GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (readable ? GL_MAP_READ_BIT : 0); - glBufferStorage(GL_COPY_WRITE_BUFFER, size, data, flags); - buffer->data = glMapBufferRange(GL_COPY_WRITE_BUFFER, 0, size, flags | GL_MAP_FLUSH_EXPLICIT_BIT); + glBufferStorage(glType, size, data, flags); + buffer->data = glMapBufferRange(glType, 0, size, flags | GL_MAP_FLUSH_EXPLICIT_BIT); } else { #endif buffer->data = calloc(1, size); - glBufferData(GL_COPY_WRITE_BUFFER, size, data, convertBufferUsage(usage)); + glBufferData(glType, size, data, convertBufferUsage(usage)); if (data) { memcpy(buffer->data, data, size); @@ -1507,6 +1553,7 @@ Buffer* lovrBufferInit(Buffer* buffer, size_t size, void* data, BufferUsage usag void lovrBufferDestroy(void* ref) { Buffer* buffer = ref; + lovrGpuDestroySyncResource(buffer, buffer->incoherent); glDeleteBuffers(1, &buffer->id); #ifndef EMSCRIPTEN if (!GLAD_GL_ARB_buffer_storage) { @@ -1522,45 +1569,18 @@ void* lovrBufferMap(Buffer* buffer, size_t offset) { } void lovrBufferFlush(Buffer* buffer, size_t offset, size_t size) { - lovrGpuBindGenericBuffer(buffer->id); + lovrGpuBindBuffer(buffer->type, buffer->id); #ifndef EMSCRIPTEN if (GLAD_GL_ARB_buffer_storage) { - glFlushMappedBufferRange(GL_COPY_WRITE_BUFFER, offset, size); + glFlushMappedBufferRange(convertBufferType(buffer->type), offset, size); } else { #endif - glBufferSubData(GL_COPY_WRITE_BUFFER, offset, size, (GLvoid*) ((uint8_t*) buffer->data + offset)); + glBufferSubData(convertBufferType(buffer->type), offset, size, (GLvoid*) ((uint8_t*) buffer->data + offset)); #ifndef EMSCRIPTEN } #endif } -void lovrBufferLock(Buffer* buffer) { -#ifndef EMSCRIPTEN - if (!GLAD_GL_ARB_buffer_storage) { - return; - } - - lovrBufferUnlock(buffer); - buffer->lock = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); -#endif -} - -void lovrBufferUnlock(Buffer* buffer) { -#ifndef EMSCRIPTEN - if (!GLAD_GL_ARB_buffer_storage || !buffer->lock) { - return; - } - - if (glClientWaitSync(buffer->lock, 0, 0) == GL_TIMEOUT_EXPIRED) { - while (glClientWaitSync(buffer->lock, GL_SYNC_FLUSH_COMMANDS_BIT, 1E9) == GL_TIMEOUT_EXPIRED) { - continue; - } - } - glDeleteSync(buffer->lock); - buffer->lock = NULL; -#endif -} - // Shader static GLuint compileShader(GLenum type, const char** sources, int count) { @@ -1717,12 +1737,7 @@ static void lovrShaderSetupUniforms(Shader* shader) { uniform.size = 4 * (uniform.components == 3 ? 4 : uniform.components); } - // Uniforms in the block need to be in order of their offset, so find the right index for it - int index = 0; - while (index < block->uniforms.length && block->uniforms.data[index].offset < uniform.offset) { - index++; - } - vec_insert(&block->uniforms, index, uniform); + vec_push(&block->uniforms, uniform); continue; } else if (uniform.location == -1) { continue; @@ -1740,7 +1755,7 @@ static void lovrShaderSetupUniforms(Shader* shader) { break; case UNIFORM_MATRIX: - uniform.size = uniform.components * uniform.components * uniform.count * sizeof(int); + uniform.size = uniform.components * uniform.components * uniform.count * sizeof(float); uniform.value.data = calloc(1, uniform.size); break; @@ -1802,9 +1817,13 @@ Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, const c "#extension GL_ARB_fragment_layer_viewport : require\n" "#define SINGLEPASS 1\n" : "#define SINGLEPASS 0\n"; + size_t maxDraws = MIN(state.limits.blockSize / (20 * sizeof(float)) / 64 * 64, 256); + char maxDrawSource[32]; + snprintf(maxDrawSource, 31, "#define MAX_DRAWS %zu\n", maxDraws); + // Vertex vertexSource = vertexSource == NULL ? lovrDefaultVertexShader : vertexSource; - const char* vertexSources[] = { vertexHeader, vertexSinglepass, lovrShaderVertexPrefix, vertexSource, lovrShaderVertexSuffix }; + const char* vertexSources[] = { vertexHeader, vertexSinglepass, maxDrawSource, lovrShaderVertexPrefix, vertexSource, lovrShaderVertexSuffix }; GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSources, sizeof(vertexSources) / sizeof(vertexSources[0])); // Fragment @@ -1823,6 +1842,7 @@ Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, const c glBindAttribLocation(program, LOVR_SHADER_TANGENT, "lovrTangent"); glBindAttribLocation(program, LOVR_SHADER_BONES, "lovrBones"); glBindAttribLocation(program, LOVR_SHADER_BONE_WEIGHTS, "lovrBoneWeights"); + glBindAttribLocation(program, LOVR_SHADER_DRAW_ID, "lovrDrawID"); linkProgram(program); glDetachShader(program, vertexShader); glDeleteShader(vertexShader); @@ -1845,7 +1865,9 @@ Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, const c map_init(&shader->attributes); for (int i = 0; i < attributeCount; i++) { char name[LOVR_MAX_ATTRIBUTE_LENGTH]; - glGetActiveAttrib(program, i, LOVR_MAX_ATTRIBUTE_LENGTH, NULL, NULL, NULL, name); + GLint size; + GLenum type; + glGetActiveAttrib(program, i, LOVR_MAX_ATTRIBUTE_LENGTH, NULL, &size, &type, name); map_set(&shader->attributes, name, glGetAttribLocation(program, name)); } @@ -1891,71 +1913,20 @@ void lovrShaderDestroy(void* ref) { map_deinit(&shader->blockMap); } -// ShaderBlock - -ShaderBlock* lovrShaderBlockInit(ShaderBlock* block, vec_uniform_t* uniforms, BlockType type, BufferUsage usage) { - lovrAssert(type != BLOCK_STORAGE || state.features.computeShaders, "Writable ShaderBlocks are not supported on this system"); - - vec_init(&block->uniforms); - vec_extend(&block->uniforms, uniforms); - map_init(&block->uniformMap); - - int i; - Uniform* uniform; - size_t size = 0; - vec_foreach_ptr(&block->uniforms, uniform, i) { - - // Calculate size and offset based on the cryptic std140 rules - size_t align; - if (uniform->count > 1 || uniform->type == UNIFORM_MATRIX) { - align = 16 * (uniform->type == UNIFORM_MATRIX ? uniform->components : 1); - uniform->size = align * uniform->count; - } else { - align = (uniform->components + (uniform->components == 3)) * 4; - uniform->size = uniform->components * 4; - } - uniform->offset = (size + (align - 1)) & -align; - size = uniform->offset + uniform->size; - - // Write index to uniform lookup - map_set(&block->uniformMap, uniform->name, i); - } - -#ifdef EMSCRIPTEN - block->target = GL_UNIFORM_BUFFER; -#else - block->target = block->type == BLOCK_UNIFORM ? GL_UNIFORM_BUFFER : GL_SHADER_STORAGE_BUFFER; -#endif - block->type = type; - block->buffer = lovrBufferCreate(size, NULL, usage, false); - - return block; -} - -void lovrShaderBlockDestroy(void* ref) { - ShaderBlock* block = ref; - lovrGpuDestroySyncResource(block, block->incoherent); - lovrRelease(block->buffer); - vec_deinit(&block->uniforms); - map_deinit(&block->uniformMap); -} - // Mesh -Mesh* lovrMeshInit(Mesh* mesh, uint32_t count, VertexFormat format, DrawMode mode, BufferUsage usage, bool readable) { - mesh->count = count; +Mesh* lovrMeshInit(Mesh* mesh, DrawMode mode, VertexFormat format, Buffer* vertexBuffer) { mesh->mode = mode; mesh->format = format; - mesh->readable = readable; - mesh->usage = usage; - mesh->vbo = lovrBufferCreate(count * format.stride, NULL, usage, readable); + mesh->vertexBuffer = vertexBuffer; + mesh->vertexCount = vertexBuffer ? (lovrBufferGetSize(vertexBuffer) / format.stride) : 0; glGenVertexArrays(1, &mesh->vao); map_init(&mesh->attributes); for (int i = 0; i < format.count; i++) { - lovrRetain(mesh->vbo); + lovrRetain(mesh->vertexBuffer); map_set(&mesh->attributes, format.attributes[i].name, ((MeshAttribute) { - .buffer = mesh->vbo, + .buffer = mesh->vertexBuffer, .offset = format.attributes[i].offset, .stride = format.stride, .type = format.attributes[i].type, @@ -1970,7 +1941,6 @@ Mesh* lovrMeshInit(Mesh* mesh, uint32_t count, VertexFormat format, DrawMode mod void lovrMeshDestroy(void* ref) { Mesh* mesh = ref; - lovrRelease(mesh->material); glDeleteVertexArrays(1, &mesh->vao); const char* key; map_iter_t iter = map_iter(&mesh->attributes); @@ -1979,6 +1949,7 @@ void lovrMeshDestroy(void* ref) { lovrRelease(attribute->buffer); } map_deinit(&mesh->attributes); - lovrRelease(mesh->vbo); - lovrRelease(mesh->ibo); + lovrRelease(mesh->vertexBuffer); + lovrRelease(mesh->indexBuffer); + lovrRelease(mesh->material); } diff --git a/src/graphics/opengl.h b/src/graphics/opengl.h index cc70699c..5bb2824b 100644 --- a/src/graphics/opengl.h +++ b/src/graphics/opengl.h @@ -11,7 +11,8 @@ #define GPU_BUFFER_FIELDS \ GLsync lock; \ - uint32_t id; + uint32_t id; \ + uint8_t incoherent; #define GPU_CANVAS_FIELDS \ uint32_t framebuffer; \ @@ -20,15 +21,12 @@ bool immortal; #define GPU_MESH_FIELDS \ - uint32_t vao; + uint32_t vao; \ + uint32_t ibo; #define GPU_SHADER_FIELDS \ uint32_t program; -#define GPU_SHADER_BLOCK_FIELDS \ - GLenum target; \ - uint8_t incoherent; - #define GPU_TEXTURE_FIELDS \ GLuint id; \ GLuint msaaId; \ diff --git a/src/graphics/shader.c b/src/graphics/shader.c index d6f6f7bb..e689ccbb 100644 --- a/src/graphics/shader.c +++ b/src/graphics/shader.c @@ -96,17 +96,15 @@ static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType t } Uniform* uniform = &shader->uniforms.data[*index]; - const char* plural = (uniform->size / size) > 1 ? "s" : ""; lovrAssert(uniform->type == type, "Unable to send %ss to uniform %s", debug, name); - lovrAssert((start + count) * size <= uniform->size, "Too many %s%s for uniform %s, maximum is %d", debug, plural, name, uniform->size / size); + lovrAssert((start + count) * size <= uniform->size, "Too many %ss for uniform %s, maximum is %d", debug, name, uniform->size / size); void* dest = uniform->value.bytes + start * size; - if (!uniform->dirty && !memcmp(dest, data, count * size)) { - return; + if (memcmp(dest, data, count * size)) { + lovrGraphicsFlushShader(shader); + memcpy(dest, data, count * size); + uniform->dirty = true; } - - memcpy(dest, data, count * size); - uniform->dirty = true; } void lovrShaderSetFloats(Shader* shader, const char* name, float* data, int start, int count) { @@ -139,37 +137,69 @@ void lovrShaderSetColor(Shader* shader, const char* name, Color color) { lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, (float*) &color, 0, 4, sizeof(float), "float"); } -void lovrShaderSetBlock(Shader* shader, const char* name, ShaderBlock* source, UniformAccess access) { +void lovrShaderSetBlock(Shader* shader, const char* name, Buffer* buffer, size_t offset, size_t size, UniformAccess access) { int* id = map_get(&shader->blockMap, name); - lovrAssert(id, "No shader block named '%s'", name); + if (!id) return; int type = *id & 1; int index = *id >> 1; UniformBlock* block = &shader->blocks[type].data[index]; - block->access = access; - if (source != block->source) { - if (source) { - lovrAssert(block->uniforms.length == source->uniforms.length, "ShaderBlock must have same number of uniforms as block definition in Shader"); - for (int i = 0; i < block->uniforms.length; i++) { - const Uniform* u = &block->uniforms.data[i]; - const Uniform* v = &source->uniforms.data[i]; - lovrAssert(u->type == v->type, "Shader is not compatible with ShaderBlock, check type of variable '%s'", v->name); - lovrAssert(u->offset == v->offset, "Shader is not compatible with ShaderBlock, check order of variable '%s'", v->name); - - // This check is disabled due to observed driver bugs with std140 layouts - // lovrAssert(u->size == v->size, "Shader is not compatible with ShaderBlock, check count of variable '%s'", v->name); - } - } - - lovrRetain(source); + if (block->source != buffer || block->offset != offset || block->size != size) { + lovrGraphicsFlushShader(shader); + lovrRetain(buffer); lovrRelease(block->source); - block->source = source; + block->access = access; + block->source = buffer; + block->offset = offset; + block->size = size; } } // ShaderBlock +// Calculates uniform size and byte offsets using std140 rules, returning the total buffer size +size_t lovrShaderComputeUniformLayout(vec_uniform_t* uniforms) { + size_t size = 0; + Uniform* uniform; int i; + vec_foreach_ptr(uniforms, uniform, i) { + size_t align; + if (uniform->count > 1 || uniform->type == UNIFORM_MATRIX) { + align = 16 * (uniform->type == UNIFORM_MATRIX ? uniform->components : 1); + uniform->size = align * uniform->count; + } else { + align = (uniform->components + (uniform->components == 3)) * 4; + uniform->size = uniform->components * 4; + } + uniform->offset = (size + (align - 1)) & -align; + size = uniform->offset + uniform->size; + } + return size; +} + +ShaderBlock* lovrShaderBlockInit(ShaderBlock* block, BlockType type, Buffer* buffer, vec_uniform_t* uniforms) { + vec_init(&block->uniforms); + map_init(&block->uniformMap); + + Uniform* uniform; int i; + vec_extend(&block->uniforms, uniforms); + vec_foreach_ptr(&block->uniforms, uniform, i) { + map_set(&block->uniformMap, uniform->name, i); + } + + block->type = type; + block->buffer = buffer; + lovrRetain(buffer); + return block; +} + +void lovrShaderBlockDestroy(void* ref) { + ShaderBlock* block = ref; + lovrRelease(block->buffer); + vec_deinit(&block->uniforms); + map_deinit(&block->uniformMap); +} + BlockType lovrShaderBlockGetType(ShaderBlock* block) { return block->type; } @@ -225,4 +255,3 @@ const Uniform* lovrShaderBlockGetUniform(ShaderBlock* block, const char* name) { Buffer* lovrShaderBlockGetBuffer(ShaderBlock* block) { return block->buffer; } - diff --git a/src/graphics/shader.h b/src/graphics/shader.h index 0d8de7ce..7a98b9b3 100644 --- a/src/graphics/shader.h +++ b/src/graphics/shader.h @@ -80,14 +80,15 @@ typedef struct { vec_uniform_t uniforms; map_int_t uniformMap; Buffer* buffer; - GPU_SHADER_BLOCK_FIELDS } ShaderBlock; typedef struct { vec_uniform_t uniforms; - int slot; - ShaderBlock* source; UniformAccess access; + Buffer* source; + size_t offset; + size_t size; + int slot; } UniformBlock; typedef vec_t(UniformBlock) vec_block_t; @@ -122,11 +123,13 @@ void lovrShaderSetMatrices(Shader* shader, const char* name, float* data, int st void lovrShaderSetTextures(Shader* shader, const char* name, Texture** data, int start, int count); void lovrShaderSetImages(Shader* shader, const char* name, Image* data, int start, int count); void lovrShaderSetColor(Shader* shader, const char* name, Color color); -void lovrShaderSetBlock(Shader* shader, const char* name, ShaderBlock* block, UniformAccess access); +void lovrShaderSetBlock(Shader* shader, const char* name, Buffer* buffer, size_t offset, size_t size, UniformAccess access); // ShaderBlock -ShaderBlock* lovrShaderBlockInit(ShaderBlock* block, vec_uniform_t* uniforms, BlockType type, BufferUsage usage); +size_t lovrShaderComputeUniformLayout(vec_uniform_t* uniforms); + +ShaderBlock* lovrShaderBlockInit(ShaderBlock* block, BlockType type, Buffer* buffer, vec_uniform_t* uniforms); #define lovrShaderBlockCreate(...) lovrShaderBlockInit(lovrAlloc(ShaderBlock), __VA_ARGS__) void lovrShaderBlockDestroy(void* ref); BlockType lovrShaderBlockGetType(ShaderBlock* block); diff --git a/src/resources/shaders.c b/src/resources/shaders.c index e31f9745..ef8d07c7 100644 --- a/src/resources/shaders.c +++ b/src/resources/shaders.c @@ -5,7 +5,7 @@ const char* lovrShaderVertexPrefix = "" "#define MAX_BONES 48 \n" "#define lovrView lovrViews[lovrViewportIndex] \n" "#define lovrProjection lovrProjections[lovrViewportIndex] \n" -"#define lovrModel lovrModels[lovrDrawID] \n" +"#define lovrModel lovrDraws[lovrDrawID].model \n" "#define lovrTransform (lovrView * lovrModel) \n" "#define lovrNormalMatrix mat3(transpose(inverse(lovrTransform))) \n" "#define lovrInstanceID (gl_InstanceID / lovrViewportCount) \n" @@ -27,8 +27,10 @@ const char* lovrShaderVertexPrefix = "" "out vec4 vertexColor; \n" "out vec4 lovrColor; \n" "uniform lovrDrawData { \n" -" mat4 lovrModels[192]; \n" -" vec4 lovrColors[192]; \n" +" struct { \n" +" mat4 model; \n" +" vec4 color; \n" +" } lovrDraws[MAX_DRAWS]; \n" "}; \n" "uniform mat4 lovrViews[2]; \n" "uniform mat4 lovrProjections[2]; \n" @@ -47,7 +49,7 @@ const char* lovrShaderVertexSuffix = "" "void main() { \n" " texCoord = (lovrMaterialTransform * vec3(lovrTexCoord, 1.)).xy; \n" " vertexColor = lovrVertexColor; \n" -" lovrColor = lovrColors[lovrDrawID]; \n" +" lovrColor = lovrDraws[lovrDrawID].color; \n" "#if SINGLEPASS \n" " gl_ViewportIndex = gl_InstanceID % lovrViewportCount; \n" "#endif \n"