Temporary buffers;

This commit is contained in:
bjorn 2022-04-27 00:28:39 -07:00
parent 43f56c223c
commit e8c809a870
7 changed files with 482 additions and 19 deletions

View File

@ -142,12 +142,8 @@ void* luax_readfile(const char* filename, size_t* bytesRead);
#endif
#ifndef LOVR_DISABLE_GRAPHICS
struct Attachment;
struct Texture;
struct Uniform;
int luax_checkuniform(struct lua_State* L, int index, const struct Uniform* uniform, void* dest, const char* debug);
int luax_optmipmap(struct lua_State* L, int index, struct Texture* texture);
void luax_readattachments(struct lua_State* L, int index, struct Attachment* attachments, int* count);
struct Buffer;
void luax_readbufferdata(struct lua_State* L, int index, struct Buffer* buffer, char* data);
#endif
#ifndef LOVR_DISABLE_MATH

View File

@ -1,5 +1,6 @@
#include "api.h"
#include "graphics/graphics.h"
#include "data/blob.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
@ -83,7 +84,7 @@ static struct { uint32_t size, scalarAlign, baseAlign, components; } fieldInfo[]
[FIELD_MAT4] = { 64, 4, 16, 16 }
};
uint32_t luax_checkfieldtype(lua_State* L, int index) {
static uint32_t luax_checkfieldtype(lua_State* L, int index) {
size_t length;
const char* string = luaL_checklstring(L, index, &length);
@ -139,6 +140,7 @@ static void luax_checkbufferformat(lua_State* L, int index, BufferInfo* info) {
switch (lua_type(L, index)) {
case LUA_TNONE:
case LUA_TNIL:
info->stride = 1;
break;
case LUA_TSTRING:
info->fieldCount = 1;
@ -307,12 +309,66 @@ static int l_lovrGraphicsGetLimits(lua_State* L) {
return 1;
}
static int l_lovrGraphicsGetBuffer(lua_State* L) {
BufferInfo info = { 0 };
luax_checkbufferformat(L, 2, &info);
switch (lua_type(L, 1)) {
case LUA_TNUMBER: info.length = lua_tointeger(L, 1); break;
case LUA_TTABLE: info.length = luax_len(L, -1); break;
default: {
Blob* blob = luax_totype(L, 1, Blob);
if (blob) {
info.length = blob->size / info.stride;
break;
} else {
return luax_typeerror(L, 1, "number, table, or Blob");
}
}
}
void* pointer;
bool hasData = !lua_isnumber(L, 1);
Buffer* buffer = lovrGraphicsGetBuffer(&info, hasData ? &pointer : NULL);
if (hasData) {
lua_settop(L, 1);
luax_readbufferdata(L, 1, buffer, pointer);
}
luax_pushtype(L, Buffer, buffer);
lovrRelease(buffer, lovrBufferDestroy);
return 1;
}
static int l_lovrGraphicsNewBuffer(lua_State* L) {
BufferInfo info = { 0 };
luax_checkbufferformat(L, 2, &info);
Buffer* buffer = lovrBufferCreate(&info, NULL);
switch (lua_type(L, 1)) {
case LUA_TNUMBER: info.length = lua_tointeger(L, 1); break;
case LUA_TTABLE: info.length = luax_len(L, -1); break;
default: {
Blob* blob = luax_totype(L, 1, Blob);
if (blob) {
info.length = blob->size / info.stride;
break;
} else {
return luax_typeerror(L, 1, "number, table, or Blob");
}
}
}
void* pointer;
bool hasData = !lua_isnumber(L, 1);
Buffer* buffer = lovrBufferCreate(&info, hasData ? &pointer : NULL);
if (hasData) {
lua_settop(L, 1);
luax_readbufferdata(L, 1, buffer, pointer);
}
luax_pushtype(L, Buffer, buffer);
lovrRelease(buffer, lovrBufferDestroy);
@ -324,6 +380,7 @@ static const luaL_Reg lovrGraphics[] = {
{ "getDevice", l_lovrGraphicsGetDevice },
{ "getFeatures", l_lovrGraphicsGetFeatures },
{ "getLimits", l_lovrGraphicsGetLimits },
{ "getBuffer", l_lovrGraphicsGetBuffer },
{ "newBuffer", l_lovrGraphicsNewBuffer },
{ NULL, NULL }
};

View File

@ -7,6 +7,209 @@
#include <stdlib.h>
#include <string.h>
static const uint32_t vectorComponents[MAX_VECTOR_TYPES] = {
[V_VEC2] = 2,
[V_VEC3] = 3,
[V_VEC4] = 4,
[V_MAT4] = 16
};
static const uint32_t fieldComponents[] = {
[FIELD_I8x4] = 4,
[FIELD_U8x4] = 4,
[FIELD_SN8x4] = 4,
[FIELD_UN8x4] = 4,
[FIELD_UN10x3] = 3,
[FIELD_I16] = 1,
[FIELD_I16x2] = 2,
[FIELD_I16x4] = 4,
[FIELD_U16] = 1,
[FIELD_U16x2] = 2,
[FIELD_U16x4] = 4,
[FIELD_SN16x2] = 2,
[FIELD_SN16x4] = 4,
[FIELD_UN16x2] = 2,
[FIELD_UN16x4] = 4,
[FIELD_I32] = 1,
[FIELD_I32x2] = 2,
[FIELD_I32x3] = 3,
[FIELD_I32x4] = 4,
[FIELD_U32] = 1,
[FIELD_U32x2] = 2,
[FIELD_U32x3] = 3,
[FIELD_U32x4] = 4,
[FIELD_F16x2] = 2,
[FIELD_F16x4] = 4,
[FIELD_F32] = 1,
[FIELD_F32x2] = 2,
[FIELD_F32x3] = 3,
[FIELD_F32x4] = 4,
[FIELD_MAT2] = 4,
[FIELD_MAT3] = 9,
[FIELD_MAT4] = 16
};
typedef union {
void* raw;
int8_t* i8;
uint8_t* u8;
int16_t* i16;
uint16_t* u16;
int32_t* i32;
uint32_t* u32;
float* f32;
} FieldPointer;
static void luax_readbufferfield(lua_State* L, int index, int type, void* data) {
FieldPointer p = { .raw = data };
if (lua_isuserdata(L, index)) {
VectorType vectorType;
float* v = luax_tovector(L, index, &vectorType);
lovrCheck(vectorComponents[vectorType] == fieldComponents[type], "Vector type is incompatible with field type");
switch (type) {
case FIELD_I8x4: for (int i = 0; i < 4; i++) p.i8[i] = (int8_t) v[i]; break;
case FIELD_U8x4: for (int i = 0; i < 4; i++) p.u8[i] = (uint8_t) v[i]; break;
case FIELD_SN8x4: for (int i = 0; i < 4; i++) p.i8[i] = (int8_t) CLAMP(v[i], -1.f, 1.f) * INT8_MAX; break;
case FIELD_UN8x4: for (int i = 0; i < 4; i++) p.u8[i] = (uint8_t) CLAMP(v[i], 0.f, 1.f) * UINT8_MAX; break;
case FIELD_UN10x3: for (int i = 0; i < 3; i++) p.u32[0] |= (uint32_t) (CLAMP(v[i], 0.f, 1.f) * 1023.f) << (10 * (2 - i)); break;
case FIELD_I16x2: for (int i = 0; i < 2; i++) p.i16[i] = (int16_t) v[i]; break;
case FIELD_I16x4: for (int i = 0; i < 4; i++) p.i16[i] = (int16_t) v[i]; break;
case FIELD_U16x2: for (int i = 0; i < 2; i++) p.u16[i] = (uint16_t) v[i]; break;
case FIELD_U16x4: for (int i = 0; i < 4; i++) p.u16[i] = (uint16_t) v[i]; break;
case FIELD_SN16x2: for (int i = 0; i < 2; i++) p.i16[i] = (int16_t) CLAMP(v[i], -1.f, 1.f) * INT16_MAX; break;
case FIELD_SN16x4: for (int i = 0; i < 4; i++) p.i16[i] = (int16_t) CLAMP(v[i], -1.f, 1.f) * INT16_MAX; break;
case FIELD_UN16x2: for (int i = 0; i < 2; i++) p.u16[i] = (uint16_t) CLAMP(v[i], 0.f, 1.f) * UINT16_MAX; break;
case FIELD_UN16x4: for (int i = 0; i < 4; i++) p.u16[i] = (uint16_t) CLAMP(v[i], 0.f, 1.f) * UINT16_MAX; break;
case FIELD_I32x2: for (int i = 0; i < 2; i++) p.i32[i] = (int32_t) v[i]; break;
case FIELD_I32x3: for (int i = 0; i < 3; i++) p.i32[i] = (int32_t) v[i]; break;
case FIELD_I32x4: for (int i = 0; i < 4; i++) p.i32[i] = (int32_t) v[i]; break;
case FIELD_U32x2: for (int i = 0; i < 2; i++) p.u32[i] = (uint32_t) v[i]; break;
case FIELD_U32x3: for (int i = 0; i < 3; i++) p.u32[i] = (uint32_t) v[i]; break;
case FIELD_U32x4: for (int i = 0; i < 4; i++) p.u32[i] = (uint32_t) v[i]; break;
case FIELD_F16x2: for (int i = 0; i < 2; i++) p.u16[i] = float32to16(v[i]); break;
case FIELD_F16x4: for (int i = 0; i < 4; i++) p.u16[i] = float32to16(v[i]); break;
case FIELD_F32x2: memcpy(data, v, 2 * sizeof(float)); break;
case FIELD_F32x3: memcpy(data, v, 3 * sizeof(float)); break;
case FIELD_F32x4: memcpy(data, v, 4 * sizeof(float)); break;
case FIELD_MAT4: memcpy(data, v, 16 * sizeof(float)); break;
default: lovrUnreachable();
}
} else {
for (uint32_t i = 0; i < fieldComponents[type]; i++) {
double x = lua_tonumber(L, index + i);
switch (type) {
case FIELD_I8x4: p.i8[i] = (int8_t) x; break;
case FIELD_U8x4: p.u8[i] = (uint8_t) x; break;
case FIELD_SN8x4: p.i8[i] = (int8_t) CLAMP(x, -1.f, 1.f) * INT8_MAX; break;
case FIELD_UN8x4: p.u8[i] = (uint8_t) CLAMP(x, 0.f, 1.f) * UINT8_MAX; break;
case FIELD_UN10x3: p.u32[0] |= (uint32_t) (CLAMP(x, 0.f, 1.f) * 1023.f) << (10 * (2 - i)); break;
case FIELD_I16: p.i16[i] = (int16_t) x; break;
case FIELD_I16x2: p.i16[i] = (int16_t) x; break;
case FIELD_I16x4: p.i16[i] = (int16_t) x; break;
case FIELD_U16: p.u16[i] = (uint16_t) x; break;
case FIELD_U16x2: p.u16[i] = (uint16_t) x; break;
case FIELD_U16x4: p.u16[i] = (uint16_t) x; break;
case FIELD_SN16x2: p.i16[i] = (int16_t) CLAMP(x, -1.f, 1.f) * INT16_MAX; break;
case FIELD_SN16x4: p.i16[i] = (int16_t) CLAMP(x, -1.f, 1.f) * INT16_MAX; break;
case FIELD_UN16x2: p.u16[i] = (uint16_t) CLAMP(x, 0.f, 1.f) * UINT16_MAX; break;
case FIELD_UN16x4: p.u16[i] = (uint16_t) CLAMP(x, 0.f, 1.f) * UINT16_MAX; break;
case FIELD_I32: p.i32[i] = (int32_t) x; break;
case FIELD_I32x2: p.i32[i] = (int32_t) x; break;
case FIELD_I32x3: p.i32[i] = (int32_t) x; break;
case FIELD_I32x4: p.i32[i] = (int32_t) x; break;
case FIELD_U32: p.u32[i] = (uint32_t) x; break;
case FIELD_U32x2: p.u32[i] = (uint32_t) x; break;
case FIELD_U32x3: p.u32[i] = (uint32_t) x; break;
case FIELD_U32x4: p.i32[i] = (uint32_t) x; break;
case FIELD_F16x2: p.u16[i] = float32to16(x); break;
case FIELD_F16x4: p.u16[i] = float32to16(x); break;
case FIELD_F32: p.f32[i] = (float) x; break;
case FIELD_F32x2: p.f32[i] = (float) x; break;
case FIELD_F32x3: p.f32[i] = (float) x; break;
case FIELD_F32x4: p.f32[i] = (float) x; break;
case FIELD_MAT2: p.f32[i] = (float) x; break;
case FIELD_MAT3: p.f32[i] = (float) x; break;
case FIELD_MAT4: p.f32[i] = (float) x; break;
default: lovrUnreachable();
}
}
}
}
void luax_readbufferdata(lua_State* L, int index, Buffer* buffer, char* data) {
const BufferInfo* info = lovrBufferGetInfo(buffer);
uint32_t stride = info->stride;
Blob* blob = luax_totype(L, index, Blob);
uint32_t srcIndex = luax_optu32(L, index + 1, 1) - 1;
uint32_t dstIndex = luax_optu32(L, index + 2, 1) - 1;
if (blob) {
uint32_t limit = MIN(blob->size / stride - srcIndex, info->length - dstIndex);
uint32_t count = luax_optu32(L, index + 3, limit);
lovrCheck(srcIndex + count <= blob->size / stride, "Tried to read too many elements from the Blob");
lovrCheck(dstIndex + count <= info->length, "Tried to write Buffer elements [%d,%d] but Buffer can only hold %d things", dstIndex + 1, dstIndex + count - 1, info->length);
data = data ? data : lovrBufferMap(buffer, dstIndex * stride, count * stride);
char* src = (char*) blob->data + srcIndex * stride;
memcpy(data, src, count * stride);
return;
}
luaL_checktype(L, index, LUA_TTABLE);
lua_rawgeti(L, index, 1);
bool nested = lua_istable(L, -1);
lua_pop(L, 1);
uint32_t length = luax_len(L, index);
uint32_t limit = nested ? MIN(length - srcIndex, info->length - dstIndex) : info->length - dstIndex;
uint32_t count = luaL_optinteger(L, index + 3, limit);
lovrCheck(dstIndex + count <= info->length, "Tried to write Buffer elements [%d,%d] but Buffer can only hold %d things", dstIndex + 1, dstIndex + count - 1, info->length);
data = data ? data : lovrBufferMap(buffer, dstIndex * stride, count * stride);
if (nested) {
for (uint32_t i = 0; i < count; i++) {
lua_rawgeti(L, index, i + srcIndex + 1);
lovrCheck(lua_type(L, -1) == LUA_TTABLE, "Expected table of tables");
int j = 1;
for (uint32_t f = 0; f < info->fieldCount; f++) {
int n = 1;
lua_rawgeti(L, -1, j);
const BufferField* field = &info->fields[f];
if (!lua_isuserdata(L, -1)) {
n = fieldComponents[field->type];
for (int c = 1; c < n; c++) {
lua_rawgeti(L, -c - 1, j + c);
}
}
luax_readbufferfield(L, -n, field->type, data + field->offset);
lua_pop(L, n);
j += n;
}
data += info->stride;
lua_pop(L, 1);
}
} else {
for (uint32_t i = 0, j = srcIndex + 1; i < count && j <= length; i++) {
for (uint32_t f = 0; f < info->fieldCount; f++) {
int n = 1;
lua_rawgeti(L, index, j);
const BufferField* field = &info->fields[f];
if (!lua_isuserdata(L, -1)) {
n = fieldComponents[field->type];
for (int c = 1; c < n; c++) {
lua_rawgeti(L, index, (int) j + c);
}
}
luax_readbufferfield(L, -n, field->type, data + field->offset);
lua_pop(L, n);
j += n;
}
data += info->stride;
}
}
}
static int l_lovrBufferGetSize(lua_State* L) {
Buffer* buffer = luax_checktype(L, 1, Buffer);
const BufferInfo* info = lovrBufferGetInfo(buffer);
@ -47,10 +250,47 @@ static int l_lovrBufferGetFormat(lua_State* L) {
return 1;
}
static int l_lovrBufferGetPointer(lua_State* L) {
Buffer* buffer = luax_checktype(L, 1, Buffer);
if (!lovrBufferIsTemporary(buffer)) {
lua_pushnil(L);
return 1;
}
void* pointer = lovrBufferMap(buffer, 0, ~0u);
lua_pushlightuserdata(L, pointer);
return 1;
}
static int l_lovrBufferIsTemporary(lua_State* L) {
Buffer* buffer = luax_checktype(L, 1, Buffer);
bool temporary = lovrBufferIsTemporary(buffer);
lua_pushboolean(L, temporary);
return 1;
}
static int l_lovrBufferSetData(lua_State* L) {
Buffer* buffer = luax_checktype(L, 1, Buffer);
luax_readbufferdata(L, 2, buffer, NULL);
return 0;
}
static int l_lovrBufferClear(lua_State* L) {
Buffer* buffer = luax_checktype(L, 1, Buffer);
const BufferInfo* info = lovrBufferGetInfo(buffer);
uint32_t index = luaL_optinteger(L, 2, 1);
uint32_t count = luaL_optinteger(L, 3, info->length - index + 1);
lovrBufferClear(buffer, (index - 1) * info->stride, count * info->stride);
return 0;
}
const luaL_Reg lovrBuffer[] = {
{ "getSize", l_lovrBufferGetSize },
{ "getLength", l_lovrBufferGetLength },
{ "getStride", l_lovrBufferGetStride },
{ "getFormat", l_lovrBufferGetFormat },
{ "getPointer", l_lovrBufferGetPointer },
{ "isTemporary", l_lovrBufferIsTemporary },
{ "setData", l_lovrBufferSetData },
{ "clear", l_lovrBufferClear },
{ NULL, NULL }
};

View File

@ -18,6 +18,13 @@ typedef struct {
bool gpu_buffer_init(gpu_buffer* buffer, gpu_buffer_info* info);
void gpu_buffer_destroy(gpu_buffer* buffer);
typedef enum {
GPU_MAP_WRITE,
GPU_MAP_READ
} gpu_map_mode;
void* gpu_map(gpu_buffer* buffer, uint32_t size, uint32_t align, gpu_map_mode mode);
// Entry
typedef struct {

View File

@ -15,12 +15,13 @@ typedef struct gpu_memory gpu_memory;
struct gpu_buffer {
VkBuffer handle;
gpu_memory* memory;
uint32_t memory;
uint32_t offset;
};
size_t gpu_sizeof_buffer() { return sizeof(gpu_buffer); }
// Pools
// Internals
struct gpu_memory {
VkDeviceMemory handle;
@ -30,6 +31,8 @@ struct gpu_memory {
typedef enum {
GPU_MEMORY_BUFFER_GPU,
GPU_MEMORY_BUFFER_CPU_WRITE,
GPU_MEMORY_BUFFER_CPU_READ,
GPU_MEMORY_COUNT
} gpu_memory_type;
@ -52,6 +55,14 @@ typedef struct {
gpu_victim data[256];
} gpu_morgue;
typedef struct {
gpu_memory* memory;
VkBuffer buffer;
uint32_t cursor;
uint32_t size;
char* pointer;
} gpu_scratchpad;
typedef struct {
VkCommandPool pool;
VkSemaphore semaphores[2];
@ -71,9 +82,10 @@ static struct {
VkDebugUtilsMessengerEXT messenger;
gpu_allocator allocators[GPU_MEMORY_COUNT];
uint8_t allocatorLookup[GPU_MEMORY_COUNT];
gpu_scratchpad scratchpad[2];
gpu_memory memory[256];
gpu_morgue morgue;
gpu_tick ticks[2];
gpu_tick ticks[4];
uint32_t tick[2];
} state;
@ -252,24 +264,94 @@ bool gpu_buffer_init(gpu_buffer* buffer, gpu_buffer_info* info) {
VkDeviceSize offset;
VkMemoryRequirements requirements;
vkGetBufferMemoryRequirements(state.device, buffer->handle, &requirements);
buffer->memory = gpu_allocate(GPU_MEMORY_BUFFER_GPU, requirements, &offset);
gpu_memory* memory = gpu_allocate(GPU_MEMORY_BUFFER_GPU, requirements, &offset);
VK(vkBindBufferMemory(state.device, buffer->handle, buffer->memory->handle, offset), "Could not bind buffer memory") {
VK(vkBindBufferMemory(state.device, buffer->handle, memory->handle, offset), "Could not bind buffer memory") {
vkDestroyBuffer(state.device, buffer->handle, NULL);
gpu_release(buffer->memory);
gpu_release(memory);
return false;
}
if (info->pointer) {
*info->pointer = buffer->memory->pointer ? (char*) buffer->memory->pointer + offset : NULL;
*info->pointer = memory->pointer ? (char*) memory->pointer + offset : NULL;
}
buffer->memory = memory - state.memory;
return true;
}
void gpu_buffer_destroy(gpu_buffer* buffer) {
condemn(buffer->handle, VK_OBJECT_TYPE_BUFFER);
gpu_release(buffer->memory);
gpu_release(&state.memory[buffer->memory]);
}
void* gpu_map(gpu_buffer* buffer, uint32_t size, uint32_t align, gpu_map_mode mode) {
gpu_scratchpad* pool = &state.scratchpad[mode];
uint32_t zone = state.tick[CPU] & TICK_MASK;
uint32_t cursor = ALIGN(pool->cursor, align);
bool oversized = size > (1 << 26); // "Big" buffers don't pollute the scratchpad (heuristic)
if (oversized || cursor + size > pool->size) {
uint32_t bufferSize;
if (oversized) {
bufferSize = size;
} else {
while (pool->size < size) {
pool->size = bufferSize = pool->size ? (pool->size << 1) : (1 << 22);
}
}
VkBufferCreateInfo info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = bufferSize,
.usage = mode == GPU_MAP_WRITE ?
(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_SRC_BIT) :
VK_BUFFER_USAGE_TRANSFER_DST_BIT
};
VkBuffer handle;
VK(vkCreateBuffer(state.device, &info, NULL, &handle), "Could not create scratch buffer") return NULL;
nickname(handle, VK_OBJECT_TYPE_BUFFER, mode == GPU_MAP_WRITE ? "Write Scratchpad" : "Read Scratchpad");
VkDeviceSize offset;
VkMemoryRequirements requirements;
vkGetBufferMemoryRequirements(state.device, handle, &requirements);
gpu_memory* memory = gpu_allocate(GPU_MEMORY_BUFFER_CPU_WRITE + mode, requirements, &offset);
VK(vkBindBufferMemory(state.device, handle, memory->handle, offset), "Could not bind scratchpad memory") {
vkDestroyBuffer(state.device, handle, NULL);
gpu_release(memory);
return NULL;
}
if (oversized) {
gpu_release(memory);
condemn(handle, VK_OBJECT_TYPE_BUFFER);
buffer->handle = handle;
buffer->memory = ~0u;
buffer->offset = 0;
return memory->pointer;
} else {
gpu_release(pool->memory);
condemn(pool->buffer, VK_OBJECT_TYPE_BUFFER);
pool->memory = memory;
pool->buffer = handle;
pool->cursor = cursor = 0;
pool->pointer = (char*) pool->memory->pointer + pool->size * zone;
}
}
pool->cursor = cursor + size;
buffer->handle = pool->buffer;
buffer->memory = ~0u;
buffer->offset = cursor + pool->size * zone;
return pool->pointer + cursor;
}
// Entry
@ -456,6 +538,8 @@ bool gpu_init(gpu_config* config) {
vkGetPhysicalDeviceMemoryProperties(state.adapter, &memoryProperties);
VkMemoryType* memoryTypes = memoryProperties.memoryTypes;
VkMemoryPropertyFlags hostVisible = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
struct { VkBufferUsageFlags usage; VkMemoryPropertyFlags flags; } bufferFlags[] = {
[GPU_MEMORY_BUFFER_GPU] = {
.usage =
@ -467,6 +551,18 @@ bool gpu_init(gpu_config* config) {
VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
},
[GPU_MEMORY_BUFFER_CPU_WRITE] = {
.usage =
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.flags = hostVisible | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
},
[GPU_MEMORY_BUFFER_CPU_READ] = {
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.flags = hostVisible | VK_MEMORY_PROPERTY_HOST_CACHED_BIT
}
};
@ -486,7 +582,7 @@ bool gpu_init(gpu_config* config) {
vkGetBufferMemoryRequirements(state.device, buffer, &requirements);
vkDestroyBuffer(state.device, buffer, NULL);
uint32_t fallback = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
uint32_t fallback = i == GPU_MEMORY_BUFFER_GPU ? VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT : hostVisible;
for (uint32_t j = 0; j < memoryProperties.memoryTypeCount; j++) {
if (~requirements.memoryTypeBits & (1 << i)) {
@ -530,7 +626,9 @@ static gpu_memory* gpu_allocate(gpu_memory_type type, VkMemoryRequirements info,
gpu_allocator* allocator = &state.allocators[state.allocatorLookup[type]];
static const uint32_t blockSizes[] = {
[GPU_MEMORY_BUFFER_GPU] = 1 << 26
[GPU_MEMORY_BUFFER_GPU] = 1 << 26,
[GPU_MEMORY_BUFFER_CPU_WRITE] = 0,
[GPU_MEMORY_BUFFER_CPU_READ] = 0
};
uint32_t blockSize = blockSizes[type];
@ -579,7 +677,7 @@ static gpu_memory* gpu_allocate(gpu_memory_type type, VkMemoryRequirements info,
}
static void gpu_release(gpu_memory* memory) {
if (--memory->refs == 0) {
if (memory && --memory->refs == 0) {
condemn(memory->handle, VK_OBJECT_TYPE_DEVICE_MEMORY);
}
}

View File

@ -12,6 +12,7 @@ struct Buffer {
uint32_t size;
gpu_buffer* gpu;
BufferInfo info;
void* pointer;
};
typedef struct {
@ -38,6 +39,8 @@ static void onMessage(void* context, const char* message, bool severe);
bool lovrGraphicsInit(bool debug) {
if (state.initialized) return false;
float16Init();
gpu_config config = {
.debug = debug,
.callback = onMessage,
@ -119,6 +122,26 @@ void lovrGraphicsGetLimits(GraphicsLimits* limits) {
// Buffer
Buffer* lovrGraphicsGetBuffer(BufferInfo* info, void** data) {
uint32_t size = info->length * MAX(info->stride, 1);
lovrCheck(size > 0, "Buffer size can not be zero");
lovrCheck(size <= 1 << 30, "Max buffer size is 16GB");
Buffer* buffer = fralloc(sizeof(Buffer) + gpu_sizeof_buffer());
buffer->ref = 1;
buffer->size = size;
buffer->gpu = (gpu_buffer*) (buffer + 1);
buffer->info = *info;
buffer->pointer = gpu_map(buffer->gpu, size, state.limits.uniformBufferAlign, GPU_MAP_WRITE);
if (data) {
*data = buffer->pointer;
}
return buffer;
}
Buffer* lovrBufferCreate(BufferInfo* info, void** data) {
uint32_t size = info->length * MAX(info->stride, 1);
lovrCheck(size > 0, "Buffer size can not be zero");
@ -137,11 +160,18 @@ Buffer* lovrBufferCreate(BufferInfo* info, void** data) {
.pointer = data
});
if (data && *data == NULL) {
gpu_buffer* scratchpad = fralloc(gpu_sizeof_buffer());
*data = gpu_map(scratchpad, size, 4, GPU_MAP_WRITE);
// TODO copy scratchpad to buffer
}
return buffer;
}
void lovrBufferDestroy(void* ref) {
Buffer* buffer = ref;
if (buffer->pointer) return;
gpu_buffer_destroy(buffer->gpu);
free(buffer);
}
@ -150,6 +180,36 @@ const BufferInfo* lovrBufferGetInfo(Buffer* buffer) {
return &buffer->info;
}
bool lovrBufferIsTemporary(Buffer* buffer) {
return !!buffer->pointer;
}
void* lovrBufferMap(Buffer* buffer, uint32_t offset, uint32_t size) {
if (size == ~0u) {
size = buffer->size - offset;
}
lovrCheck(offset + size <= buffer->size, "Buffer write range [%d,%d] exceeds buffer size", offset, offset + size);
if (buffer->pointer) {
return (char*) buffer->pointer + offset;
}
// TODO stage copy once commands exist
return NULL;
}
void lovrBufferClear(Buffer* buffer, uint32_t offset, uint32_t size) {
lovrCheck(offset + size <= buffer->size, "Tried to clear past the end of the Buffer");
if (buffer->pointer) {
memset((char*) buffer->pointer + offset, 0, size);
} else {
lovrCheck(size % 4 == 0, "Buffer clear size must be a multiple of 4");
lovrCheck(offset % 4 == 0, "Buffer clear offset must be a multiple of 4");
// TODO clear once commands exist
}
}
// Helpers
static void* fralloc(size_t size) {

View File

@ -113,10 +113,15 @@ typedef struct {
uint32_t stride;
uint32_t fieldCount;
BufferField fields[16];
void** pointer;
const char* label;
uintptr_t handle;
} BufferInfo;
Buffer* lovrGraphicsGetBuffer(BufferInfo* info, void** data);
Buffer* lovrBufferCreate(BufferInfo* info, void** data);
void lovrBufferDestroy(void* ref);
const BufferInfo* lovrBufferGetInfo(Buffer* buffer);
bool lovrBufferIsTemporary(Buffer* buffer);
void* lovrBufferMap(Buffer* buffer, uint32_t offset, uint32_t size);
void lovrBufferClear(Buffer* buffer, uint32_t offset, uint32_t size);