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"
|
2022-04-26 22:32:54 +00:00
|
|
|
#include <stdlib.h>
|
2022-04-20 07:38:21 +00:00
|
|
|
#include <string.h>
|
2019-06-28 05:17:50 +00:00
|
|
|
|
2022-04-27 05:44:44 +00:00
|
|
|
#define MAX_FRAME_MEMORY (1 << 30)
|
|
|
|
|
2022-04-26 22:32:54 +00:00
|
|
|
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-26 22:32:54 +00:00
|
|
|
};
|
|
|
|
|
2022-04-29 05:30:31 +00:00
|
|
|
struct Pass {
|
|
|
|
uint32_t ref;
|
|
|
|
PassInfo info;
|
|
|
|
gpu_stream* stream;
|
|
|
|
};
|
|
|
|
|
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-29 05:30:31 +00:00
|
|
|
bool active;
|
|
|
|
uint32_t tick;
|
|
|
|
Pass* transfers;
|
2022-04-22 20:28:59 +00:00
|
|
|
gpu_device_info device;
|
2022-04-21 09:16:17 +00:00
|
|
|
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-29 05:30:31 +00:00
|
|
|
static void* tempAlloc(size_t size);
|
|
|
|
static void beginFrame(void);
|
|
|
|
static gpu_stream* getTransfers(void);
|
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,
|
2022-04-21 09:16:17 +00:00
|
|
|
.callback = onMessage,
|
2022-04-27 07:21:04 +00:00
|
|
|
.engineName = "LOVR",
|
|
|
|
.engineVersion = { LOVR_VERSION_MAJOR, LOVR_VERSION_MINOR, LOVR_VERSION_PATCH },
|
2022-04-21 09:16:17 +00:00
|
|
|
.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
|
|
|
}
|
|
|
|
|
2016-10-04 22:13:57 +00:00
|
|
|
void lovrGraphicsDestroy() {
|
2018-02-23 03:18:36 +00:00
|
|
|
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
|
|
|
|
2022-04-21 09:16:17 +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;
|
2022-04-21 09:16:17 +00:00
|
|
|
device->renderer = state.device.renderer;
|
|
|
|
device->subgroupSize = state.device.subgroupSize;
|
2022-04-22 20:28:59 +00:00
|
|
|
device->discrete = state.device.discrete;
|
2022-04-21 09:16:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lovrGraphicsGetFeatures(GraphicsFeatures* features) {
|
2022-04-26 22:31:51 +00:00
|
|
|
features->textureBC = state.features.textureBC;
|
|
|
|
features->textureASTC = state.features.textureASTC;
|
2022-04-21 09:16:17 +00:00
|
|
|
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;
|
2022-04-26 22:31:51 +00:00
|
|
|
limits->clipDistances = state.limits.clipDistances;
|
|
|
|
limits->cullDistances = state.limits.cullDistances;
|
|
|
|
limits->clipAndCullDistances = state.limits.clipAndCullDistances;
|
2022-04-21 09:16:17 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-04-30 00:12:10 +00:00
|
|
|
bool lovrGraphicsIsFormatSupported(uint32_t format, uint32_t features) {
|
|
|
|
uint8_t supports = state.features.formats[format];
|
|
|
|
if (!features) return supports;
|
|
|
|
if ((features & TEXTURE_FEATURE_SAMPLE) && !(supports & GPU_FEATURE_SAMPLE)) return false;
|
|
|
|
if ((features & TEXTURE_FEATURE_FILTER) && !(supports & GPU_FEATURE_FILTER)) return false;
|
|
|
|
if ((features & TEXTURE_FEATURE_RENDER) && !(supports & GPU_FEATURE_RENDER)) return false;
|
|
|
|
if ((features & TEXTURE_FEATURE_BLEND) && !(supports & GPU_FEATURE_BLEND)) return false;
|
|
|
|
if ((features & TEXTURE_FEATURE_STORAGE) && !(supports & GPU_FEATURE_STORAGE)) return false;
|
|
|
|
if ((features & TEXTURE_FEATURE_ATOMIC) && !(supports & GPU_FEATURE_ATOMIC)) return false;
|
|
|
|
if ((features & TEXTURE_FEATURE_BLIT_SRC) && !(supports & GPU_FEATURE_BLIT_SRC)) return false;
|
|
|
|
if ((features & TEXTURE_FEATURE_BLIT_DST) && !(supports & GPU_FEATURE_BLIT_DST)) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-04-29 05:30:31 +00:00
|
|
|
void lovrGraphicsSubmit(Pass** passes, uint32_t count) {
|
|
|
|
if (!state.active) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate a few extra stream handles for any internal passes we sneak in
|
|
|
|
gpu_stream** streams = tempAlloc((count + 3) * sizeof(gpu_stream*));
|
|
|
|
|
|
|
|
uint32_t extraPassCount = 0;
|
|
|
|
|
|
|
|
if (state.transfers) {
|
|
|
|
streams[extraPassCount++] = state.transfers->stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
|
|
streams[extraPassCount + i] = passes[i]->stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < extraPassCount + count; i++) {
|
|
|
|
gpu_stream_end(streams[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
gpu_submit(streams, extraPassCount + count);
|
|
|
|
|
|
|
|
state.transfers = NULL;
|
|
|
|
state.active = false;
|
|
|
|
}
|
|
|
|
|
2022-04-29 05:37:03 +00:00
|
|
|
void lovrGraphicsWait() {
|
|
|
|
gpu_wait();
|
|
|
|
}
|
|
|
|
|
2022-04-26 22:32:54 +00:00
|
|
|
// Buffer
|
|
|
|
|
2022-04-27 07:28:39 +00:00
|
|
|
Buffer* lovrGraphicsGetBuffer(BufferInfo* info, void** data) {
|
2022-04-28 22:39:45 +00:00
|
|
|
uint32_t size = info->length * info->stride;
|
2022-04-27 07:28:39 +00:00
|
|
|
lovrCheck(size > 0, "Buffer size can not be zero");
|
|
|
|
lovrCheck(size <= 1 << 30, "Max buffer size is 16GB");
|
|
|
|
|
2022-04-29 05:30:31 +00:00
|
|
|
Buffer* buffer = tempAlloc(sizeof(Buffer) + gpu_sizeof_buffer());
|
2022-04-27 07:28:39 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-04-26 22:32:54 +00:00
|
|
|
Buffer* lovrBufferCreate(BufferInfo* info, void** data) {
|
2022-04-28 22:39:45 +00:00
|
|
|
uint32_t size = info->length * info->stride;
|
2022-04-26 22:32:54 +00:00
|
|
|
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) {
|
2022-04-29 05:30:31 +00:00
|
|
|
gpu_buffer* scratchpad = tempAlloc(gpu_sizeof_buffer());
|
2022-04-27 07:28:39 +00:00
|
|
|
*data = gpu_map(scratchpad, size, 4, GPU_MAP_WRITE);
|
|
|
|
// TODO copy scratchpad to buffer
|
|
|
|
}
|
|
|
|
|
2022-04-26 22:32:54 +00:00
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lovrBufferDestroy(void* ref) {
|
|
|
|
Buffer* buffer = ref;
|
2022-04-27 07:28:39 +00:00
|
|
|
if (buffer->pointer) return;
|
2022-04-26 22:32:54 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2022-04-29 05:30:31 +00:00
|
|
|
gpu_stream* transfers = getTransfers();
|
|
|
|
gpu_buffer* scratchpad = tempAlloc(gpu_sizeof_buffer());
|
|
|
|
void* data = gpu_map(scratchpad, size, 4, GPU_MAP_WRITE);
|
|
|
|
gpu_copy_buffers(transfers, scratchpad, buffer->gpu, 0, offset, size);
|
|
|
|
return data;
|
2022-04-27 07:28:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lovrBufferClear(Buffer* buffer, uint32_t offset, uint32_t size) {
|
2022-04-29 05:30:31 +00:00
|
|
|
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");
|
2022-04-27 07:28:39 +00:00
|
|
|
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 {
|
2022-04-29 05:30:31 +00:00
|
|
|
gpu_stream* transfers = getTransfers();
|
|
|
|
gpu_clear_buffer(transfers, buffer->gpu, offset, size);
|
2022-04-27 07:28:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-29 05:30:31 +00:00
|
|
|
// Pass
|
|
|
|
|
|
|
|
Pass* lovrGraphicsGetPass(PassInfo* info) {
|
|
|
|
beginFrame();
|
|
|
|
Pass* pass = tempAlloc(sizeof(Pass));
|
|
|
|
pass->ref = 1;
|
|
|
|
pass->info = *info;
|
|
|
|
pass->stream = gpu_stream_begin(info->label);
|
|
|
|
return pass;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lovrPassDestroy(void* ref) {
|
|
|
|
//
|
|
|
|
}
|
|
|
|
|
|
|
|
const PassInfo* lovrPassGetInfo(Pass* pass) {
|
|
|
|
return &pass->info;
|
|
|
|
}
|
|
|
|
|
2022-04-21 07:27:13 +00:00
|
|
|
// Helpers
|
|
|
|
|
2022-04-29 05:30:31 +00:00
|
|
|
static void* tempAlloc(size_t size) {
|
2022-04-27 05:44:44 +00:00
|
|
|
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-29 05:30:31 +00:00
|
|
|
static void beginFrame(void) {
|
|
|
|
if (state.active) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
state.active = true;
|
|
|
|
state.tick = gpu_begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
static gpu_stream* getTransfers(void) {
|
|
|
|
if (!state.transfers) {
|
|
|
|
state.transfers = lovrGraphicsGetPass(&(PassInfo) {
|
|
|
|
.type = PASS_TRANSFER,
|
|
|
|
.label = "Internal Transfers"
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return state.transfers->stream;
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|