Compare commits

...

3 Commits

Author SHA1 Message Date
bjorn c1d8c64c45 Pass:copy can copy tables to buffers; 2022-06-22 00:39:56 -07:00
bjorn cb4275bff7 Add DrawStyle; 2022-06-22 00:05:26 -07:00
bjorn 499cf9c0dc More Font APIs; 2022-06-21 15:28:03 -07:00
5 changed files with 241 additions and 97 deletions

View File

@ -53,6 +53,13 @@ StringEntry lovrCullMode[] = {
StringEntry lovrDefaultShader[] = {
[SHADER_UNLIT] = ENTRY("unlit"),
[SHADER_FONT] = ENTRY("font"),
{ 0 }
};
StringEntry lovrDrawStyle[] = {
[STYLE_FILL] = ENTRY("fill"),
[STYLE_LINE] = ENTRY("line"),
{ 0 }
};

View File

@ -1,7 +1,33 @@
#include "api.h"
#include "graphics/graphics.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
static int l_lovrFontGetRasterizer(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
const FontInfo* info = lovrFontGetInfo(font);
luax_pushtype(L, Rasterizer, info->rasterizer);
return 1;
}
static int l_lovrFontGetPixelDensity(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
float density = lovrFontGetPixelDensity(font);
lua_pushnumber(L, density);
return 1;
}
static int l_lovrFontSetPixelDensity(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
float pixelDensity = luax_optfloat(L, 2, 0.f);
lovrFontSetPixelDensity(font, pixelDensity);
return 0;
}
const luaL_Reg lovrFont[] = {
{ "getRasterizer", l_lovrFontGetRasterizer },
{ "getPixelDensity", l_lovrFontGetPixelDensity },
{ "setPixelDensity", l_lovrFontSetPixelDensity },
{ NULL, NULL }
};

View File

@ -480,25 +480,28 @@ static int l_lovrPassPlane(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
float transform[16];
int index = luax_readmat4(L, 2, transform, 2);
DrawStyle style = luax_checkenum(L, index++, DrawStyle, "fill");
uint32_t cols = luax_optu32(L, index++, 1);
uint32_t rows = luax_optu32(L, index, cols);
lovrPassPlane(pass, transform, cols, rows);
lovrPassPlane(pass, transform, style, cols, rows);
return 0;
}
static int l_lovrPassCube(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
float transform[16];
luax_readmat4(L, 2, transform, 1);
lovrPassBox(pass, transform);
int index = luax_readmat4(L, 2, transform, 1);
DrawStyle style = luax_checkenum(L, index, DrawStyle, "fill");
lovrPassBox(pass, transform, style);
return 0;
}
static int l_lovrPassBox(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
float transform[16];
luax_readmat4(L, 2, transform, 3);
lovrPassBox(pass, transform);
int index = luax_readmat4(L, 2, transform, 3);
DrawStyle style = luax_checkenum(L, index, DrawStyle, "fill");
lovrPassBox(pass, transform, style);
return 0;
}
@ -506,10 +509,11 @@ static int l_lovrPassCircle(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
float transform[16];
int index = luax_readmat4(L, 2, transform, 1);
DrawStyle style = luax_checkenum(L, index++, DrawStyle, "fill");
float angle1 = luax_optfloat(L, index++, 0.f);
float angle2 = luax_optfloat(L, index++, 2.f * (float) M_PI);
uint32_t segments = luax_optu32(L, index++, 64);
lovrPassCircle(pass, transform, angle1, angle2, segments);
lovrPassCircle(pass, transform, style, angle1, angle2, segments);
return 0;
}
@ -600,6 +604,26 @@ static int l_lovrPassClear(lua_State* L) {
static int l_lovrPassCopy(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
if (lua_istable(L, 2)) {
Buffer* buffer = luax_checktype(L, 3, Buffer);
uint32_t srcIndex = luax_optu32(L, 4, 1) - 1;
uint32_t dstIndex = luax_optu32(L, 5, 1) - 1;
lua_rawgeti(L, 2, 1);
bool nested = lua_istable(L, -1);
lua_pop(L, 1);
uint32_t length = luax_len(L, 2);
const BufferInfo* info = lovrBufferGetInfo(buffer);
uint32_t limit = nested ? MIN(length - srcIndex, info->length - dstIndex) : info->length - dstIndex;
uint32_t count = luax_optu32(L, 6, limit);
void* data = lovrPassCopyDataToBuffer(pass, buffer, dstIndex * info->stride, count * info->stride);
lua_remove(L, 3); // table, srcIndex, dstIndex, count
luax_readbufferdata(L, 2, buffer, data);
return 0;
}
Blob* blob = luax_totype(L, 2, Blob);
if (blob) {
@ -611,7 +635,8 @@ static int l_lovrPassCopy(lua_State* L) {
uint32_t extent = luax_optu32(L, 6, limit);
lovrCheck(extent <= blob->size - srcOffset, "Buffer copy range exceeds Blob size");
lovrCheck(extent <= info->length * info->stride - dstOffset, "Buffer copy offset exceeds Buffer size");
lovrPassCopyDataToBuffer(pass, (char*) blob->data + srcOffset, buffer, dstOffset, extent);
void* data = lovrPassCopyDataToBuffer(pass, buffer, dstOffset, extent);
memcpy(data, (char*) blob->data + srcOffset, extent);
return 0;
}

View File

@ -628,7 +628,7 @@ void lovrGraphicsSetBackground(float background[4]) {
Font* lovrGraphicsGetDefaultFont() {
if (!state.defaultFont) {
state.defaultFont = lovrFontCreate(&(FontInfo) {
.rasterizer = lovrRasterizerCreate(NULL, 48),
.rasterizer = lovrRasterizerCreate(NULL, 32),
.padding = 1,
.spread = 4.
});
@ -1738,6 +1738,10 @@ void lovrFontDestroy(void* ref) {
free(font);
}
const FontInfo* lovrFontGetInfo(Font* font) {
return &font->info;
}
float lovrFontGetPixelDensity(Font* font) {
return font->pixelDensity;
}
@ -2695,21 +2699,36 @@ void lovrPassLine(Pass* pass, uint32_t count, float** points) {
}
}
void lovrPassPlane(Pass* pass, float* transform, uint32_t cols, uint32_t rows) {
void lovrPassPlane(Pass* pass, float* transform, DrawStyle style, uint32_t cols, uint32_t rows) {
ShapeVertex* vertices;
uint16_t* indices;
uint32_t vertexCount = (cols + 1) * (rows + 1);
uint32_t indexCount = (cols * rows) * 6;
uint32_t indexCount;
lovrPassDraw(pass, &(Draw) {
.mode = VERTEX_TRIANGLES,
.transform = transform,
.vertex.pointer = (void**) &vertices,
.vertex.count = vertexCount,
.index.pointer = (void**) &indices,
.index.count = indexCount
});
if (style == STYLE_LINE) {
indexCount = 2 * (rows + 1) + 2 * (cols + 1);
lovrPassDraw(pass, &(Draw) {
.mode = VERTEX_LINES,
.transform = transform,
.vertex.pointer = (void**) &vertices,
.vertex.count = vertexCount,
.index.pointer = (void**) &indices,
.index.count = indexCount
});
} else {
indexCount = (cols * rows) * 6;
lovrPassDraw(pass, &(Draw) {
.mode = VERTEX_TRIANGLES,
.transform = transform,
.vertex.pointer = (void**) &vertices,
.vertex.count = vertexCount,
.index.pointer = (void**) &indices,
.index.count = indexCount
});
}
for (uint32_t y = 0; y <= rows; y++) {
float v = y * (1.f / rows);
@ -2723,73 +2742,113 @@ void lovrPassPlane(Pass* pass, float* transform, uint32_t cols, uint32_t rows) {
}
}
for (uint32_t y = 0; y < rows; y++) {
for (uint32_t x = 0; x < cols; x++) {
uint16_t a = (y * (cols + 1)) + x;
uint16_t b = a + 1;
uint16_t c = a + cols + 1;
uint16_t d = a + cols + 2;
uint16_t cell[] = { a, b, c, c, b, d };
memcpy(indices, cell, sizeof(cell));
indices += COUNTOF(cell);
if (style == STYLE_LINE) {
for (uint32_t y = 0; y <= rows; y++) {
uint16_t a = y * (cols + 1);
uint16_t b = a + cols;
uint16_t line[] = { a, b };
memcpy(indices, line, sizeof(line));
indices += COUNTOF(line);
}
for (uint32_t x = 0; x <= cols; x++) {
uint16_t a = x;
uint16_t b = x + ((cols + 1) * rows);
uint16_t line[] = { a, b };
memcpy(indices, line, sizeof(line));
indices += COUNTOF(line);
}
} else {
for (uint32_t y = 0; y < rows; y++) {
for (uint32_t x = 0; x < cols; x++) {
uint16_t a = (y * (cols + 1)) + x;
uint16_t b = a + 1;
uint16_t c = a + cols + 1;
uint16_t d = a + cols + 2;
uint16_t cell[] = { a, b, c, c, b, d };
memcpy(indices, cell, sizeof(cell));
indices += COUNTOF(cell);
}
}
}
}
void lovrPassBox(Pass* pass, float* transform) {
ShapeVertex* vertices;
uint16_t* indices;
void lovrPassBox(Pass* pass, float* transform, DrawStyle style) {
if (style == STYLE_LINE) {
static ShapeVertex vertices[] = {
{ { -.5f, .5f, -.5f }, { 0.f, 0.f, 0.f }, { 0.f, 0.f } }, // Front
{ { .5f, .5f, -.5f }, { 0.f, 0.f, 0.f }, { 0.f, 0.f } },
{ { .5f, -.5f, -.5f }, { 0.f, 0.f, 0.f }, { 0.f, 0.f } },
{ { -.5f, -.5f, -.5f }, { 0.f, 0.f, 0.f }, { 0.f, 0.f } },
{ { -.5f, .5f, .5f }, { 0.f, 0.f, 0.f }, { 0.f, 0.f } }, // Back
{ { .5f, .5f, .5f }, { 0.f, 0.f, 0.f }, { 0.f, 0.f } },
{ { .5f, -.5f, .5f }, { 0.f, 0.f, 0.f }, { 0.f, 0.f } },
{ { -.5f, -.5f, .5f }, { 0.f, 0.f, 0.f }, { 0.f, 0.f } }
};
ShapeVertex vertexData[] = {
{ { -.5f, -.5f, -.5f }, { 0.f, 0.f, -1.f }, { 0.f, 0.f } }, // Front
{ { -.5f, .5f, -.5f }, { 0.f, 0.f, -1.f }, { 0.f, 1.f } },
{ { .5f, -.5f, -.5f }, { 0.f, 0.f, -1.f }, { 1.f, 0.f } },
{ { .5f, .5f, -.5f }, { 0.f, 0.f, -1.f }, { 1.f, 1.f } },
{ { .5f, .5f, -.5f }, { 1.f, 0.f, 0.f }, { 0.f, 1.f } }, // Right
{ { .5f, .5f, .5f }, { 1.f, 0.f, 0.f }, { 1.f, 1.f } },
{ { .5f, -.5f, -.5f }, { 1.f, 0.f, 0.f }, { 0.f, 0.f } },
{ { .5f, -.5f, .5f }, { 1.f, 0.f, 0.f }, { 1.f, 0.f } },
{ { .5f, -.5f, .5f }, { 0.f, 0.f, 1.f }, { 0.f, 0.f } }, // Back
{ { .5f, .5f, .5f }, { 0.f, 0.f, 1.f }, { 0.f, 1.f } },
{ { -.5f, -.5f, .5f }, { 0.f, 0.f, 1.f }, { 1.f, 0.f } },
{ { -.5f, .5f, .5f }, { 0.f, 0.f, 1.f }, { 1.f, 1.f } },
{ { -.5f, .5f, .5f }, { -1.f, 0.f, 0.f }, { 0.f, 1.f } }, // Left
{ { -.5f, .5f, -.5f }, { -1.f, 0.f, 0.f }, { 1.f, 1.f } },
{ { -.5f, -.5f, .5f }, { -1.f, 0.f, 0.f }, { 0.f, 0.f } },
{ { -.5f, -.5f, -.5f }, { -1.f, 0.f, 0.f }, { 1.f, 0.f } },
{ { -.5f, -.5f, -.5f }, { 0.f, -1.f, 0.f }, { 0.f, 0.f } }, // Bottom
{ { .5f, -.5f, -.5f }, { 0.f, -1.f, 0.f }, { 1.f, 0.f } },
{ { -.5f, -.5f, .5f }, { 0.f, -1.f, 0.f }, { 0.f, 1.f } },
{ { .5f, -.5f, .5f }, { 0.f, -1.f, 0.f }, { 1.f, 1.f } },
{ { -.5f, .5f, -.5f }, { 0.f, 1.f, 0.f }, { 0.f, 1.f } }, // Top
{ { -.5f, .5f, .5f }, { 0.f, 1.f, 0.f }, { 0.f, 0.f } },
{ { .5f, .5f, -.5f }, { 0.f, 1.f, 0.f }, { 1.f, 1.f } },
{ { .5f, .5f, .5f }, { 0.f, 1.f, 0.f }, { 1.f, 0.f } }
};
static uint16_t indices[] = {
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
};
uint16_t indexData[] = {
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
};
lovrPassDraw(pass, &(Draw) {
.mode = VERTEX_LINES,
.transform = transform,
.vertex.data = vertices,
.vertex.count = COUNTOF(vertices),
.index.data = indices,
.index.count = COUNTOF(indices)
});
} else {
ShapeVertex vertices[] = {
{ { -.5f, -.5f, -.5f }, { 0.f, 0.f, -1.f }, { 0.f, 0.f } }, // Front
{ { -.5f, .5f, -.5f }, { 0.f, 0.f, -1.f }, { 0.f, 1.f } },
{ { .5f, -.5f, -.5f }, { 0.f, 0.f, -1.f }, { 1.f, 0.f } },
{ { .5f, .5f, -.5f }, { 0.f, 0.f, -1.f }, { 1.f, 1.f } },
{ { .5f, .5f, -.5f }, { 1.f, 0.f, 0.f }, { 0.f, 1.f } }, // Right
{ { .5f, .5f, .5f }, { 1.f, 0.f, 0.f }, { 1.f, 1.f } },
{ { .5f, -.5f, -.5f }, { 1.f, 0.f, 0.f }, { 0.f, 0.f } },
{ { .5f, -.5f, .5f }, { 1.f, 0.f, 0.f }, { 1.f, 0.f } },
{ { .5f, -.5f, .5f }, { 0.f, 0.f, 1.f }, { 0.f, 0.f } }, // Back
{ { .5f, .5f, .5f }, { 0.f, 0.f, 1.f }, { 0.f, 1.f } },
{ { -.5f, -.5f, .5f }, { 0.f, 0.f, 1.f }, { 1.f, 0.f } },
{ { -.5f, .5f, .5f }, { 0.f, 0.f, 1.f }, { 1.f, 1.f } },
{ { -.5f, .5f, .5f }, { -1.f, 0.f, 0.f }, { 0.f, 1.f } }, // Left
{ { -.5f, .5f, -.5f }, { -1.f, 0.f, 0.f }, { 1.f, 1.f } },
{ { -.5f, -.5f, .5f }, { -1.f, 0.f, 0.f }, { 0.f, 0.f } },
{ { -.5f, -.5f, -.5f }, { -1.f, 0.f, 0.f }, { 1.f, 0.f } },
{ { -.5f, -.5f, -.5f }, { 0.f, -1.f, 0.f }, { 0.f, 0.f } }, // Bottom
{ { .5f, -.5f, -.5f }, { 0.f, -1.f, 0.f }, { 1.f, 0.f } },
{ { -.5f, -.5f, .5f }, { 0.f, -1.f, 0.f }, { 0.f, 1.f } },
{ { .5f, -.5f, .5f }, { 0.f, -1.f, 0.f }, { 1.f, 1.f } },
{ { -.5f, .5f, -.5f }, { 0.f, 1.f, 0.f }, { 0.f, 1.f } }, // Top
{ { -.5f, .5f, .5f }, { 0.f, 1.f, 0.f }, { 0.f, 0.f } },
{ { .5f, .5f, -.5f }, { 0.f, 1.f, 0.f }, { 1.f, 1.f } },
{ { .5f, .5f, .5f }, { 0.f, 1.f, 0.f }, { 1.f, 0.f } }
};
lovrPassDraw(pass, &(Draw) {
.mode = VERTEX_TRIANGLES,
.transform = transform,
.vertex.pointer = (void**) &vertices,
.vertex.count = COUNTOF(vertexData),
.index.pointer = (void**) &indices,
.index.count = COUNTOF(indexData)
});
uint16_t indices[] = {
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
};
memcpy(vertices, vertexData, sizeof(vertexData));
memcpy(indices, indexData, sizeof(indexData));
lovrPassDraw(pass, &(Draw) {
.mode = VERTEX_TRIANGLES,
.transform = transform,
.vertex.data = vertices,
.vertex.count = COUNTOF(vertices),
.index.data = indices,
.index.count = COUNTOF(indices)
});
}
}
void lovrPassCircle(Pass* pass, float* transform, float angle1, float angle2, uint32_t segments) {
void lovrPassCircle(Pass* pass, float* transform, DrawStyle style, float angle1, float angle2, uint32_t segments) {
ShapeVertex* vertices;
uint16_t* indices;
@ -2798,20 +2857,34 @@ void lovrPassCircle(Pass* pass, float* transform, float angle1, float angle2, ui
angle2 = 2.f * (float) M_PI;
}
uint32_t vertexCount = segments + 2;
uint32_t indexCount = segments * 3;
if (style == STYLE_LINE) {
uint32_t vertexCount = segments + 1;
uint32_t indexCount = segments * 2;
lovrPassDraw(pass, &(Draw) {
.mode = VERTEX_TRIANGLES,
.transform = transform,
.vertex.pointer = (void**) &vertices,
.vertex.count = vertexCount,
.index.pointer = (void**) &indices,
.index.count = indexCount
});
lovrPassDraw(pass, &(Draw) {
.mode = VERTEX_LINES,
.transform = transform,
.vertex.pointer = (void**) &vertices,
.vertex.count = vertexCount,
.index.pointer = (void**) &indices,
.index.count = indexCount
});
} else {
uint32_t vertexCount = segments + 2;
uint32_t indexCount = segments * 3;
// Center
*vertices++ = (ShapeVertex) { { 0.f, 0.f, 0.f }, { 0.f, 0.f, 1.f }, { .5f, .5f } };
lovrPassDraw(pass, &(Draw) {
.mode = VERTEX_TRIANGLES,
.transform = transform,
.vertex.pointer = (void**) &vertices,
.vertex.count = vertexCount,
.index.pointer = (void**) &indices,
.index.count = indexCount
});
// Center
*vertices++ = (ShapeVertex) { { 0.f, 0.f, 0.f }, { 0.f, 0.f, 1.f }, { .5f, .5f } };
}
float angleShift = (angle2 - angle1) / segments;
for (uint32_t i = 0; i <= segments; i++) {
@ -2821,10 +2894,18 @@ void lovrPassCircle(Pass* pass, float* transform, float angle1, float angle2, ui
*vertices++ = (ShapeVertex) { { x, y, 0.f }, { 0.f, 0.f, 1.f }, { x + .5f, .5f - y } };
}
for (uint32_t i = 0; i < segments; i++) {
uint16_t wedge[] = { 0, i + 1, i + 2 };
memcpy(indices, wedge, sizeof(wedge));
indices += COUNTOF(wedge);
if (style == STYLE_LINE) {
for (uint32_t i = 0; i < segments; i++) {
uint16_t segment[] = { i, i + 1 };
memcpy(indices, segment, sizeof(segment));
indices += COUNTOF(segment);
}
} else {
for (uint32_t i = 0; i < segments; i++) {
uint16_t wedge[] = { 0, i + 1, i + 2 };
memcpy(indices, wedge, sizeof(wedge));
indices += COUNTOF(wedge);
}
}
}
@ -3242,7 +3323,7 @@ void lovrPassClearTexture(Pass* pass, Texture* texture, float value[4], uint32_t
trackTexture(pass, texture, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_WRITE);
}
void lovrPassCopyDataToBuffer(Pass* pass, void* data, Buffer* buffer, uint32_t offset, uint32_t extent) {
void* lovrPassCopyDataToBuffer(Pass* pass, Buffer* buffer, uint32_t offset, uint32_t extent) {
lovrCheck(pass->info.type == PASS_TRANSFER, "This function can only be called on a transfer pass");
lovrCheck(!lovrBufferIsTemporary(buffer), "Temporary buffers can not be copied to, use Buffer:setData");
lovrCheck(offset + extent <= buffer->size, "Buffer copy range goes past the end of the Buffer");
@ -3250,7 +3331,7 @@ void lovrPassCopyDataToBuffer(Pass* pass, void* data, Buffer* buffer, uint32_t o
void* pointer = gpu_map(scratchpad, extent, 4, GPU_MAP_WRITE);
gpu_copy_buffers(pass->stream, scratchpad, buffer->gpu, 0, offset, extent);
trackBuffer(pass, buffer, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_WRITE);
memcpy(pointer, data, extent);
return pointer;
}
void lovrPassCopyBufferToBuffer(Pass* pass, Buffer* src, Buffer* dst, uint32_t srcOffset, uint32_t dstOffset, uint32_t extent) {

View File

@ -369,6 +369,11 @@ typedef enum {
CULL_BACK
} CullMode;
typedef enum {
STYLE_FILL,
STYLE_LINE
} DrawStyle;
typedef enum {
ALIGN_LEFT,
ALIGN_CENTER,
@ -469,16 +474,16 @@ void lovrPassSendSampler(Pass* pass, const char* name, size_t length, uint32_t s
void lovrPassSendValue(Pass* pass, const char* name, size_t length, void** data, FieldType* type);
void lovrPassPoints(Pass* pass, uint32_t count, float** vertices);
void lovrPassLine(Pass* pass, uint32_t count, float** vertices);
void lovrPassPlane(Pass* pass, float* transform, uint32_t cols, uint32_t rows);
void lovrPassBox(Pass* pass, float* transform);
void lovrPassCircle(Pass* pass, float* transform, float angle1, float angle2, uint32_t segments);
void lovrPassPlane(Pass* pass, float* transform, DrawStyle style, uint32_t cols, uint32_t rows);
void lovrPassBox(Pass* pass, float* transform, DrawStyle style);
void lovrPassCircle(Pass* pass, float* transform, DrawStyle style, float angle1, float angle2, uint32_t segments);
void lovrPassText(Pass* pass, Font* font, const char* text, uint32_t length, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign);
void lovrPassMesh(Pass* pass, Buffer* vertices, Buffer* indices, float* transform, uint32_t start, uint32_t count, uint32_t instances);
void lovrPassMultimesh(Pass* pass, Buffer* vertices, Buffer* indices, Buffer* indirect, uint32_t count, uint32_t offset, uint32_t stride);
void lovrPassCompute(Pass* pass, uint32_t x, uint32_t y, uint32_t z, Buffer* indirect, uint32_t offset);
void lovrPassClearBuffer(Pass* pass, Buffer* buffer, uint32_t offset, uint32_t extent);
void lovrPassClearTexture(Pass* pass, Texture* texture, float value[4], uint32_t layer, uint32_t layerCount, uint32_t level, uint32_t levelCount);
void lovrPassCopyDataToBuffer(Pass* pass, void* data, Buffer* buffer, uint32_t offset, uint32_t size);
void* lovrPassCopyDataToBuffer(Pass* pass, Buffer* buffer, uint32_t offset, uint32_t extent);
void lovrPassCopyBufferToBuffer(Pass* pass, Buffer* src, Buffer* dst, uint32_t srcOffset, uint32_t dstOffset, uint32_t extent);
void lovrPassCopyImageToTexture(Pass* pass, struct Image* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[3]);
void lovrPassCopyTextureToTexture(Pass* pass, Texture* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[3]);