This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/gpu/intern/gpu_batch.cc
Clément Foucault 65ad36f5fd DRWManager: New implementation.
This is a new implementation of the draw manager using modern
rendering practices and GPU driven culling.

This only ports features that are not considered deprecated or to be
removed.

The old DRW API is kept working along side this new one, and does not
interfeer with it. However this needed some more hacking inside the
draw_view_lib.glsl. At least the create info are well separated.

The reviewer might start by looking at `draw_pass_test.cc` to see the
API in usage.

Important files are `draw_pass.hh`, `draw_command.hh`,
`draw_command_shared.hh`.

In a nutshell (for a developper used to old DRW API):
- `DRWShadingGroups` are replaced by `Pass<T>::Sub`.
- Contrary to DRWShadingGroups, all commands recorded inside a pass or
   sub-pass (even binds / push_constant / uniforms) will be executed in order.
- All memory is managed per object (except for Sub-Pass which are managed
   by their parent pass) and not from draw manager pools. So passes "can"
   potentially be recorded once and submitted multiple time (but this is
   not really encouraged for now). The only implicit link is between resource
   lifetime and `ResourceHandles`
- Sub passes can be any level deep.
- IMPORTANT: All state propagate from sub pass to subpass. There is no
   state stack concept anymore. Ensure the correct render state is set before
   drawing anything using `Pass::state_set()`.
- The drawcalls now needs a `ResourceHandle` instead of an `Object *`.
   This is to remove any implicit dependency between `Pass` and `Manager`.
   This was a huge problem in old implementation since the manager did not
   know what to pull from the object. Now it is explicitly requested by the
   engine.
- The pases need to be submitted to a `draw::Manager` instance which can
   be retrieved using `DRW_manager_get()` (for now).

Internally:
- All object data are stored in contiguous storage buffers. Removing a lot
   of complexity in the pass submission.
- Draw calls are sorted and visibility tested on GPU. Making more modern
   culling and better instancing usage possible in the future.
- Unit Tests have been added for regression testing and avoid most API
   breakage.
- `draw::View` now contains culling data for all objects in the scene
   allowing caching for multiple views.
- Bounding box and sphere final setup is moved to GPU.
- Some global resources locations have been hardcoded to reduce complexity.

What is missing:
- ~~Workaround for lack of gl_BaseInstanceARB.~~ Done
- ~~Object Uniform Attributes.~~ Done (Not in this patch)
- Workaround for hardware supporting a maximum of 8 SSBO.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D15817
2022-09-02 18:45:14 +02:00

364 lines
9.6 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2016 by Mike Erwin. All rights reserved. */
/** \file
* \ingroup gpu
*
* GPU geometry batch
* Contains VAOs + VBOs + Shader representing a drawable entity.
*/
#include "MEM_guardedalloc.h"
#include "BLI_math_base.h"
#include "GPU_batch.h"
#include "GPU_batch_presets.h"
#include "GPU_platform.h"
#include "GPU_shader.h"
#include "gpu_backend.hh"
#include "gpu_context_private.hh"
#include "gpu_index_buffer_private.hh"
#include "gpu_shader_private.hh"
#include "gpu_vertex_buffer_private.hh"
#include "gpu_batch_private.hh"
#include <cstring>
using namespace blender::gpu;
/* -------------------------------------------------------------------- */
/** \name Creation & Deletion
* \{ */
GPUBatch *GPU_batch_calloc()
{
GPUBatch *batch = GPUBackend::get()->batch_alloc();
memset(batch, 0, sizeof(*batch));
return batch;
}
GPUBatch *GPU_batch_create_ex(GPUPrimType prim_type,
GPUVertBuf *verts,
GPUIndexBuf *elem,
eGPUBatchFlag owns_flag)
{
GPUBatch *batch = GPU_batch_calloc();
GPU_batch_init_ex(batch, prim_type, verts, elem, owns_flag);
return batch;
}
void GPU_batch_init_ex(GPUBatch *batch,
GPUPrimType prim_type,
GPUVertBuf *verts,
GPUIndexBuf *elem,
eGPUBatchFlag owns_flag)
{
BLI_assert(verts != nullptr);
/* Do not pass any other flag */
BLI_assert((owns_flag & ~(GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX)) == 0);
batch->verts[0] = verts;
for (int v = 1; v < GPU_BATCH_VBO_MAX_LEN; v++) {
batch->verts[v] = nullptr;
}
for (auto &v : batch->inst) {
v = nullptr;
}
batch->elem = elem;
batch->prim_type = prim_type;
batch->flag = owns_flag | GPU_BATCH_INIT | GPU_BATCH_DIRTY;
batch->shader = nullptr;
}
void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src)
{
GPU_batch_init_ex(
batch_dst, GPU_PRIM_POINTS, batch_src->verts[0], batch_src->elem, GPU_BATCH_INVALID);
batch_dst->prim_type = batch_src->prim_type;
for (int v = 1; v < GPU_BATCH_VBO_MAX_LEN; v++) {
batch_dst->verts[v] = batch_src->verts[v];
}
}
void GPU_batch_clear(GPUBatch *batch)
{
if (batch->flag & GPU_BATCH_OWNS_INDEX) {
GPU_indexbuf_discard(batch->elem);
}
if (batch->flag & GPU_BATCH_OWNS_VBO_ANY) {
for (int v = 0; (v < GPU_BATCH_VBO_MAX_LEN) && batch->verts[v]; v++) {
if (batch->flag & (GPU_BATCH_OWNS_VBO << v)) {
GPU_VERTBUF_DISCARD_SAFE(batch->verts[v]);
}
}
}
if (batch->flag & GPU_BATCH_OWNS_INST_VBO_ANY) {
for (int v = 0; (v < GPU_BATCH_INST_VBO_MAX_LEN) && batch->inst[v]; v++) {
if (batch->flag & (GPU_BATCH_OWNS_INST_VBO << v)) {
GPU_VERTBUF_DISCARD_SAFE(batch->inst[v]);
}
}
}
batch->flag = GPU_BATCH_INVALID;
}
void GPU_batch_discard(GPUBatch *batch)
{
GPU_batch_clear(batch);
delete static_cast<Batch *>(batch);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Buffers Management
* \{ */
void GPU_batch_instbuf_set(GPUBatch *batch, GPUVertBuf *inst, bool own_vbo)
{
BLI_assert(inst);
batch->flag |= GPU_BATCH_DIRTY;
if (batch->inst[0] && (batch->flag & GPU_BATCH_OWNS_INST_VBO)) {
GPU_vertbuf_discard(batch->inst[0]);
}
batch->inst[0] = inst;
SET_FLAG_FROM_TEST(batch->flag, own_vbo, GPU_BATCH_OWNS_INST_VBO);
}
void GPU_batch_elembuf_set(GPUBatch *batch, GPUIndexBuf *elem, bool own_ibo)
{
BLI_assert(elem);
batch->flag |= GPU_BATCH_DIRTY;
if (batch->elem && (batch->flag & GPU_BATCH_OWNS_INDEX)) {
GPU_indexbuf_discard(batch->elem);
}
batch->elem = elem;
SET_FLAG_FROM_TEST(batch->flag, own_ibo, GPU_BATCH_OWNS_INDEX);
}
int GPU_batch_instbuf_add_ex(GPUBatch *batch, GPUVertBuf *insts, bool own_vbo)
{
BLI_assert(insts);
batch->flag |= GPU_BATCH_DIRTY;
for (uint v = 0; v < GPU_BATCH_INST_VBO_MAX_LEN; v++) {
if (batch->inst[v] == nullptr) {
/* for now all VertexBuffers must have same vertex_len */
if (batch->inst[0]) {
/* Allow for different size of vertex buffer (will choose the smallest number of verts). */
// BLI_assert(insts->vertex_len == batch->inst[0]->vertex_len);
}
batch->inst[v] = insts;
SET_FLAG_FROM_TEST(batch->flag, own_vbo, (eGPUBatchFlag)(GPU_BATCH_OWNS_INST_VBO << v));
return v;
}
}
/* we only make it this far if there is no room for another GPUVertBuf */
BLI_assert_msg(0, "Not enough Instance VBO slot in batch");
return -1;
}
int GPU_batch_vertbuf_add_ex(GPUBatch *batch, GPUVertBuf *verts, bool own_vbo)
{
BLI_assert(verts);
batch->flag |= GPU_BATCH_DIRTY;
for (uint v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
if (batch->verts[v] == nullptr) {
/* for now all VertexBuffers must have same vertex_len */
if (batch->verts[0] != nullptr) {
/* This is an issue for the HACK inside DRW_vbo_request(). */
// BLI_assert(verts->vertex_len == batch->verts[0]->vertex_len);
}
batch->verts[v] = verts;
SET_FLAG_FROM_TEST(batch->flag, own_vbo, (eGPUBatchFlag)(GPU_BATCH_OWNS_VBO << v));
return v;
}
}
/* we only make it this far if there is no room for another GPUVertBuf */
BLI_assert_msg(0, "Not enough VBO slot in batch");
return -1;
}
bool GPU_batch_vertbuf_has(GPUBatch *batch, GPUVertBuf *verts)
{
for (uint v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
if (batch->verts[v] == verts) {
return true;
}
}
return false;
}
void GPU_batch_resource_id_buf_set(GPUBatch *batch, GPUStorageBuf *resource_id_buf)
{
BLI_assert(resource_id_buf);
batch->flag |= GPU_BATCH_DIRTY;
batch->resource_id_buf = resource_id_buf;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Uniform setters
*
* TODO(fclem): port this to GPUShader.
* \{ */
void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader)
{
batch->shader = shader;
GPU_shader_bind(batch->shader);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Drawing / Drawcall functions
* \{ */
void GPU_batch_draw_parameter_get(
GPUBatch *gpu_batch, int *r_v_count, int *r_v_first, int *r_base_index, int *r_i_count)
{
Batch *batch = static_cast<Batch *>(gpu_batch);
if (batch->elem) {
*r_v_count = batch->elem_()->index_len_get();
*r_v_first = batch->elem_()->index_start_get();
*r_base_index = batch->elem_()->index_base_get();
}
else {
*r_v_count = batch->verts_(0)->vertex_len;
*r_v_first = 0;
*r_base_index = -1;
}
int i_count = (batch->inst[0]) ? batch->inst_(0)->vertex_len : 1;
/* Meh. This is to be able to use different numbers of verts in instance VBO's. */
if (batch->inst[1] != nullptr) {
i_count = min_ii(i_count, batch->inst_(1)->vertex_len);
}
*r_i_count = i_count;
}
void GPU_batch_draw(GPUBatch *batch)
{
GPU_shader_bind(batch->shader);
GPU_batch_draw_advanced(batch, 0, 0, 0, 0);
}
void GPU_batch_draw_range(GPUBatch *batch, int v_first, int v_count)
{
GPU_shader_bind(batch->shader);
GPU_batch_draw_advanced(batch, v_first, v_count, 0, 0);
}
void GPU_batch_draw_instanced(GPUBatch *batch, int i_count)
{
BLI_assert(batch->inst[0] == nullptr);
GPU_shader_bind(batch->shader);
GPU_batch_draw_advanced(batch, 0, 0, 0, i_count);
}
void GPU_batch_draw_advanced(
GPUBatch *gpu_batch, int v_first, int v_count, int i_first, int i_count)
{
BLI_assert(Context::get()->shader != nullptr);
Batch *batch = static_cast<Batch *>(gpu_batch);
if (v_count == 0) {
if (batch->elem) {
v_count = batch->elem_()->index_len_get();
}
else {
v_count = batch->verts_(0)->vertex_len;
}
}
if (i_count == 0) {
i_count = (batch->inst[0]) ? batch->inst_(0)->vertex_len : 1;
/* Meh. This is to be able to use different numbers of verts in instance VBO's. */
if (batch->inst[1] != nullptr) {
i_count = min_ii(i_count, batch->inst_(1)->vertex_len);
}
}
if (v_count == 0 || i_count == 0) {
/* Nothing to draw. */
return;
}
batch->draw(v_first, v_count, i_first, i_count);
}
void GPU_batch_draw_indirect(GPUBatch *gpu_batch, GPUStorageBuf *indirect_buf, intptr_t offset)
{
BLI_assert(Context::get()->shader != nullptr);
BLI_assert(indirect_buf != nullptr);
Batch *batch = static_cast<Batch *>(gpu_batch);
batch->draw_indirect(indirect_buf, offset);
}
void GPU_batch_multi_draw_indirect(
GPUBatch *gpu_batch, GPUStorageBuf *indirect_buf, int count, intptr_t offset, intptr_t stride)
{
BLI_assert(Context::get()->shader != nullptr);
BLI_assert(indirect_buf != nullptr);
Batch *batch = static_cast<Batch *>(gpu_batch);
batch->multi_draw_indirect(indirect_buf, count, offset, stride);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Utilities
* \{ */
void GPU_batch_program_set_builtin_with_config(GPUBatch *batch,
eGPUBuiltinShader shader_id,
eGPUShaderConfig sh_cfg)
{
GPUShader *shader = GPU_shader_get_builtin_shader_with_config(shader_id, sh_cfg);
GPU_batch_set_shader(batch, shader);
}
void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id)
{
GPU_batch_program_set_builtin_with_config(batch, shader_id, GPU_SHADER_CFG_DEFAULT);
}
void GPU_batch_program_set_imm_shader(GPUBatch *batch)
{
GPU_batch_set_shader(batch, immGetShader());
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Init/Exit
* \{ */
void gpu_batch_init()
{
gpu_batch_presets_init();
}
void gpu_batch_exit()
{
gpu_batch_presets_exit();
}
/** \} */