189 lines
4.9 KiB
C
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;
|
|
}
|