GPUVertBuf: GL Backend Isolation

Part of the Vulkan port T68990

This makes a few changes in how the data is being handled by the
backend to allow more flexibility in the future.

The overall code logic is left unchanged.
This commit is contained in:
Clément Foucault 2020-09-07 01:20:55 +02:00
parent bb2aeb4504
commit c38debd39f
7 changed files with 224 additions and 167 deletions

View File

@ -35,8 +35,6 @@
#include <cstring>
#define VRAM_USAGE 1
/* -------------------------------------------------------------------- */
/** \name VertBuf
* \{ */
@ -45,6 +43,75 @@ namespace blender::gpu {
size_t VertBuf::memory_usage = 0;
VertBuf::VertBuf()
{
/* Needed by some code check. */
format.attr_len = 0;
}
VertBuf::~VertBuf()
{
/* Should already have been cleared. */
BLI_assert(flag == GPU_VERTBUF_INVALID);
}
void VertBuf::init(const GPUVertFormat *format, GPUUsageType usage)
{
usage_ = usage;
flag = GPU_VERTBUF_DATA_DIRTY;
GPU_vertformat_copy(&this->format, format);
if (!format->packed) {
VertexFormat_pack(&this->format);
}
flag |= GPU_VERTBUF_INIT;
}
void VertBuf::clear(void)
{
this->release_data();
flag = GPU_VERTBUF_INVALID;
}
VertBuf *VertBuf::duplicate(void)
{
VertBuf *dst = GPUBackend::get()->vertbuf_alloc();
/* Full copy. */
*dst = *this;
/* Almost full copy... */
dst->handle_refcount_ = 1;
/* Duplicate all needed implementation specifics data. */
this->duplicate_data(dst);
return dst;
}
void VertBuf::allocate(uint vert_len)
{
BLI_assert(format.packed);
/* Catch any unnecessary usage. */
BLI_assert(vertex_alloc != vert_len || data == nullptr);
vertex_len = vertex_alloc = vert_len;
this->acquire_data();
flag |= GPU_VERTBUF_DATA_DIRTY;
}
void VertBuf::resize(uint vert_len)
{
/* Catch any unnecessary usage. */
BLI_assert(vertex_alloc != vert_len);
vertex_len = vertex_alloc = vert_len;
this->resize_data();
flag |= GPU_VERTBUF_DATA_DIRTY;
}
void VertBuf::upload(void)
{
this->upload_data();
}
} // namespace blender::gpu
/** \} */
@ -56,8 +123,6 @@ size_t VertBuf::memory_usage = 0;
using namespace blender;
using namespace blender::gpu;
static uint GPU_vertbuf_size_get(const VertBuf *verts);
/* -------- Creation & deletion -------- */
GPUVertBuf *GPU_vertbuf_calloc(void)
@ -68,7 +133,7 @@ GPUVertBuf *GPU_vertbuf_calloc(void)
GPUVertBuf *GPU_vertbuf_create_with_format_ex(const GPUVertFormat *format, GPUUsageType usage)
{
GPUVertBuf *verts = GPU_vertbuf_calloc();
GPU_vertbuf_init_with_format_ex(verts, format, usage);
unwrap(verts)->init(format, usage);
return verts;
}
@ -76,133 +141,48 @@ void GPU_vertbuf_init_with_format_ex(GPUVertBuf *verts_,
const GPUVertFormat *format,
GPUUsageType usage)
{
VertBuf *verts = unwrap(verts_);
verts->usage = usage;
verts->flag = GPU_VERTBUF_DATA_DIRTY;
verts->handle_refcount = 1;
GPU_vertformat_copy(&verts->format, format);
if (!format->packed) {
VertexFormat_pack(&verts->format);
}
verts->flag |= GPU_VERTBUF_INIT;
unwrap(verts_)->init(format, usage);
}
GPUVertBuf *GPU_vertbuf_duplicate(GPUVertBuf *verts_)
{
VertBuf *verts = unwrap(verts_);
VertBuf *verts_dst = unwrap(GPU_vertbuf_calloc());
/* Full copy. */
*verts_dst = *verts;
verts_dst->handle_refcount = 1;
GPU_vertformat_copy(&verts_dst->format, &verts->format);
if (verts->vbo_id) {
uint buffer_sz = GPU_vertbuf_size_get(verts);
verts_dst->vbo_id = GPU_buf_alloc();
glBindBuffer(GL_COPY_READ_BUFFER, verts->vbo_id);
glBindBuffer(GL_COPY_WRITE_BUFFER, verts_dst->vbo_id);
glBufferData(GL_COPY_WRITE_BUFFER, buffer_sz, NULL, to_gl(verts->usage));
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, buffer_sz);
VertBuf::memory_usage += GPU_vertbuf_size_get(verts);
}
if (verts->data) {
verts_dst->data = (uchar *)MEM_dupallocN(verts->data);
}
return wrap(verts_dst);
return wrap(unwrap(verts_)->duplicate());
}
/** Same as discard but does not free. */
void GPU_vertbuf_clear(GPUVertBuf *verts_)
void GPU_vertbuf_clear(GPUVertBuf *verts)
{
VertBuf *verts = unwrap(verts_);
if (verts->vbo_id) {
GPU_buf_free(verts->vbo_id);
verts->vbo_id = 0;
VertBuf::memory_usage -= GPU_vertbuf_size_get(verts);
}
if (verts->data) {
MEM_SAFE_FREE(verts->data);
}
verts->flag = GPU_VERTBUF_INVALID;
unwrap(verts)->clear();
}
void GPU_vertbuf_discard(GPUVertBuf *verts)
{
GPU_vertbuf_clear(verts);
GPU_vertbuf_handle_ref_remove(verts);
unwrap(verts)->clear();
unwrap(verts)->reference_remove();
}
void GPU_vertbuf_handle_ref_add(GPUVertBuf *verts_)
void GPU_vertbuf_handle_ref_add(GPUVertBuf *verts)
{
VertBuf *verts = unwrap(verts_);
verts->handle_refcount++;
unwrap(verts)->reference_add();
}
void GPU_vertbuf_handle_ref_remove(GPUVertBuf *verts_)
void GPU_vertbuf_handle_ref_remove(GPUVertBuf *verts)
{
VertBuf *verts = unwrap(verts_);
BLI_assert(verts->handle_refcount > 0);
verts->handle_refcount--;
if (verts->handle_refcount == 0) {
/* Should already have been cleared. */
BLI_assert(verts->vbo_id == 0 && verts->data == NULL);
MEM_freeN(verts);
}
}
uint GPU_vertbuf_size_get(const VertBuf *verts)
{
return vertex_buffer_size(&verts->format, verts->vertex_len);
unwrap(verts)->reference_remove();
}
/* -------- Data update -------- */
/* create a new allocation, discarding any existing data */
void GPU_vertbuf_data_alloc(GPUVertBuf *verts_, uint v_len)
void GPU_vertbuf_data_alloc(GPUVertBuf *verts, uint v_len)
{
VertBuf *verts = unwrap(verts_);
GPUVertFormat *format = &verts->format;
if (!format->packed) {
VertexFormat_pack(format);
}
#if TRUST_NO_ONE
/* catch any unnecessary use */
assert(verts->vertex_alloc != v_len || verts->data == NULL);
#endif
/* discard previous data if any */
if (verts->data) {
MEM_freeN(verts->data);
}
uint new_size = vertex_buffer_size(&verts->format, v_len);
VertBuf::memory_usage += new_size - GPU_vertbuf_size_get(verts);
verts->flag |= GPU_VERTBUF_DATA_DIRTY;
verts->vertex_len = verts->vertex_alloc = v_len;
verts->data = (uchar *)MEM_mallocN(sizeof(GLubyte) * GPU_vertbuf_size_get(verts), __func__);
unwrap(verts)->allocate(v_len);
}
/* resize buffer keeping existing data */
void GPU_vertbuf_data_resize(GPUVertBuf *verts_, uint v_len)
void GPU_vertbuf_data_resize(GPUVertBuf *verts, uint v_len)
{
VertBuf *verts = unwrap(verts_);
#if TRUST_NO_ONE
assert(verts->data != NULL);
assert(verts->vertex_alloc != v_len);
#endif
uint new_size = vertex_buffer_size(&verts->format, v_len);
VertBuf::memory_usage += new_size - GPU_vertbuf_size_get(verts);
verts->flag |= GPU_VERTBUF_DATA_DIRTY;
verts->vertex_len = verts->vertex_alloc = v_len;
verts->data = (uchar *)MEM_reallocN(verts->data, sizeof(GLubyte) * GPU_vertbuf_size_get(verts));
unwrap(verts)->resize(v_len);
}
/* Set vertex count but does not change allocation.
@ -214,9 +194,6 @@ void GPU_vertbuf_data_len_set(GPUVertBuf *verts_, uint v_len)
BLI_assert(verts->data != NULL); /* Only for dynamic data. */
BLI_assert(v_len <= verts->vertex_alloc);
uint new_size = vertex_buffer_size(&verts->format, v_len);
VertBuf::memory_usage += new_size - GPU_vertbuf_size_get(verts);
verts->vertex_len = v_len;
}
@ -359,34 +336,10 @@ uint GPU_vertbuf_get_memory_usage(void)
return VertBuf::memory_usage;
}
static void VertBuffer_upload_data(GPUVertBuf *verts_)
/* Should be rename to GPU_vertbuf_data_upload */
void GPU_vertbuf_use(GPUVertBuf *verts)
{
VertBuf *verts = unwrap(verts_);
uint buffer_sz = GPU_vertbuf_size_get(verts);
/* orphan the vbo to avoid sync */
glBufferData(GL_ARRAY_BUFFER, buffer_sz, NULL, to_gl(verts->usage));
/* upload data */
glBufferSubData(GL_ARRAY_BUFFER, 0, buffer_sz, verts->data);
if (verts->usage == GPU_USAGE_STATIC) {
MEM_SAFE_FREE(verts->data);
}
verts->flag &= ~GPU_VERTBUF_DATA_DIRTY;
verts->flag |= GPU_VERTBUF_DATA_UPLOADED;
}
void GPU_vertbuf_use(GPUVertBuf *verts_)
{
VertBuf *verts = unwrap(verts_);
/* only create the buffer the 1st time */
if (verts->vbo_id == 0) {
verts->vbo_id = GPU_buf_alloc();
}
glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
if (verts->flag & GPU_VERTBUF_DATA_DIRTY) {
VertBuffer_upload_data(verts_);
}
unwrap(verts)->upload();
}
/** \} */

View File

@ -29,7 +29,8 @@
namespace blender::gpu {
struct VertBuf {
class VertBuf {
public:
static size_t memory_usage;
GPUVertFormat format = {};
@ -37,16 +38,65 @@ struct VertBuf {
uint vertex_len = 0;
/** Number of verts data. */
uint vertex_alloc = 0;
/** 0 indicates not yet allocated. */
uint32_t vbo_id = 0;
/** Usage hint for GL optimisation. */
GPUUsageType usage = GPU_USAGE_STATIC;
/** Status flag. */
GPUVertBufStatus flag = GPU_VERTBUF_INVALID;
/** This counter will only avoid freeing the GPUVertBuf, not the data. */
char handle_refcount = 0;
/** NULL indicates data in VRAM (unmapped) */
uchar *data = NULL;
protected:
/** Usage hint for GL optimisation. */
GPUUsageType usage_ = GPU_USAGE_STATIC;
private:
/** This counter will only avoid freeing the GPUVertBuf, not the data. */
int handle_refcount_ = 1;
public:
VertBuf();
virtual ~VertBuf();
void init(const GPUVertFormat *format, GPUUsageType usage);
void clear(void);
/* Data manament */
void allocate(uint vert_len);
void resize(uint vert_len);
void upload(void);
VertBuf *duplicate(void);
/* Size of the data allocated. */
size_t size_alloc_get(void) const
{
BLI_assert(format.packed);
return vertex_alloc * format.stride;
}
/* Size of the data uploaded to the GPU. */
size_t size_used_get(void) const
{
BLI_assert(format.packed);
return vertex_len * format.stride;
}
void reference_add(void)
{
handle_refcount_++;
}
void reference_remove(void)
{
BLI_assert(handle_refcount_ > 0);
handle_refcount_--;
if (handle_refcount_ == 0) {
delete this;
}
}
protected:
virtual void acquire_data(void) = 0;
virtual void resize_data(void) = 0;
virtual void release_data(void) = 0;
virtual void upload_data(void) = 0;
virtual void duplicate_data(VertBuf *dst) = 0;
};
/* Syntacting suggar. */

View File

@ -270,9 +270,9 @@ bool GLShader::transform_feedback_enable(GPUVertBuf *buf_)
GLVertBuf *buf = static_cast<GLVertBuf *>(unwrap(buf_));
BLI_assert(buf->vbo_id != 0);
BLI_assert(buf->vbo_id_ != 0);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf->vbo_id);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf->vbo_id_);
switch (transform_feedback_type_) {
case GPU_SHADER_TFB_POINTS:

View File

@ -120,10 +120,10 @@ bool GLTexture::init_internal(GPUVertBuf *vbo)
GLenum internal_format = to_gl_internal_format(format_);
if (GLEW_ARB_direct_state_access) {
glTextureBuffer(tex_id_, internal_format, gl_vbo->vbo_id);
glTextureBuffer(tex_id_, internal_format, gl_vbo->vbo_id_);
}
else {
glTexBuffer(target_, internal_format, gl_vbo->vbo_id);
glTexBuffer(target_, internal_format, gl_vbo->vbo_id_);
}
#ifndef __APPLE__

View File

@ -122,7 +122,7 @@ void GLVertArray::update_bindings(const GLuint vao,
for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; v--) {
GLVertBuf *vbo = batch->verts_(v);
if (vbo) {
GPU_vertbuf_use(batch->verts[v]);
vbo->bind();
attr_mask &= ~vbo_bind(interface, &vbo->format, 0, vbo->vertex_len, false);
}
}
@ -130,7 +130,7 @@ void GLVertArray::update_bindings(const GLuint vao,
for (int v = GPU_BATCH_INST_VBO_MAX_LEN - 1; v > -1; v--) {
GLVertBuf *vbo = batch->inst_(v);
if (vbo) {
GPU_vertbuf_use(batch->inst[v]);
vbo->bind();
attr_mask &= ~vbo_bind(interface, &vbo->format, base_instance, vbo->vertex_len, true);
}
}

View File

@ -27,30 +27,83 @@
namespace blender::gpu {
void GLVertBuf::bind(void)
void GLVertBuf::acquire_data(void)
{
/* Discard previous data if any. */
MEM_SAFE_FREE(data);
data = (uchar *)MEM_mallocN(sizeof(uchar) * this->size_alloc_get(), __func__);
}
void GLVertBuf::upload_data(void)
void GLVertBuf::resize_data(void)
{
}
uchar *GLVertBuf::acquire_data(void)
{
return nullptr;
}
uchar *GLVertBuf::resize_data(void)
{
return nullptr;
data = (uchar *)MEM_reallocN(data, sizeof(uchar) * this->size_alloc_get());
}
void GLVertBuf::release_data(void)
{
if (vbo_id_ != 0) {
GLBackend::get()->buf_free(vbo_id_);
vbo_id_ = 0;
memory_usage -= vbo_size_;
}
MEM_SAFE_FREE(data);
}
void GLVertBuf::duplicate_data(VertBuf *)
void GLVertBuf::duplicate_data(VertBuf *dst_)
{
BLI_assert(GPU_context_active_get() != NULL);
GLVertBuf *src = this;
GLVertBuf *dst = static_cast<GLVertBuf *>(dst_);
if (src->vbo_id_ != 0) {
dst->vbo_size_ = src->size_used_get();
glGenBuffers(1, &dst->vbo_id_);
glBindBuffer(GL_COPY_WRITE_BUFFER, dst->vbo_id_);
glBufferData(GL_COPY_WRITE_BUFFER, dst->vbo_size_, NULL, to_gl(dst->usage_));
glBindBuffer(GL_COPY_READ_BUFFER, src->vbo_id_);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, dst->vbo_size_);
memory_usage += dst->vbo_size_;
}
if (data != nullptr) {
dst->data = (uchar *)MEM_dupallocN(src->data);
}
}
void GLVertBuf::upload_data(void)
{
this->bind();
}
void GLVertBuf::bind(void)
{
BLI_assert(GPU_context_active_get() != NULL);
if (vbo_id_ == 0) {
glGenBuffers(1, &vbo_id_);
}
glBindBuffer(GL_ARRAY_BUFFER, vbo_id_);
if (flag & GPU_VERTBUF_DATA_DIRTY) {
vbo_size_ = this->size_used_get();
/* Orphan the vbo to avoid sync then upload data. */
glBufferData(GL_ARRAY_BUFFER, vbo_size_, NULL, to_gl(usage_));
glBufferSubData(GL_ARRAY_BUFFER, 0, vbo_size_, data);
memory_usage += vbo_size_;
if (usage_ == GPU_USAGE_STATIC) {
MEM_SAFE_FREE(data);
}
flag &= ~GPU_VERTBUF_DATA_DIRTY;
flag |= GPU_VERTBUF_DATA_UPLOADED;
}
}
} // namespace blender::gpu

View File

@ -37,19 +37,20 @@ class GLVertBuf : public VertBuf {
friend class GLShader; /* For transform feedback. */
private:
// GLuint vbo_id_ = 0;
/** OpenGL buffer handle. Init on first upload. Immutable after that. */
GLuint vbo_id_ = 0;
/** Size on the GPU. */
// size_t vbo_size_ = 0;
size_t vbo_size_ = 0;
public:
void bind(void);
protected:
uchar *acquire_data(void);
uchar *resize_data(void);
void release_data(void);
void upload_data(void);
void duplicate_data(VertBuf *dst);
void acquire_data(void) override;
void resize_data(void) override;
void release_data(void) override;
void upload_data(void) override;
void duplicate_data(VertBuf *dst) override;
MEM_CXX_CLASS_ALLOC_FUNCS("GLVertBuf");
};