GPUDrawList: GL backend isolation
This commit is contained in:
@@ -35,6 +35,7 @@
|
||||
|
||||
#include "GPU_batch.h"
|
||||
#include "GPU_context.h"
|
||||
#include "GPU_drawlist.h"
|
||||
#include "GPU_framebuffer.h"
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_uniformbuffer.h"
|
||||
|
||||
@@ -54,8 +54,6 @@ typedef struct DRWCommandsState {
|
||||
int resource_id;
|
||||
int base_inst;
|
||||
int inst_count;
|
||||
int v_first;
|
||||
int v_count;
|
||||
bool neg_scale;
|
||||
/* Resource location. */
|
||||
int obmats_loc;
|
||||
@@ -714,18 +712,12 @@ BLI_INLINE void draw_indirect_call(DRWShadingGroup *shgroup, DRWCommandsState *s
|
||||
GPU_draw_list_submit(DST.draw_list);
|
||||
draw_geometry_bind(shgroup, state->batch);
|
||||
}
|
||||
GPU_draw_list_command_add(
|
||||
DST.draw_list, state->v_first, state->v_count, state->base_inst, state->inst_count);
|
||||
GPU_draw_list_append(DST.draw_list, state->batch, state->base_inst, state->inst_count);
|
||||
}
|
||||
/* Fallback when unsupported */
|
||||
else {
|
||||
draw_geometry_execute(shgroup,
|
||||
state->batch,
|
||||
state->v_first,
|
||||
state->v_count,
|
||||
state->base_inst,
|
||||
state->inst_count,
|
||||
state->baseinst_loc);
|
||||
draw_geometry_execute(
|
||||
shgroup, state->batch, 0, 0, state->base_inst, state->inst_count, state->baseinst_loc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1015,8 +1007,6 @@ static void draw_call_batching_start(DRWCommandsState *state)
|
||||
state->resource_id = -1;
|
||||
state->base_inst = 0;
|
||||
state->inst_count = 0;
|
||||
state->v_first = 0;
|
||||
state->v_count = 0;
|
||||
state->batch = NULL;
|
||||
|
||||
state->select_id = -1;
|
||||
@@ -1039,15 +1029,10 @@ static void draw_call_batching_do(DRWShadingGroup *shgroup,
|
||||
draw_call_batching_flush(shgroup, state);
|
||||
|
||||
state->batch = call->batch;
|
||||
state->v_first = (call->batch->elem) ? call->batch->elem->index_start : 0;
|
||||
state->v_count = (call->batch->elem) ? call->batch->elem->index_len :
|
||||
call->batch->verts[0]->vertex_len;
|
||||
state->inst_count = 1;
|
||||
state->base_inst = id;
|
||||
|
||||
draw_call_resource_bind(state, &call->handle);
|
||||
|
||||
GPU_draw_list_init(DST.draw_list, state->batch);
|
||||
}
|
||||
/* Is the id consecutive? */
|
||||
else if (id != state->base_inst + state->inst_count) {
|
||||
|
||||
@@ -63,6 +63,7 @@ set(SRC
|
||||
intern/gpu_codegen.c
|
||||
intern/gpu_context.cc
|
||||
intern/gpu_debug.cc
|
||||
intern/gpu_drawlist.cc
|
||||
intern/gpu_element.cc
|
||||
intern/gpu_extensions.cc
|
||||
intern/gpu_framebuffer.cc
|
||||
@@ -89,6 +90,7 @@ set(SRC
|
||||
intern/gpu_viewport.c
|
||||
|
||||
opengl/gl_context.cc
|
||||
opengl/gl_drawlist.cc
|
||||
|
||||
GPU_attr_binding.h
|
||||
GPU_batch.h
|
||||
@@ -98,6 +100,7 @@ set(SRC
|
||||
GPU_common.h
|
||||
GPU_context.h
|
||||
GPU_debug.h
|
||||
GPU_drawlist.h
|
||||
GPU_element.h
|
||||
GPU_extensions.h
|
||||
GPU_framebuffer.h
|
||||
@@ -125,6 +128,7 @@ set(SRC
|
||||
intern/gpu_batch_private.h
|
||||
intern/gpu_codegen.h
|
||||
intern/gpu_context_private.hh
|
||||
intern/gpu_drawlist_private.hh
|
||||
intern/gpu_material_library.h
|
||||
intern/gpu_matrix_private.h
|
||||
intern/gpu_node_graph.h
|
||||
@@ -136,6 +140,7 @@ set(SRC
|
||||
|
||||
opengl/gl_backend.hh
|
||||
opengl/gl_context.hh
|
||||
opengl/gl_drawlist.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
||||
@@ -199,19 +199,6 @@ GPUBatch *create_BatchInGeneral(GPUPrimType, VertexBufferStuff, ElementListStuff
|
||||
|
||||
#endif /* future plans */
|
||||
|
||||
/**
|
||||
* #GPUDrawList is an API to do lots of similar draw-calls very fast using multi-draw-indirect.
|
||||
* There is a fallback if the feature is not supported.
|
||||
*/
|
||||
typedef struct GPUDrawList GPUDrawList;
|
||||
|
||||
GPUDrawList *GPU_draw_list_create(int length);
|
||||
void GPU_draw_list_discard(GPUDrawList *list);
|
||||
void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch);
|
||||
void GPU_draw_list_command_add(
|
||||
GPUDrawList *list, int v_first, int v_count, int i_first, int i_count);
|
||||
void GPU_draw_list_submit(GPUDrawList *list);
|
||||
|
||||
void gpu_batch_init(void);
|
||||
void gpu_batch_exit(void);
|
||||
|
||||
|
||||
46
source/blender/gpu/GPU_drawlist.h
Normal file
46
source/blender/gpu/GPU_drawlist.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2020 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPUDrawList is an API to do lots of similar draw-calls very fast using
|
||||
* multi-draw-indirect. There is a fallback if the feature is not supported.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct GPUBatch;
|
||||
|
||||
typedef void *GPUDrawList; /* Opaque pointer. */
|
||||
|
||||
/* Create a list with at least length drawcalls. Length can affect performance. */
|
||||
GPUDrawList GPU_draw_list_create(int length);
|
||||
void GPU_draw_list_discard(GPUDrawList list);
|
||||
|
||||
void GPU_draw_list_append(GPUDrawList list, GPUBatch *batch, int i_first, int i_count);
|
||||
void GPU_draw_list_submit(GPUDrawList list);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -25,13 +25,21 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
struct GPUContext;
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_drawlist_private.hh"
|
||||
|
||||
namespace blender {
|
||||
namespace gpu {
|
||||
|
||||
class GPUBackend {
|
||||
public:
|
||||
virtual ~GPUBackend(){};
|
||||
|
||||
static GPUBackend *get(void);
|
||||
|
||||
virtual GPUContext *context_alloc(void *ghost_window) = 0;
|
||||
virtual DrawList *drawlist_alloc(int list_length) = 0;
|
||||
};
|
||||
|
||||
GPUBackend *gpu_backend_get(void);
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
|
||||
@@ -795,193 +795,6 @@ void GPU_draw_primitive(GPUPrimType prim_type, int v_count)
|
||||
// glBindVertexArray(0);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Indirect Draw Calls
|
||||
* \{ */
|
||||
|
||||
#if 0
|
||||
# define USE_MULTI_DRAW_INDIRECT 0
|
||||
#else
|
||||
# define USE_MULTI_DRAW_INDIRECT \
|
||||
(GL_ARB_multi_draw_indirect && GPU_arb_base_instance_is_supported())
|
||||
#endif
|
||||
|
||||
typedef struct GPUDrawCommand {
|
||||
uint v_count;
|
||||
uint i_count;
|
||||
uint v_first;
|
||||
uint i_first;
|
||||
} GPUDrawCommand;
|
||||
|
||||
typedef struct GPUDrawCommandIndexed {
|
||||
uint v_count;
|
||||
uint i_count;
|
||||
uint v_first;
|
||||
uint base_index;
|
||||
uint i_first;
|
||||
} GPUDrawCommandIndexed;
|
||||
|
||||
struct GPUDrawList {
|
||||
GPUBatch *batch;
|
||||
uint base_index; /* Avoid dereferencing batch. */
|
||||
uint cmd_offset; /* in bytes, offset inside indirect command buffer. */
|
||||
uint cmd_len; /* Number of used command for the next call. */
|
||||
uint buffer_size; /* in bytes, size of indirect command buffer. */
|
||||
GLuint buffer_id; /* Draw Indirect Buffer id */
|
||||
union {
|
||||
GPUDrawCommand *commands;
|
||||
GPUDrawCommandIndexed *commands_indexed;
|
||||
};
|
||||
};
|
||||
|
||||
GPUDrawList *GPU_draw_list_create(int length)
|
||||
{
|
||||
GPUDrawList *list = (GPUDrawList *)MEM_callocN(sizeof(GPUDrawList), "GPUDrawList");
|
||||
/* Alloc the biggest possible command list which is indexed. */
|
||||
list->buffer_size = sizeof(GPUDrawCommandIndexed) * length;
|
||||
if (USE_MULTI_DRAW_INDIRECT) {
|
||||
list->buffer_id = GPU_buf_alloc();
|
||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id);
|
||||
glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW);
|
||||
}
|
||||
else {
|
||||
list->commands = (GPUDrawCommand *)MEM_mallocN(list->buffer_size, "GPUDrawList data");
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void GPU_draw_list_discard(GPUDrawList *list)
|
||||
{
|
||||
if (list->buffer_id) {
|
||||
GPU_buf_free(list->buffer_id);
|
||||
}
|
||||
else {
|
||||
MEM_SAFE_FREE(list->commands);
|
||||
}
|
||||
MEM_freeN(list);
|
||||
}
|
||||
|
||||
void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch)
|
||||
{
|
||||
BLI_assert(batch->phase == GPU_BATCH_READY_TO_DRAW);
|
||||
list->batch = batch;
|
||||
list->base_index = batch->elem ? BASE_INDEX(batch->elem) : UINT_MAX;
|
||||
list->cmd_len = 0;
|
||||
|
||||
if (USE_MULTI_DRAW_INDIRECT) {
|
||||
if (list->commands == NULL) {
|
||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id);
|
||||
if (list->cmd_offset >= list->buffer_size) {
|
||||
/* Orphan buffer data and start fresh. */
|
||||
glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW);
|
||||
list->cmd_offset = 0;
|
||||
}
|
||||
GLenum flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
|
||||
list->commands = (GPUDrawCommand *)glMapBufferRange(
|
||||
GL_DRAW_INDIRECT_BUFFER, list->cmd_offset, list->buffer_size - list->cmd_offset, flags);
|
||||
}
|
||||
}
|
||||
else {
|
||||
list->cmd_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_draw_list_command_add(
|
||||
GPUDrawList *list, int v_first, int v_count, int i_first, int i_count)
|
||||
{
|
||||
BLI_assert(list->commands);
|
||||
|
||||
if (v_count == 0 || i_count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (list->base_index != UINT_MAX) {
|
||||
GPUDrawCommandIndexed *cmd = list->commands_indexed + list->cmd_len;
|
||||
cmd->v_first = v_first;
|
||||
cmd->v_count = v_count;
|
||||
cmd->i_count = i_count;
|
||||
cmd->base_index = list->base_index;
|
||||
cmd->i_first = i_first;
|
||||
}
|
||||
else {
|
||||
GPUDrawCommand *cmd = list->commands + list->cmd_len;
|
||||
cmd->v_first = v_first;
|
||||
cmd->v_count = v_count;
|
||||
cmd->i_count = i_count;
|
||||
cmd->i_first = i_first;
|
||||
}
|
||||
|
||||
list->cmd_len++;
|
||||
uint offset = list->cmd_offset + list->cmd_len * sizeof(GPUDrawCommandIndexed);
|
||||
|
||||
if (offset == list->buffer_size) {
|
||||
GPU_draw_list_submit(list);
|
||||
GPU_draw_list_init(list, list->batch);
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_draw_list_submit(GPUDrawList *list)
|
||||
{
|
||||
GPUBatch *batch = list->batch;
|
||||
|
||||
if (list->cmd_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_assert(list->commands);
|
||||
BLI_assert(batch->program_in_use);
|
||||
/* TODO could assert that VAO is bound. */
|
||||
|
||||
/* TODO We loose a bit of memory here if we only draw arrays. Fix that. */
|
||||
uintptr_t offset = list->cmd_offset;
|
||||
uint cmd_len = list->cmd_len;
|
||||
size_t bytes_used = cmd_len * sizeof(GPUDrawCommandIndexed);
|
||||
list->cmd_len = 0; /* Avoid reuse. */
|
||||
|
||||
/* Only do multi-draw indirect if doing more than 2 drawcall.
|
||||
* This avoids the overhead of buffer mapping if scene is
|
||||
* not very instance friendly.
|
||||
* BUT we also need to take into account the case where only
|
||||
* a few instances are needed to finish filling a call buffer. */
|
||||
const bool do_mdi = (cmd_len > 2) || (list->cmd_offset + bytes_used == list->buffer_size);
|
||||
|
||||
if (USE_MULTI_DRAW_INDIRECT && do_mdi) {
|
||||
GLenum prim = batch->gl_prim_type;
|
||||
|
||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id);
|
||||
glFlushMappedBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, bytes_used);
|
||||
glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER);
|
||||
list->commands = NULL; /* Unmapped */
|
||||
list->cmd_offset += bytes_used;
|
||||
|
||||
if (batch->elem) {
|
||||
glMultiDrawElementsIndirect(prim, INDEX_TYPE(batch->elem), (void *)offset, cmd_len, 0);
|
||||
}
|
||||
else {
|
||||
glMultiDrawArraysIndirect(prim, (void *)offset, cmd_len, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Fallback */
|
||||
if (batch->elem) {
|
||||
GPUDrawCommandIndexed *cmd = list->commands_indexed;
|
||||
for (int i = 0; i < cmd_len; i++, cmd++) {
|
||||
/* Index start was added by Draw manager. Avoid counting it twice. */
|
||||
cmd->v_first -= batch->elem->index_start;
|
||||
GPU_batch_draw_advanced(batch, cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count);
|
||||
}
|
||||
}
|
||||
else {
|
||||
GPUDrawCommand *cmd = list->commands;
|
||||
for (int i = 0; i < cmd_len; i++, cmd++) {
|
||||
GPU_batch_draw_advanced(batch, cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utilities
|
||||
* \{ */
|
||||
|
||||
@@ -83,12 +83,12 @@ bool GPUContext::is_active_on_thread(void)
|
||||
|
||||
GPUContext *GPU_context_create(void *ghost_window)
|
||||
{
|
||||
if (gpu_backend_get() == NULL) {
|
||||
if (GPUBackend::get() == NULL) {
|
||||
/* TODO move where it make sense. */
|
||||
GPU_backend_init(GPU_BACKEND_OPENGL);
|
||||
}
|
||||
|
||||
GPUContext *ctx = gpu_backend_get()->context_alloc(ghost_window);
|
||||
GPUContext *ctx = GPUBackend::get()->context_alloc(ghost_window);
|
||||
|
||||
GPU_context_active_set(ctx);
|
||||
return ctx;
|
||||
@@ -173,14 +173,14 @@ void GPU_fbo_free(GLuint fbo_id, GPUContext *ctx)
|
||||
void GPU_buf_free(GLuint buf_id)
|
||||
{
|
||||
/* TODO avoid using backend */
|
||||
GPUBackend *backend = gpu_backend_get();
|
||||
GPUBackend *backend = GPUBackend::get();
|
||||
static_cast<GLBackend *>(backend)->buf_free(buf_id);
|
||||
}
|
||||
|
||||
void GPU_tex_free(GLuint tex_id)
|
||||
{
|
||||
/* TODO avoid using backend */
|
||||
GPUBackend *backend = gpu_backend_get();
|
||||
GPUBackend *backend = GPUBackend::get();
|
||||
static_cast<GLBackend *>(backend)->tex_free(tex_id);
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ void GPU_backend_exit(void)
|
||||
delete g_backend;
|
||||
}
|
||||
|
||||
GPUBackend *gpu_backend_get(void)
|
||||
GPUBackend *GPUBackend::get(void)
|
||||
{
|
||||
return g_backend;
|
||||
}
|
||||
|
||||
59
source/blender/gpu/intern/gpu_drawlist.cc
Normal file
59
source/blender/gpu/intern/gpu_drawlist.cc
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2016 by Mike Erwin.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Implementation of Multi Draw Indirect.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
#include "GPU_drawlist.h"
|
||||
|
||||
#include "gpu_backend.hh"
|
||||
|
||||
#include "gpu_drawlist_private.hh"
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
GPUDrawList GPU_draw_list_create(int list_length)
|
||||
{
|
||||
DrawList *list_ptr = GPUBackend::get()->drawlist_alloc(list_length);
|
||||
return reinterpret_cast<DrawList *>(list_ptr);
|
||||
}
|
||||
|
||||
void GPU_draw_list_discard(GPUDrawList list)
|
||||
{
|
||||
DrawList *list_ptr = reinterpret_cast<DrawList *>(list);
|
||||
delete list_ptr;
|
||||
}
|
||||
|
||||
void GPU_draw_list_append(GPUDrawList list, GPUBatch *batch, int i_first, int i_count)
|
||||
{
|
||||
DrawList *list_ptr = reinterpret_cast<DrawList *>(list);
|
||||
list_ptr->append(batch, i_first, i_count);
|
||||
}
|
||||
|
||||
void GPU_draw_list_submit(GPUDrawList list)
|
||||
{
|
||||
DrawList *list_ptr = reinterpret_cast<DrawList *>(list);
|
||||
list_ptr->submit();
|
||||
}
|
||||
40
source/blender/gpu/intern/gpu_drawlist_private.hh
Normal file
40
source/blender/gpu/intern/gpu_drawlist_private.hh
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2020 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender {
|
||||
namespace gpu {
|
||||
|
||||
class DrawList {
|
||||
public:
|
||||
virtual ~DrawList(){};
|
||||
|
||||
virtual void append(GPUBatch *batch, int i_first, int i_count) = 0;
|
||||
virtual void submit() = 0;
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "gl_context.hh"
|
||||
#include "gl_drawlist.hh"
|
||||
|
||||
namespace blender {
|
||||
namespace gpu {
|
||||
@@ -42,6 +43,11 @@ class GLBackend : public GPUBackend {
|
||||
return new GLContext(ghost_window, shared_orphan_list_);
|
||||
};
|
||||
|
||||
DrawList *drawlist_alloc(int list_length)
|
||||
{
|
||||
return new GLDrawList(list_length);
|
||||
};
|
||||
|
||||
/* TODO remove */
|
||||
void buf_free(GLuint buf_id);
|
||||
void tex_free(GLuint tex_id);
|
||||
|
||||
238
source/blender/gpu/opengl/gl_drawlist.cc
Normal file
238
source/blender/gpu/opengl/gl_drawlist.cc
Normal file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2016 by Mike Erwin.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Implementation of Multi Draw Indirect using OpenGL.
|
||||
* Fallback if the needed extensions are not supported.
|
||||
*/
|
||||
|
||||
#include "BLI_assert.h"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
#include "GPU_extensions.h"
|
||||
|
||||
#include "glew-mx.h"
|
||||
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_drawlist_private.hh"
|
||||
|
||||
#include "gl_backend.hh"
|
||||
#include "gl_drawlist.hh"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#define USE_MULTI_DRAW_INDIRECT 1
|
||||
|
||||
/* TODO remove. */
|
||||
#if GPU_TRACK_INDEX_RANGE
|
||||
# define BASE_INDEX(el) ((el)->base_index)
|
||||
# define INDEX_TYPE(el) ((el)->gl_index_type)
|
||||
#else
|
||||
# define BASE_INDEX(el) 0
|
||||
# define INDEX_TYPE(el) GL_UNSIGNED_INT
|
||||
#endif
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
typedef struct GLDrawCommand {
|
||||
GLuint v_count;
|
||||
GLuint i_count;
|
||||
GLuint v_first;
|
||||
GLuint i_first;
|
||||
} GLDrawCommand;
|
||||
|
||||
typedef struct GLDrawCommandIndexed {
|
||||
GLuint v_count;
|
||||
GLuint i_count;
|
||||
GLuint v_first;
|
||||
GLuint base_index;
|
||||
GLuint i_first;
|
||||
} GLDrawCommandIndexed;
|
||||
|
||||
#define MDI_ENABLED (buffer_size_ != 0)
|
||||
#define MDI_DISABLED (buffer_size_ == 0)
|
||||
#define MDI_INDEXED (base_index_ != UINT_MAX)
|
||||
|
||||
GLDrawList::GLDrawList(int length)
|
||||
{
|
||||
BLI_assert(length > 0);
|
||||
batch_ = NULL;
|
||||
buffer_id_ = 0;
|
||||
command_len_ = 0;
|
||||
command_offset_ = 0;
|
||||
data_offset_ = 0;
|
||||
data_size_ = 0;
|
||||
data_ = NULL;
|
||||
|
||||
if (USE_MULTI_DRAW_INDIRECT && GLEW_ARB_multi_draw_indirect &&
|
||||
GPU_arb_base_instance_is_supported()) {
|
||||
/* Alloc the biggest possible command list, which is indexed. */
|
||||
buffer_size_ = sizeof(GLDrawCommandIndexed) * length;
|
||||
}
|
||||
else {
|
||||
/* Indicates MDI is not supported. */
|
||||
buffer_size_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GLDrawList::~GLDrawList()
|
||||
{
|
||||
/* TODO This ... */
|
||||
static_cast<GLBackend *>(GPUBackend::get())->buf_free(buffer_id_);
|
||||
/* ... should be this. */
|
||||
// context_->buf_free(buffer_id_)
|
||||
}
|
||||
|
||||
void GLDrawList::init(void)
|
||||
{
|
||||
BLI_assert(GPU_context_active_get());
|
||||
BLI_assert(MDI_ENABLED);
|
||||
BLI_assert(data_ == NULL);
|
||||
batch_ = NULL;
|
||||
command_len_ = 0;
|
||||
|
||||
if (buffer_id_ == 0) {
|
||||
/* Allocate on first use. */
|
||||
glGenBuffers(1, &buffer_id_);
|
||||
context_ = static_cast<GLContext *>(GPU_context_active_get());
|
||||
}
|
||||
|
||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_);
|
||||
/* If buffer is full, orphan buffer data and start fresh. */
|
||||
// if (command_offset_ >= data_size_) {
|
||||
glBufferData(GL_DRAW_INDIRECT_BUFFER, buffer_size_, NULL, GL_DYNAMIC_DRAW);
|
||||
data_offset_ = 0;
|
||||
// }
|
||||
/* Map the remaining range. */
|
||||
GLbitfield flag = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
|
||||
data_size_ = buffer_size_ - data_offset_;
|
||||
data_ = (GLbyte *)glMapBufferRange(GL_DRAW_INDIRECT_BUFFER, data_offset_, data_size_, flag);
|
||||
command_offset_ = 0;
|
||||
}
|
||||
|
||||
void GLDrawList::append(GPUBatch *batch, int i_first, int i_count)
|
||||
{
|
||||
/* Fallback when MultiDrawIndirect is not supported/enabled. */
|
||||
if (MDI_DISABLED) {
|
||||
GPU_batch_draw_advanced(batch, 0, 0, i_first, i_count);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data_ == NULL) {
|
||||
this->init();
|
||||
}
|
||||
|
||||
if (batch != batch_) {
|
||||
// BLI_assert(batch->flag | GPU_BATCH_INIT);
|
||||
this->submit();
|
||||
batch_ = batch;
|
||||
/* Cached for faster access. */
|
||||
base_index_ = batch->elem ? BASE_INDEX(batch->elem) : UINT_MAX;
|
||||
v_first_ = batch->elem ? batch->elem->index_start : 0;
|
||||
v_count_ = batch->elem ? batch->elem->index_len : batch->verts[0]->vertex_len;
|
||||
}
|
||||
|
||||
if (MDI_INDEXED) {
|
||||
GLDrawCommandIndexed *cmd = reinterpret_cast<GLDrawCommandIndexed *>(data_ + command_offset_);
|
||||
cmd->v_first = v_first_;
|
||||
cmd->v_count = v_count_;
|
||||
cmd->i_count = i_count;
|
||||
cmd->base_index = base_index_;
|
||||
cmd->i_first = i_first;
|
||||
command_offset_ += sizeof(GLDrawCommandIndexed);
|
||||
}
|
||||
else {
|
||||
GLDrawCommand *cmd = reinterpret_cast<GLDrawCommand *>(data_ + command_offset_);
|
||||
cmd->v_first = v_first_;
|
||||
cmd->v_count = v_count_;
|
||||
cmd->i_count = i_count;
|
||||
cmd->i_first = i_first;
|
||||
command_offset_ += sizeof(GLDrawCommand);
|
||||
}
|
||||
|
||||
command_len_++;
|
||||
|
||||
if (command_offset_ >= data_size_) {
|
||||
this->submit();
|
||||
}
|
||||
}
|
||||
|
||||
void GLDrawList::submit(void)
|
||||
{
|
||||
if (command_len_ == 0) {
|
||||
return;
|
||||
}
|
||||
/* Something's wrong if we get here without MDI support. */
|
||||
BLI_assert(MDI_ENABLED);
|
||||
BLI_assert(data_);
|
||||
/* TODO fix this assert */
|
||||
// BLI_assert(batch_->program_in_use);
|
||||
/* TODO could assert that VAO is bound. */
|
||||
|
||||
/* Only do multi-draw indirect if doing more than 2 drawcall. This avoids the overhead of
|
||||
* buffer mapping if scene is not very instance friendly. BUT we also need to take into
|
||||
* account the
|
||||
* case where only a few instances are needed to finish filling a call buffer. */
|
||||
const bool is_finishing_a_buffer = (command_offset_ >= data_size_);
|
||||
if (command_len_ > 2 || is_finishing_a_buffer) {
|
||||
// GLenum prim = convert_prim_type_to_gl(batch_->prim_type);
|
||||
GLenum prim = batch_->gl_prim_type;
|
||||
void *offset = (void *)data_offset_;
|
||||
|
||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_);
|
||||
glFlushMappedBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, command_offset_);
|
||||
glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER);
|
||||
data_ = NULL; /* Unmapped */
|
||||
data_offset_ += command_offset_;
|
||||
|
||||
if (MDI_INDEXED) {
|
||||
glMultiDrawElementsIndirect(prim, INDEX_TYPE(batch_->elem), offset, command_len_, 0);
|
||||
}
|
||||
else {
|
||||
glMultiDrawArraysIndirect(prim, offset, command_len_, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Fallback do simple drawcalls, and don't unmap the buffer. */
|
||||
if (MDI_INDEXED) {
|
||||
GLDrawCommandIndexed *cmd = (GLDrawCommandIndexed *)data_;
|
||||
for (int i = 0; i < command_len_; i++, cmd++) {
|
||||
/* Index start was already added. Avoid counting it twice. */
|
||||
cmd->v_first -= batch_->elem->index_start;
|
||||
GPU_batch_draw_advanced(batch_, cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count);
|
||||
}
|
||||
/* Reuse the same data. */
|
||||
command_offset_ -= command_len_ * sizeof(GLDrawCommandIndexed);
|
||||
}
|
||||
else {
|
||||
GLDrawCommand *cmd = (GLDrawCommand *)data_;
|
||||
for (int i = 0; i < command_len_; i++, cmd++) {
|
||||
GPU_batch_draw_advanced(batch_, cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count);
|
||||
}
|
||||
/* Reuse the same data. */
|
||||
command_offset_ -= command_len_ * sizeof(GLDrawCommand);
|
||||
}
|
||||
}
|
||||
/* Do not submit this buffer again. */
|
||||
command_len_ = 0;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
80
source/blender/gpu/opengl/gl_drawlist.hh
Normal file
80
source/blender/gpu/opengl/gl_drawlist.hh
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2020 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
#include "GPU_glew.h"
|
||||
|
||||
#include "gpu_drawlist_private.hh"
|
||||
|
||||
#include "gl_context.hh"
|
||||
|
||||
namespace blender {
|
||||
namespace gpu {
|
||||
|
||||
class GLDrawList : public DrawList {
|
||||
public:
|
||||
GLDrawList(int length);
|
||||
~GLDrawList();
|
||||
|
||||
void append(GPUBatch *batch, int i_first, int i_count) override;
|
||||
void submit(void) override;
|
||||
|
||||
private:
|
||||
void init(void);
|
||||
|
||||
/** Batch for which we are recording commands for. */
|
||||
GPUBatch *batch_;
|
||||
/** Mapped memory bounds. */
|
||||
GLbyte *data_;
|
||||
/** Length of the mapped buffer (in byte). */
|
||||
GLsizeiptr data_size_;
|
||||
/** Current offset inside the mapped buffer (in byte). */
|
||||
GLintptr command_offset_;
|
||||
/** Current number of command recorded inside the mapped buffer. */
|
||||
uint command_len_;
|
||||
/** Is UINT_MAX if not drawing indexed geom. Also Avoid dereferencing batch. */
|
||||
GLuint base_index_;
|
||||
/** Also Avoid dereferencing batch. */
|
||||
GLuint v_first_, v_count_;
|
||||
|
||||
/** GL Indirect Buffer id. 0 means MultiDrawIndirect is not supported/enabled. */
|
||||
GLuint buffer_id_;
|
||||
/** Length of whole the buffer (in byte). */
|
||||
GLsizeiptr buffer_size_;
|
||||
/** Offset of data_ inside the whole buffer (in byte). */
|
||||
GLintptr data_offset_;
|
||||
|
||||
/** To free the buffer_id_. */
|
||||
GLContext *context_;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLDrawList");
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user