lovr/src/modules/graphics/graphics.c

234 lines
7.0 KiB
C
Raw Normal View History

2016-11-19 09:28:01 +00:00
#include "graphics/graphics.h"
2022-04-21 07:27:13 +00:00
#include "core/gpu.h"
2022-04-27 05:51:24 +00:00
#include "core/os.h"
2022-04-21 07:27:13 +00:00
#include "util.h"
#include <stdlib.h>
2022-04-20 07:38:21 +00:00
#include <string.h>
2022-04-27 05:44:44 +00:00
#define MAX_FRAME_MEMORY (1 << 30)
struct Buffer {
uint32_t ref;
uint32_t size;
gpu_buffer* gpu;
BufferInfo info;
2022-04-27 07:35:09 +00:00
char* pointer;
};
2022-04-27 05:44:44 +00:00
typedef struct {
char* memory;
uint32_t cursor;
uint32_t length;
} Allocator;
2019-06-27 08:47:08 +00:00
static struct {
bool initialized;
2022-04-22 20:28:59 +00:00
gpu_device_info device;
gpu_features features;
gpu_limits limits;
2022-04-27 05:44:44 +00:00
Allocator allocator;
2019-06-27 08:47:08 +00:00
} state;
2016-07-07 07:04:24 +00:00
2022-04-21 07:27:13 +00:00
// Helpers
2022-04-27 05:44:44 +00:00
static void* fralloc(size_t size);
2022-04-21 07:27:13 +00:00
static void onMessage(void* context, const char* message, bool severe);
// Entry
bool lovrGraphicsInit(bool debug) {
2022-04-19 02:30:58 +00:00
if (state.initialized) return false;
2022-04-21 07:27:13 +00:00
2022-04-27 07:28:39 +00:00
float16Init();
2022-04-21 07:27:13 +00:00
gpu_config config = {
.debug = debug,
.callback = onMessage,
.engineName = "LOVR",
.engineVersion = { LOVR_VERSION_MAJOR, LOVR_VERSION_MINOR, LOVR_VERSION_PATCH },
.device = &state.device,
.features = &state.features,
.limits = &state.limits
2022-04-21 07:27:13 +00:00
};
if (!gpu_init(&config)) {
lovrThrow("Failed to initialize GPU");
}
2022-04-27 05:44:44 +00:00
// Temporary frame memory uses a large 1GB virtual memory allocation, committing pages as needed
state.allocator.length = 1 << 14;
state.allocator.memory = os_vm_init(MAX_FRAME_MEMORY);
os_vm_commit(state.allocator.memory, state.allocator.length);
2022-04-19 02:30:58 +00:00
state.initialized = true;
return true;
2016-09-28 03:20:08 +00:00
}
void lovrGraphicsDestroy() {
if (!state.initialized) return;
2022-04-21 07:27:13 +00:00
gpu_destroy();
2022-04-27 05:44:44 +00:00
os_vm_free(state.allocator.memory, MAX_FRAME_MEMORY);
2019-06-27 08:47:08 +00:00
memset(&state, 0, sizeof(state));
2016-07-07 07:04:24 +00:00
}
2022-04-21 07:27:13 +00:00
void lovrGraphicsGetDevice(GraphicsDevice* device) {
2022-04-22 20:28:59 +00:00
device->deviceId = state.device.deviceId;
device->vendorId = state.device.vendorId;
device->name = state.device.deviceName;
device->renderer = state.device.renderer;
device->subgroupSize = state.device.subgroupSize;
2022-04-22 20:28:59 +00:00
device->discrete = state.device.discrete;
}
void lovrGraphicsGetFeatures(GraphicsFeatures* features) {
features->textureBC = state.features.textureBC;
features->textureASTC = state.features.textureASTC;
features->wireframe = state.features.wireframe;
features->depthClamp = state.features.depthClamp;
features->indirectDrawFirstInstance = state.features.indirectDrawFirstInstance;
features->float64 = state.features.float64;
features->int64 = state.features.int64;
features->int16 = state.features.int16;
}
void lovrGraphicsGetLimits(GraphicsLimits* limits) {
limits->textureSize2D = state.limits.textureSize2D;
limits->textureSize3D = state.limits.textureSize3D;
limits->textureSizeCube = state.limits.textureSizeCube;
limits->textureLayers = state.limits.textureLayers;
limits->renderSize[0] = state.limits.renderSize[0];
limits->renderSize[1] = state.limits.renderSize[1];
limits->renderSize[2] = state.limits.renderSize[2];
limits->uniformBufferRange = state.limits.uniformBufferRange;
limits->storageBufferRange = state.limits.storageBufferRange;
limits->uniformBufferAlign = state.limits.uniformBufferAlign;
limits->storageBufferAlign = state.limits.storageBufferAlign;
limits->vertexAttributes = state.limits.vertexAttributes;
limits->vertexBufferStride = state.limits.vertexBufferStride;
limits->vertexShaderOutputs = state.limits.vertexShaderOutputs;
limits->clipDistances = state.limits.clipDistances;
limits->cullDistances = state.limits.cullDistances;
limits->clipAndCullDistances = state.limits.clipAndCullDistances;
memcpy(limits->computeDispatchCount, state.limits.computeDispatchCount, 3 * sizeof(uint32_t));
memcpy(limits->computeWorkgroupSize, state.limits.computeWorkgroupSize, 3 * sizeof(uint32_t));
limits->computeWorkgroupVolume = state.limits.computeWorkgroupVolume;
limits->computeSharedMemory = state.limits.computeSharedMemory;
limits->shaderConstantSize = state.limits.pushConstantSize;
limits->indirectDrawCount = state.limits.indirectDrawCount;
limits->instances = state.limits.instances;
limits->anisotropy = state.limits.anisotropy;
limits->pointSize = state.limits.pointSize;
}
// Buffer
2022-04-27 07:28:39 +00:00
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");
lovrCheck(size <= 1 << 30, "Max buffer size is 1GB");
Buffer* buffer = calloc(1, sizeof(Buffer) + gpu_sizeof_buffer());
lovrAssert(buffer, "Out of memory");
buffer->ref = 1;
buffer->size = size;
buffer->gpu = (gpu_buffer*) (buffer + 1);
buffer->info = *info;
gpu_buffer_init(buffer->gpu, &(gpu_buffer_info) {
.size = buffer->size,
.label = info->label,
.pointer = data
});
2022-04-27 07:28:39 +00:00
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;
2022-04-27 07:28:39 +00:00
if (buffer->pointer) return;
gpu_buffer_destroy(buffer->gpu);
free(buffer);
}
const BufferInfo* lovrBufferGetInfo(Buffer* buffer) {
return &buffer->info;
}
2022-04-27 07:28:39 +00:00
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) {
2022-04-27 07:35:09 +00:00
return buffer->pointer + offset;
2022-04-27 07:28:39 +00:00
}
// 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) {
2022-04-27 07:35:09 +00:00
memset(buffer->pointer + offset, 0, size);
2022-04-27 07:28:39 +00:00
} 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
}
}
2022-04-21 07:27:13 +00:00
// Helpers
2022-04-27 05:44:44 +00:00
static void* fralloc(size_t size) {
while (state.allocator.cursor + size > state.allocator.length) {
lovrAssert(state.allocator.length << 1 <= MAX_FRAME_MEMORY, "Out of memory");
os_vm_commit(state.allocator.memory + state.allocator.length, state.allocator.length);
state.allocator.length <<= 1;
}
uint32_t cursor = ALIGN(state.allocator.cursor, 8);
state.allocator.cursor = cursor + size;
return state.allocator.memory + cursor;
}
2022-04-21 07:27:13 +00:00
static void onMessage(void* context, const char* message, bool severe) {
if (severe) {
lovrLog(LOG_ERROR, "GPU", message);
} else {
lovrLog(LOG_DEBUG, "GPU", message);
}
}