tornavis/source/blender/draw/intern/draw_manager.hh

331 lines
11 KiB
C++

/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup draw
*
* `draw::Manager` is the interface between scene data and viewport engines.
*
* It holds per component data (`ObjectInfo`, `ObjectMatrices`, ...) indexed per `ResourceHandle`.
*
* \note It is currently work in progress and should replace the old global draw manager.
*/
#include "BLI_listbase_wrapper.hh"
#include "BLI_sys_types.h"
#include "GPU_material.h"
#include "draw_resource.hh"
#include "draw_view.hh"
#include <string>
namespace blender::draw {
/* Forward declarations. */
namespace detail {
template<typename T> class Pass;
} // namespace detail
namespace command {
class DrawCommandBuf;
class DrawMultiBuf;
} // namespace command
using PassSimple = detail::Pass<command::DrawCommandBuf>;
using PassMain = detail::Pass<command::DrawMultiBuf>;
class PassSortable;
class Manager {
using ObjectMatricesBuf = StorageArrayBuffer<ObjectMatrices, 128>;
using ObjectBoundsBuf = StorageArrayBuffer<ObjectBounds, 128>;
using ObjectInfosBuf = StorageArrayBuffer<ObjectInfos, 128>;
using ObjectAttributeBuf = StorageArrayBuffer<ObjectAttribute, 128>;
using LayerAttributeBuf = UniformArrayBuffer<LayerAttribute, 512>;
/**
* TODO(@fclem): Remove once we get rid of old EEVEE code-base.
* `DRW_RESOURCE_CHUNK_LEN = 512`.
*/
using ObjectAttributeLegacyBuf = UniformArrayBuffer<float4, 8 * 512>;
public:
struct SubmitDebugOutput {
/** Indexed by resource id. */
Span<uint32_t> visibility;
/** Indexed by drawn instance. */
Span<uint32_t> resource_id;
};
struct DataDebugOutput {
/** Indexed by resource id. */
Span<ObjectMatrices> matrices;
/** Indexed by resource id. */
Span<ObjectBounds> bounds;
/** Indexed by resource id. */
Span<ObjectInfos> infos;
};
/**
* Buffers containing all object data. Referenced by resource index.
* Exposed as public members for shader access after sync.
*/
SwapChain<ObjectMatricesBuf, 2> matrix_buf;
SwapChain<ObjectBoundsBuf, 2> bounds_buf;
SwapChain<ObjectInfosBuf, 2> infos_buf;
/**
* Object Attributes are reference by indirection data inside ObjectInfos.
* This is because attribute list is arbitrary.
*/
ObjectAttributeBuf attributes_buf;
/**
* TODO(@fclem): Remove once we get rid of old EEVEE code-base.
* Only here to satisfy bindings.
*/
ObjectAttributeLegacyBuf attributes_buf_legacy;
/**
* Table of all View Layer attributes required by shaders, used to populate the buffer below.
*/
Map<uint32_t, GPULayerAttr> layer_attributes;
/**
* Buffer of layer attribute values, indexed and sorted by the hash.
*/
LayerAttributeBuf layer_attributes_buf;
/**
* List of textures coming from Image data-blocks.
* They need to be reference-counted in order to avoid being freed in another thread.
*/
Vector<GPUTexture *> acquired_textures;
private:
/** Number of resource handle recorded. */
uint resource_len_ = 0;
/** Number of object attribute recorded. */
uint attribute_len_ = 0;
Object *object_active = nullptr;
public:
Manager(){};
~Manager();
/**
* Create a new resource handle for the given object.
*/
ResourceHandle resource_handle(const ObjectRef ref, float inflate_bounds = 0.0f);
/**
* Create a new resource handle for the given object, but optionally override model matrix and
* bounds.
*/
ResourceHandle resource_handle(const ObjectRef ref,
const float4x4 *model_matrix,
const float3 *bounds_center,
const float3 *bounds_half_extent);
/**
* Get resource id for a loose matrix. The draw-calls for this resource handle won't be culled
* and there won't be any associated object info / bounds. Assumes correct handedness / winding.
*/
ResourceHandle resource_handle(const float4x4 &model_matrix);
/**
* Get resource id for a loose matrix with bounds. The draw-calls for this resource handle will
* be culled but there won't be any associated object info / bounds.
* Assumes correct handedness / winding.
*/
ResourceHandle resource_handle(const float4x4 &model_matrix,
const float3 &bounds_center,
const float3 &bounds_half_extent);
/** Update the bounds of an already created handle. */
void update_handle_bounds(ResourceHandle handle,
const ObjectRef ref,
float inflate_bounds = 0.0f);
/** Update the bounds of an already created handle. */
void update_handle_bounds(ResourceHandle handle,
const float3 &bounds_center,
const float3 &bounds_half_extent);
/**
* Populate additional per resource data on demand.
*/
void extract_object_attributes(ResourceHandle handle,
const ObjectRef &ref,
Span<GPUMaterial *> materials);
/**
* Collect necessary View Layer attributes.
*/
void register_layer_attributes(GPUMaterial *material);
/**
* Submit a pass for drawing. All resource reference will be dereferenced and commands will be
* sent to GPU.
*/
void submit(PassSimple &pass, View &view);
void submit(PassMain &pass, View &view);
void submit(PassSortable &pass, View &view);
/**
* Variant without any view. Must not contain any shader using `draw_view` create info.
*/
void submit(PassSimple &pass);
/**
* Submit a pass for drawing but read back all data buffers for inspection.
*/
SubmitDebugOutput submit_debug(PassSimple &pass, View &view);
SubmitDebugOutput submit_debug(PassMain &pass, View &view);
/**
* Check data buffers of the draw manager. Only to be used after end_sync().
*/
DataDebugOutput data_debug();
/**
* Will acquire the texture using ref counting and release it after drawing. To be used for
* texture coming from blender Image.
*/
void acquire_texture(GPUTexture *texture)
{
GPU_texture_ref(texture);
acquired_textures.append(texture);
}
/**
* Return the number of resource handles allocated.
*/
uint resource_handle_count() const
{
return resource_len_;
}
/** TODO(fclem): The following should become private at some point. */
void begin_sync();
void end_sync();
void debug_bind();
void resource_bind();
private:
void sync_layer_attributes();
};
inline ResourceHandle Manager::resource_handle(const ObjectRef ref, float inflate_bounds)
{
bool is_active_object = (ref.dupli_object ? ref.dupli_parent : ref.object) == object_active;
matrix_buf.current().get_or_resize(resource_len_).sync(*ref.object);
bounds_buf.current().get_or_resize(resource_len_).sync(*ref.object, inflate_bounds);
infos_buf.current().get_or_resize(resource_len_).sync(ref, is_active_object);
return ResourceHandle(resource_len_++, (ref.object->transflag & OB_NEG_SCALE) != 0);
}
inline ResourceHandle Manager::resource_handle(const ObjectRef ref,
const float4x4 *model_matrix,
const float3 *bounds_center,
const float3 *bounds_half_extent)
{
bool is_active_object = (ref.dupli_object ? ref.dupli_parent : ref.object) == object_active;
if (model_matrix) {
matrix_buf.current().get_or_resize(resource_len_).sync(*model_matrix);
}
else {
matrix_buf.current().get_or_resize(resource_len_).sync(*ref.object);
}
if (bounds_center && bounds_half_extent) {
bounds_buf.current().get_or_resize(resource_len_).sync(*bounds_center, *bounds_half_extent);
}
else {
bounds_buf.current().get_or_resize(resource_len_).sync(*ref.object);
}
infos_buf.current().get_or_resize(resource_len_).sync(ref, is_active_object);
return ResourceHandle(resource_len_++, (ref.object->transflag & OB_NEG_SCALE) != 0);
}
inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix)
{
matrix_buf.current().get_or_resize(resource_len_).sync(model_matrix);
bounds_buf.current().get_or_resize(resource_len_).sync();
infos_buf.current().get_or_resize(resource_len_).sync();
return ResourceHandle(resource_len_++, false);
}
inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix,
const float3 &bounds_center,
const float3 &bounds_half_extent)
{
matrix_buf.current().get_or_resize(resource_len_).sync(model_matrix);
bounds_buf.current().get_or_resize(resource_len_).sync(bounds_center, bounds_half_extent);
infos_buf.current().get_or_resize(resource_len_).sync();
return ResourceHandle(resource_len_++, false);
}
inline void Manager::update_handle_bounds(ResourceHandle handle,
const ObjectRef ref,
float inflate_bounds)
{
bounds_buf.current()[handle.resource_index()].sync(*ref.object, inflate_bounds);
}
inline void Manager::update_handle_bounds(ResourceHandle handle,
const float3 &bounds_center,
const float3 &bounds_half_extent)
{
bounds_buf.current()[handle.resource_index()].sync(bounds_center, bounds_half_extent);
}
inline void Manager::extract_object_attributes(ResourceHandle handle,
const ObjectRef &ref,
Span<GPUMaterial *> materials)
{
ObjectInfos &infos = infos_buf.current().get_or_resize(handle.resource_index());
infos.object_attrs_offset = attribute_len_;
/* Simple cache solution to avoid duplicates. */
Vector<uint32_t, 4> hash_cache;
for (const GPUMaterial *mat : materials) {
const GPUUniformAttrList *attr_list = GPU_material_uniform_attributes(mat);
if (attr_list == nullptr) {
continue;
}
LISTBASE_FOREACH (const GPUUniformAttr *, attr, &attr_list->list) {
/** WATCH: Linear Search. Avoid duplicate attributes across materials. */
if ((mat != materials.first()) && (hash_cache.first_index_of_try(attr->hash_code) != -1)) {
/* Attribute has already been added to the attribute buffer by another material. */
continue;
}
hash_cache.append(attr->hash_code);
if (attributes_buf.get_or_resize(attribute_len_).sync(ref, *attr)) {
infos.object_attrs_len++;
attribute_len_++;
}
}
}
}
inline void Manager::register_layer_attributes(GPUMaterial *material)
{
const ListBase *attr_list = GPU_material_layer_attributes(material);
if (attr_list != nullptr) {
LISTBASE_FOREACH (const GPULayerAttr *, attr, attr_list) {
/** Since layer attributes are global to the whole render pass,
* this only collects a table of their names. */
layer_attributes.add(attr->hash_code, *attr);
}
}
}
} // namespace blender::draw
/* TODO(@fclem): This is for testing. The manager should be passed to the engine through the
* callbacks. */
blender::draw::Manager *DRW_manager_get();
blender::draw::ObjectRef DRW_object_ref_get(Object *object);