taisei/src/renderer/gl33/common_buffer.c
2024-05-17 14:11:48 +02:00

189 lines
4.9 KiB
C

/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "common_buffer.h"
#include "gl33.h"
#define STREAM_CBUF(rw) ((CommonBuffer*)rw)
static int64_t gl33_buffer_stream_seek(SDL_RWops *rw, int64_t offset, int whence) {
CommonBuffer *cbuf = STREAM_CBUF(rw);
switch(whence) {
case RW_SEEK_CUR: {
cbuf->offset += offset;
break;
}
case RW_SEEK_END: {
cbuf->offset = cbuf->size + offset;
break;
}
case RW_SEEK_SET: {
cbuf->offset = offset;
break;
}
}
assert(cbuf->offset <= cbuf->size);
return cbuf->offset;
}
static int64_t gl33_buffer_stream_size(SDL_RWops *rw) {
return STREAM_CBUF(rw)->size;
}
static size_t gl33_buffer_stream_write(SDL_RWops *rw, const void *data, size_t size, size_t num) {
CommonBuffer *cbuf = STREAM_CBUF(rw);
size_t total_size = size * num;
size_t offset = cbuf->offset;
size_t req_bufsize = offset + total_size;
if(UNLIKELY(req_bufsize > cbuf->size)) {
gl33_buffer_resize(cbuf, req_bufsize);
assert(req_bufsize <= cbuf->size);
assert(offset == cbuf->offset);
}
if(LIKELY(total_size > 0)) {
memcpy(cbuf->cache.buffer + cbuf->offset, data, total_size);
cbuf->cache.update_begin = min(cbuf->offset, cbuf->cache.update_begin);
cbuf->cache.update_end = max(cbuf->offset + total_size, cbuf->cache.update_end);
cbuf->offset += total_size;
}
return num;
}
static size_t gl33_buffer_stream_read(SDL_RWops *rw, void *data, size_t size, size_t num) {
SDL_SetError("Can't read from an OpenGL buffer stream");
return 0;
}
static int gl33_buffer_stream_close(SDL_RWops *rw) {
SDL_SetError("Can't close an OpenGL buffer stream");
return -1;
}
SDL_RWops *gl33_buffer_get_stream(CommonBuffer *cbuf) {
return &cbuf->stream;
}
CommonBuffer *gl33_buffer_create(uint bindidx, size_t alloc_size) {
CommonBuffer *cbuf = mem_alloc(alloc_size);
cbuf->bindidx = bindidx;
cbuf->stream.type = SDL_RWOPS_UNKNOWN;
cbuf->stream.close = gl33_buffer_stream_close;
cbuf->stream.read = gl33_buffer_stream_read;
cbuf->stream.write = gl33_buffer_stream_write;
cbuf->stream.seek = gl33_buffer_stream_seek;
cbuf->stream.size = gl33_buffer_stream_size;
glGenBuffers(1, &cbuf->gl_handle);
return cbuf;
}
void gl33_buffer_init_cache(CommonBuffer *cbuf, size_t capacity) {
capacity = topow2(capacity);
if(cbuf->size != capacity || !cbuf->cache.buffer) {
cbuf->cache.buffer = mem_realloc(cbuf->cache.buffer, capacity);
cbuf->size = cbuf->commited_size = capacity;
cbuf->cache.update_begin = capacity;
}
}
void gl33_buffer_init(CommonBuffer *cbuf, size_t capacity, void *data, GLenum usage_hint) {
size_t data_size = capacity;
gl33_buffer_init_cache(cbuf, capacity);
cbuf->gl_usage_hint = usage_hint;
GL33_BUFFER_TEMP_BIND(cbuf, {
assert(glIsBuffer(cbuf->gl_handle));
GLenum target = gl33_bindidx_to_glenum(cbuf->bindidx);
glBufferData(target, cbuf->size, NULL, usage_hint);
if(data != NULL) {
glBufferSubData(target, 0, data_size, data);
memcpy(cbuf->cache.buffer, data, data_size);
}
});
}
void gl33_buffer_destroy(CommonBuffer *cbuf) {
mem_free(cbuf->cache.buffer);
gl33_buffer_deleted(cbuf);
glDeleteBuffers(1, &cbuf->gl_handle);
mem_free(cbuf);
}
void gl33_buffer_invalidate(CommonBuffer *cbuf) {
// TODO: a better way to set this properly in advance
cbuf->gl_usage_hint = GL_DYNAMIC_DRAW;
GL33_BUFFER_TEMP_BIND(cbuf, {
glBufferData(gl33_bindidx_to_glenum(cbuf->bindidx), cbuf->size, NULL, cbuf->gl_usage_hint);
});
cbuf->offset = 0;
}
void gl33_buffer_resize(CommonBuffer *cbuf, size_t new_size) {
assert(cbuf->cache.buffer != NULL);
size_t old_size = cbuf->size;
new_size = topow2(new_size);
if(UNLIKELY(cbuf->size == new_size)) {
return;
}
log_warn("Resizing buffer %u (%s) from %zu to %zu",
cbuf->gl_handle, cbuf->debug_label, old_size, new_size
);
cbuf->size = new_size;
cbuf->cache.buffer = mem_realloc(cbuf->cache.buffer, new_size);
cbuf->cache.update_begin = 0;
cbuf->cache.update_end = min(old_size, new_size);
if(cbuf->offset > new_size) {
cbuf->offset = new_size;
}
}
void gl33_buffer_flush(CommonBuffer *cbuf) {
if(cbuf->cache.update_begin >= cbuf->cache.update_end) {
return;
}
size_t update_size = cbuf->cache.update_end - cbuf->cache.update_begin;
assert(update_size > 0);
GL33_BUFFER_TEMP_BIND(cbuf, {
GLenum target = gl33_bindidx_to_glenum(cbuf->bindidx);
if(cbuf->size != cbuf->commited_size) {
log_debug("Resizing buffer %u (%s) from %zu to %zu",
cbuf->gl_handle, cbuf->debug_label, cbuf->commited_size, cbuf->size
);
glBufferData(target, cbuf->size, NULL, cbuf->gl_usage_hint);
cbuf->commited_size = cbuf->size;
}
glBufferSubData(
target,
cbuf->cache.update_begin,
update_size,
cbuf->cache.buffer + cbuf->cache.update_begin
);
});
cbuf->cache.update_begin = cbuf->size;
cbuf->cache.update_end = 0;
}