Many batching improvements; Refactoring;

This commit is contained in:
bjorn 2018-12-27 13:50:42 -08:00 committed by Bjorn Swenson
parent d66057ee70
commit ea3a77a73a
23 changed files with 1542 additions and 1004 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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();

View File

@ -1,4 +1,5 @@
#include "graphics/material.h"
#include "graphics/graphics.h"
#include "resources/shaders.h"
#include <math.h>
#include <stdlib.h>
@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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) {

View File

@ -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);
}

View File

@ -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; \

View File

@ -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;
}

View File

@ -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);

View File

@ -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"