Caused by 7688677e29, which replaced `DRW_draw_depth_object` with
`DRW_draw_depth_loop`.
`DRW_draw_depth_object` simply rendered the object without actually
using the DRW manager capabilities.
Now, with `DRW_draw_depth_loop`, the depth is rendered based on what
the engine sees with overlays disabled, which doesn't hide the
particles.
The solution to this issue is to skip particle rendering in the overlay
engine in `DRW_draw_depth_loop`.
Co-authored-by: Miguel Pozo <pragma37@gmail.com>
Pull Request: https://projects.blender.org/blender/blender/pulls/141981
2048 lines
63 KiB
C++
2048 lines
63 KiB
C++
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup draw
|
|
*/
|
|
|
|
#include <cstdio>
|
|
|
|
#include "CLG_log.h"
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_math_matrix.h"
|
|
#include "BLI_math_vector.h"
|
|
#include "BLI_rect.h"
|
|
#include "BLI_string.h"
|
|
#include "BLI_task.h"
|
|
#include "BLI_threads.h"
|
|
|
|
#include "BLF_api.hh"
|
|
|
|
#include "BLT_translation.hh"
|
|
|
|
#include "BKE_context.hh"
|
|
#include "BKE_curve.hh"
|
|
#include "BKE_curves.h"
|
|
#include "BKE_duplilist.hh"
|
|
#include "BKE_editmesh.hh"
|
|
#include "BKE_global.hh"
|
|
#include "BKE_grease_pencil.h"
|
|
#include "BKE_lattice.hh"
|
|
#include "BKE_layer.hh"
|
|
#include "BKE_main.hh"
|
|
#include "BKE_mball.hh"
|
|
#include "BKE_mesh.hh"
|
|
#include "BKE_mesh_wrapper.hh"
|
|
#include "BKE_modifier.hh"
|
|
#include "BKE_object.hh"
|
|
#include "BKE_object_types.hh"
|
|
#include "BKE_paint.hh"
|
|
#include "BKE_particle.h"
|
|
#include "BKE_pointcache.h"
|
|
#include "BKE_pointcloud.hh"
|
|
#include "BKE_scene.hh"
|
|
#include "BKE_screen.hh"
|
|
#include "BKE_subdiv_modifier.hh"
|
|
#include "BKE_volume.hh"
|
|
|
|
#include "DNA_camera_types.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_userdef_types.h"
|
|
#include "DNA_view3d_types.h"
|
|
#include "DNA_world_types.h"
|
|
|
|
#include "ED_gpencil_legacy.hh"
|
|
#include "ED_screen.hh"
|
|
#include "ED_space_api.hh"
|
|
#include "ED_view3d.hh"
|
|
|
|
#include "GPU_capabilities.hh"
|
|
#include "GPU_framebuffer.hh"
|
|
#include "GPU_matrix.hh"
|
|
#include "GPU_platform.hh"
|
|
#include "GPU_shader_shared.hh"
|
|
#include "GPU_state.hh"
|
|
#include "GPU_uniform_buffer.hh"
|
|
#include "GPU_viewport.hh"
|
|
|
|
#include "UI_resources.hh"
|
|
#include "UI_view2d.hh"
|
|
|
|
#include "WM_api.hh"
|
|
|
|
#include "draw_cache.hh"
|
|
#include "draw_color_management.hh"
|
|
#include "draw_common_c.hh"
|
|
#include "draw_context_private.hh"
|
|
#include "draw_manager_text.hh"
|
|
#include "draw_shader.hh"
|
|
#include "draw_subdivision.hh"
|
|
#include "draw_view_c.hh"
|
|
#include "draw_view_data.hh"
|
|
|
|
/* only for callbacks */
|
|
#include "draw_cache_impl.hh"
|
|
|
|
#include "engines/compositor/compositor_engine.h"
|
|
#include "engines/eevee/eevee_engine.h"
|
|
#include "engines/external/external_engine.h"
|
|
#include "engines/gpencil/gpencil_engine.hh"
|
|
#include "engines/image/image_engine.h"
|
|
#include "engines/overlay/overlay_engine.h"
|
|
#include "engines/select/select_engine.hh"
|
|
#include "engines/workbench/workbench_engine.h"
|
|
|
|
#include "GPU_context.hh"
|
|
|
|
#include "DEG_depsgraph.hh"
|
|
#include "DEG_depsgraph_query.hh"
|
|
|
|
#include "BLI_time.h"
|
|
|
|
#include "DRW_select_buffer.hh"
|
|
|
|
thread_local DRWContext *DRWContext::g_context = nullptr;
|
|
|
|
DRWContext::DRWContext(Mode mode_,
|
|
Depsgraph *depsgraph,
|
|
const int2 size,
|
|
const bContext *C,
|
|
ARegion *region,
|
|
View3D *v3d)
|
|
: mode(mode_)
|
|
{
|
|
BLI_assert(size.x > 0 && size.y > 0);
|
|
|
|
this->size = float2(size);
|
|
this->inv_size = 1.0f / this->size;
|
|
|
|
this->depsgraph = depsgraph;
|
|
this->scene = DEG_get_evaluated_scene(depsgraph);
|
|
this->view_layer = DEG_get_evaluated_view_layer(depsgraph);
|
|
|
|
this->evil_C = C;
|
|
|
|
this->region = (region) ? region : ((C) ? CTX_wm_region(C) : nullptr);
|
|
this->space_data = (C) ? CTX_wm_space_data(C) : nullptr;
|
|
this->v3d = (v3d) ? v3d : ((C) ? CTX_wm_view3d(C) : nullptr);
|
|
if (this->v3d != nullptr && this->region != nullptr) {
|
|
this->rv3d = static_cast<RegionView3D *>(this->region->regiondata);
|
|
}
|
|
/* Active object. Set to nullptr for render (when region is nullptr). */
|
|
this->obact = (this->region) ? BKE_view_layer_active_object_get(this->view_layer) : nullptr;
|
|
/* Object mode. */
|
|
this->object_mode = (this->obact) ? eObjectMode(this->obact->mode) : OB_MODE_OBJECT;
|
|
/* Edit object. */
|
|
this->object_edit = (this->object_mode & OB_MODE_EDIT) ? this->obact : nullptr;
|
|
/* Pose object. */
|
|
if (this->object_mode & OB_MODE_POSE) {
|
|
this->object_pose = this->obact;
|
|
}
|
|
else if (this->object_mode & OB_MODE_ALL_WEIGHT_PAINT) {
|
|
this->object_pose = BKE_object_pose_armature_get(this->obact);
|
|
}
|
|
else {
|
|
this->object_pose = nullptr;
|
|
}
|
|
|
|
/* View layer can be lazily synced. */
|
|
BKE_view_layer_synced_ensure(this->scene, this->view_layer);
|
|
|
|
/* fclem: Is this still needed ? */
|
|
if (this->object_edit && rv3d) {
|
|
ED_view3d_init_mats_rv3d(this->object_edit, rv3d);
|
|
}
|
|
|
|
BLI_assert(g_context == nullptr);
|
|
g_context = this;
|
|
}
|
|
|
|
DRWContext::DRWContext(Mode mode_,
|
|
Depsgraph *depsgraph,
|
|
GPUViewport *viewport,
|
|
const bContext *C,
|
|
ARegion *region,
|
|
View3D *v3d)
|
|
: DRWContext(mode_,
|
|
depsgraph,
|
|
int2(GPU_texture_width(GPU_viewport_color_texture(viewport, 0)),
|
|
GPU_texture_height(GPU_viewport_color_texture(viewport, 0))),
|
|
C,
|
|
region,
|
|
v3d)
|
|
{
|
|
this->viewport = viewport;
|
|
|
|
blender::draw::color_management::viewport_color_management_set(*viewport, *this);
|
|
}
|
|
|
|
DRWContext::~DRWContext()
|
|
{
|
|
BLI_assert(g_context == this);
|
|
g_context = nullptr;
|
|
}
|
|
|
|
GPUFrameBuffer *DRWContext::default_framebuffer()
|
|
{
|
|
return view_data_active->dfbl.default_fb;
|
|
}
|
|
|
|
DefaultFramebufferList *DRWContext::viewport_framebuffer_list_get() const
|
|
{
|
|
return const_cast<DefaultFramebufferList *>(&view_data_active->dfbl);
|
|
}
|
|
|
|
DefaultTextureList *DRWContext::viewport_texture_list_get() const
|
|
{
|
|
return const_cast<DefaultTextureList *>(&view_data_active->dtxl);
|
|
}
|
|
|
|
static bool draw_show_annotation()
|
|
{
|
|
DRWContext &draw_ctx = drw_get();
|
|
SpaceLink *space_data = draw_ctx.space_data;
|
|
View3D *v3d = draw_ctx.v3d;
|
|
|
|
if (space_data != nullptr) {
|
|
switch (space_data->spacetype) {
|
|
case SPACE_IMAGE: {
|
|
SpaceImage *sima = (SpaceImage *)space_data;
|
|
return (sima->flag & SI_SHOW_GPENCIL) != 0;
|
|
}
|
|
case SPACE_NODE:
|
|
/* Don't draw the annotation for the node editor. Annotations are handled by space_image as
|
|
* the draw manager is only used to draw the background. */
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return (v3d && ((v3d->flag2 & V3D_SHOW_ANNOTATION) != 0) &&
|
|
((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0));
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Threaded Extraction
|
|
* \{ */
|
|
|
|
struct ExtractionGraph {
|
|
public:
|
|
TaskGraph *graph = BLI_task_graph_create();
|
|
|
|
private:
|
|
/* WORKAROUND: BLI_gset_free is not allowing to pass a data pointer to the free function. */
|
|
static thread_local TaskGraph *task_graph_ptr_;
|
|
|
|
public:
|
|
~ExtractionGraph()
|
|
{
|
|
BLI_assert_msg(graph == nullptr, "Missing call to work_and_wait");
|
|
}
|
|
|
|
/* `delayed_extraction` is a set of object to add to the graph before running.
|
|
* The non-null, the set is consumed and freed after use. */
|
|
void work_and_wait(GSet *&delayed_extraction)
|
|
{
|
|
BLI_assert_msg(graph, "Trying to submit more than once");
|
|
|
|
if (delayed_extraction) {
|
|
task_graph_ptr_ = graph;
|
|
BLI_gset_free(delayed_extraction, delayed_extraction_free_callback);
|
|
task_graph_ptr_ = nullptr;
|
|
delayed_extraction = nullptr;
|
|
}
|
|
|
|
BLI_task_graph_work_and_wait(graph);
|
|
BLI_task_graph_free(graph);
|
|
graph = nullptr;
|
|
}
|
|
|
|
private:
|
|
static void delayed_extraction_free_callback(void *object)
|
|
{
|
|
blender::draw::drw_batch_cache_generate_requested_evaluated_mesh_or_curve(
|
|
reinterpret_cast<Object *>(object), *task_graph_ptr_);
|
|
}
|
|
};
|
|
|
|
thread_local TaskGraph *ExtractionGraph::task_graph_ptr_ = nullptr;
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Settings
|
|
* \{ */
|
|
|
|
bool DRW_object_is_renderable(const Object *ob)
|
|
{
|
|
BLI_assert((ob->base_flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) != 0);
|
|
|
|
if (ob->type == OB_MESH) {
|
|
DRWContext &draw_ctx = drw_get();
|
|
/* The evaluated object might be a mesh even though the original object has a different type.
|
|
* Also make sure the original object is a mesh (see #140762). */
|
|
if (draw_ctx.object_edit && draw_ctx.object_edit->type != OB_MESH) {
|
|
/* Noop. */
|
|
}
|
|
else if ((ob == draw_ctx.object_edit) || ob->mode == OB_MODE_EDIT) {
|
|
View3D *v3d = draw_ctx.v3d;
|
|
if (v3d && ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && RETOPOLOGY_ENABLED(v3d)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DRW_object_is_in_edit_mode(const Object *ob)
|
|
{
|
|
if (BKE_object_is_in_editmode(ob)) {
|
|
if (ELEM(ob->type, OB_MESH, OB_CURVES)) {
|
|
if ((ob->mode & OB_MODE_EDIT) == 0) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int DRW_object_visibility_in_active_context(const Object *ob)
|
|
{
|
|
const eEvaluationMode mode = DRW_context_get()->is_scene_render() ? DAG_EVAL_RENDER :
|
|
DAG_EVAL_VIEWPORT;
|
|
return BKE_object_visibility(ob, mode);
|
|
}
|
|
|
|
bool DRW_object_use_hide_faces(const Object *ob)
|
|
{
|
|
if (ob->type == OB_MESH) {
|
|
switch (ob->mode) {
|
|
case OB_MODE_SCULPT:
|
|
case OB_MODE_TEXTURE_PAINT:
|
|
case OB_MODE_VERTEX_PAINT:
|
|
case OB_MODE_WEIGHT_PAINT:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DRW_object_is_visible_psys_in_active_context(const Object *object, const ParticleSystem *psys)
|
|
{
|
|
const bool for_render = DRW_context_get()->is_image_render();
|
|
/* NOTE: psys_check_enabled is using object and particle system for only
|
|
* reading, but is using some other functions which are more generic and
|
|
* which are hard to make const-pointer. */
|
|
if (!psys_check_enabled((Object *)object, (ParticleSystem *)psys, for_render)) {
|
|
return false;
|
|
}
|
|
const DRWContext *draw_ctx = DRW_context_get();
|
|
const Scene *scene = draw_ctx->scene;
|
|
if (object == draw_ctx->object_edit) {
|
|
return false;
|
|
}
|
|
const ParticleSettings *part = psys->part;
|
|
const ParticleEditSettings *pset = &scene->toolsettings->particle;
|
|
if (object->mode == OB_MODE_PARTICLE_EDIT) {
|
|
if (psys_in_edit_mode(draw_ctx->depsgraph, psys)) {
|
|
if ((pset->flag & PE_DRAW_PART) == 0) {
|
|
return false;
|
|
}
|
|
if ((part->childtype == 0) &&
|
|
(psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED) == 0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const Mesh *DRW_object_get_editmesh_cage_for_drawing(const Object &object)
|
|
{
|
|
/* Same as DRW_object_get_data_for_drawing, but for the cage mesh. */
|
|
BLI_assert(object.type == OB_MESH);
|
|
const Mesh *cage_mesh = BKE_object_get_editmesh_eval_cage(&object);
|
|
if (cage_mesh == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (BKE_subsurf_modifier_has_gpu_subdiv(cage_mesh)) {
|
|
return cage_mesh;
|
|
}
|
|
return BKE_mesh_wrapper_ensure_subdivision(cage_mesh);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Viewport (DRW_viewport)
|
|
* \{ */
|
|
|
|
DRWData *DRW_viewport_data_create()
|
|
{
|
|
DRWData *drw_data = MEM_callocN<DRWData>("DRWData");
|
|
|
|
drw_data->default_view = new blender::draw::View("DrawDefaultView");
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
drw_data->view_data[i] = new DRWViewData();
|
|
}
|
|
return drw_data;
|
|
}
|
|
|
|
void DRWData::modules_init()
|
|
{
|
|
using namespace blender::draw;
|
|
DRW_pointcloud_init(this);
|
|
DRW_curves_init(this);
|
|
DRW_volume_init(this);
|
|
}
|
|
|
|
void DRWData::modules_begin_sync()
|
|
{
|
|
using namespace blender::draw;
|
|
DRW_curves_begin_sync(this);
|
|
DRW_smoke_begin_sync(this);
|
|
}
|
|
|
|
void DRWData::modules_exit()
|
|
{
|
|
DRW_smoke_exit(this);
|
|
}
|
|
|
|
void DRW_viewport_data_free(DRWData *drw_data)
|
|
{
|
|
for (int i = 0; i < 2; i++) {
|
|
delete drw_data->view_data[i];
|
|
}
|
|
DRW_volume_module_free(drw_data->volume_module);
|
|
DRW_pointcloud_module_free(drw_data->pointcloud_module);
|
|
DRW_curves_module_free(drw_data->curves_module);
|
|
delete drw_data->default_view;
|
|
MEM_freeN(drw_data);
|
|
}
|
|
|
|
static DRWData *drw_viewport_data_ensure(GPUViewport *viewport)
|
|
{
|
|
DRWData **data_p = GPU_viewport_data_get(viewport);
|
|
DRWData *data = *data_p;
|
|
|
|
if (data == nullptr) {
|
|
*data_p = data = DRW_viewport_data_create();
|
|
}
|
|
return data;
|
|
}
|
|
|
|
void DRWContext::acquire_data()
|
|
{
|
|
BLI_assert(GPU_context_active_get() != nullptr);
|
|
|
|
blender::gpu::TexturePool::get().reset();
|
|
|
|
{
|
|
/* Acquire DRWData. */
|
|
if (!this->viewport && this->data) {
|
|
/* Manager was init first without a viewport, created DRWData, but is being re-init.
|
|
* In this case, keep the old data. */
|
|
}
|
|
else if (this->viewport) {
|
|
/* Use viewport's persistent DRWData. */
|
|
this->data = drw_viewport_data_ensure(this->viewport);
|
|
}
|
|
else {
|
|
/* Create temporary DRWData. Freed in drw_manager_exit(). */
|
|
this->data = DRW_viewport_data_create();
|
|
}
|
|
int view = (this->viewport) ? GPU_viewport_active_view_get(this->viewport) : 0;
|
|
this->view_data_active = this->data->view_data[view];
|
|
|
|
this->view_data_active->texture_list_size_validate(int2(this->size));
|
|
|
|
if (this->viewport) {
|
|
DRW_view_data_default_lists_from_viewport(this->view_data_active, this->viewport);
|
|
}
|
|
}
|
|
{
|
|
/* Create the default view. */
|
|
if (this->rv3d != nullptr) {
|
|
blender::draw::View::default_set(float4x4(this->rv3d->viewmat),
|
|
float4x4(this->rv3d->winmat));
|
|
}
|
|
else if (this->region) {
|
|
/* Assume that if rv3d is nullptr, we are drawing for a 2D area. */
|
|
View2D *v2d = &this->region->v2d;
|
|
rctf region_space = {0.0f, 1.0f, 0.0f, 1.0f};
|
|
|
|
float4x4 viewmat;
|
|
BLI_rctf_transform_calc_m4_pivot_min(&v2d->cur, ®ion_space, viewmat.ptr());
|
|
|
|
float4x4 winmat = float4x4::identity();
|
|
winmat[0][0] = winmat[1][1] = 2.0f;
|
|
winmat[3][0] = winmat[3][1] = -1.0f;
|
|
|
|
blender::draw::View::default_set(viewmat, winmat);
|
|
}
|
|
else {
|
|
/* Assume that this is the render mode or custom mode and
|
|
* that the default view will be set appropriately or not used. */
|
|
BLI_assert(this->is_image_render() || this->mode == DRWContext::CUSTOM);
|
|
}
|
|
}
|
|
|
|
/* Init modules ahead of time because the begin_sync happens before DRW_render_object_iter. */
|
|
this->data->modules_init();
|
|
}
|
|
|
|
void DRWContext::release_data()
|
|
{
|
|
BLI_assert(GPU_context_active_get() != nullptr);
|
|
|
|
this->data->modules_exit();
|
|
|
|
/* Reset drawing state to avoid to side-effects. */
|
|
blender::draw::command::StateSet::set();
|
|
|
|
DRW_view_data_reset(this->view_data_active);
|
|
|
|
if (this->data != nullptr && this->viewport == nullptr) {
|
|
DRW_viewport_data_free(this->data);
|
|
}
|
|
this->data = nullptr;
|
|
this->viewport = nullptr;
|
|
}
|
|
|
|
blender::draw::TextureFromPool &DRW_viewport_pass_texture_get(const char *pass_name)
|
|
{
|
|
return *drw_get().view_data_active->viewport_compositor_passes.lookup_or_add_cb(
|
|
pass_name, [&]() { return std::make_unique<blender::draw::TextureFromPool>(pass_name); });
|
|
}
|
|
|
|
void DRW_viewport_request_redraw()
|
|
{
|
|
if (drw_get().viewport) {
|
|
GPU_viewport_tag_update(drw_get().viewport);
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Duplis
|
|
* \{ */
|
|
|
|
/* The Dupli systems generate a lot of transient objects that share the batch caches.
|
|
* So we ensure to only clear and generate the cache once per source instance type using this
|
|
* set. */
|
|
/* TODO(fclem): This should be reconsidered as this has some unneeded overhead and complexity.
|
|
* Maybe it isn't needed at all. */
|
|
struct DupliCacheManager {
|
|
private:
|
|
/* Key identifying a single instance source. */
|
|
struct DupliKey {
|
|
Object *ob = nullptr;
|
|
ID *ob_data = nullptr;
|
|
|
|
bool operator==(const DupliObject *ob_dupli)
|
|
{
|
|
return this->ob == ob_dupli->ob && this->ob_data == ob_dupli->ob_data;
|
|
}
|
|
|
|
uint64_t hash() const
|
|
{
|
|
return blender::get_default_hash(this->ob, this->ob_data);
|
|
}
|
|
|
|
friend bool operator==(const DupliKey &a, const DupliKey &b)
|
|
{
|
|
return a.ob == b.ob && a.ob_data == b.ob_data;
|
|
}
|
|
};
|
|
|
|
/* Last key used. Allows to avoid the overhead of polling the `dupli_set` for each instance.
|
|
* This helps when a Dupli system generates a lot of similar geometry consecutively. */
|
|
DupliKey last_key_ = {};
|
|
|
|
/* Set containing all visited Dupli source object. */
|
|
blender::Set<DupliKey> *dupli_set_ = nullptr;
|
|
|
|
public:
|
|
void try_add(blender::draw::ObjectRef &ob_ref);
|
|
void extract_all(ExtractionGraph &extraction);
|
|
};
|
|
|
|
void DupliCacheManager::try_add(blender::draw::ObjectRef &ob_ref)
|
|
{
|
|
if (ob_ref.is_dupli() == false) {
|
|
return;
|
|
}
|
|
if (last_key_ == ob_ref.dupli_object_) {
|
|
/* Same data as previous iteration. No need to perform the check again. */
|
|
return;
|
|
}
|
|
|
|
last_key_.ob = ob_ref.dupli_object_->ob;
|
|
last_key_.ob_data = ob_ref.dupli_object_->ob_data;
|
|
|
|
if (dupli_set_ == nullptr) {
|
|
dupli_set_ = MEM_new<blender::Set<DupliKey>>("DupliCacheManager::dupli_set_");
|
|
}
|
|
|
|
if (dupli_set_->add(last_key_)) {
|
|
/* Key is newly added. It is the first time we sync this object. */
|
|
/* TODO: Meh a bit out of place but this is nice as it is
|
|
* only done once per instance type. */
|
|
/* Note that this can happen for geometry data whose type is different from the original
|
|
* object (e.g. Text evaluated as Mesh, Geometry node instance etc...).
|
|
* In this case, key.ob is not going to have the same data type as ob_ref.object nor the same
|
|
* data at all. */
|
|
blender::draw::drw_batch_cache_validate(ob_ref.object);
|
|
}
|
|
}
|
|
|
|
void DupliCacheManager::extract_all(ExtractionGraph &extraction)
|
|
{
|
|
/* Reset for next iter. */
|
|
last_key_ = {};
|
|
|
|
if (dupli_set_ == nullptr) {
|
|
return;
|
|
}
|
|
|
|
/* Note these can referenced by the temporary object pointer `Object *ob` and needs to have at
|
|
* least the same lifetime. */
|
|
blender::bke::ObjectRuntime tmp_runtime;
|
|
Object tmp_object;
|
|
|
|
using Iter = blender::Set<DupliKey>::Iterator;
|
|
Iter begin = dupli_set_->begin();
|
|
Iter end = dupli_set_->end();
|
|
for (Iter iter = begin; iter != end; ++iter) {
|
|
const DupliKey &key = *iter;
|
|
Object *ob = iter->ob;
|
|
|
|
if (key.ob_data != ob->data) {
|
|
/* Copy both object data and runtime. */
|
|
tmp_runtime = *ob->runtime;
|
|
tmp_object = blender::dna::shallow_copy(*ob);
|
|
tmp_object.runtime = &tmp_runtime;
|
|
/* Geometry instances shouldn't be rendered with edit mode overlays. */
|
|
tmp_object.mode = OB_MODE_OBJECT;
|
|
/* Do not modify the original bound-box. */
|
|
BKE_object_replace_data_on_shallow_copy(&tmp_object, key.ob_data);
|
|
|
|
ob = &tmp_object;
|
|
}
|
|
|
|
blender::draw::drw_batch_cache_generate_requested(ob, *extraction.graph);
|
|
}
|
|
|
|
/* TODO(fclem): Could eventually keep the set allocated. */
|
|
MEM_SAFE_DELETE(dupli_set_);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name ObjectRef
|
|
* \{ */
|
|
|
|
namespace blender::draw {
|
|
|
|
ObjectRef::ObjectRef(DEGObjectIterData &iter_data, Object *ob)
|
|
: dupli_object_(iter_data.dupli_object_current),
|
|
dupli_parent_(iter_data.dupli_parent),
|
|
object(ob)
|
|
{
|
|
}
|
|
|
|
ObjectRef::ObjectRef(Object *ob) : object(ob) {}
|
|
|
|
} // namespace blender::draw
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Garbage Collection
|
|
* \{ */
|
|
|
|
void DRW_cache_free_old_batches(Main *bmain)
|
|
{
|
|
using namespace blender::draw;
|
|
Scene *scene;
|
|
static int lasttime = 0;
|
|
int ctime = int(BLI_time_now_seconds());
|
|
|
|
if (U.vbotimeout == 0 || (ctime - lasttime) < U.vbocollectrate || ctime == lasttime) {
|
|
return;
|
|
}
|
|
|
|
lasttime = ctime;
|
|
|
|
for (scene = static_cast<Scene *>(bmain->scenes.first); scene;
|
|
scene = static_cast<Scene *>(scene->id.next))
|
|
{
|
|
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
|
|
Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer);
|
|
if (depsgraph == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
/* TODO(fclem): This is not optimal since it iter over all dupli instances.
|
|
* In this case only the source object should be tagged. */
|
|
DEGObjectIterSettings deg_iter_settings = {nullptr};
|
|
deg_iter_settings.depsgraph = depsgraph;
|
|
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
|
|
DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) {
|
|
DRW_batch_cache_free_old(ob, ctime);
|
|
}
|
|
DEG_OBJECT_ITER_END;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Rendering (DRW_engines)
|
|
* \{ */
|
|
|
|
static void drw_engines_cache_populate(blender::draw::ObjectRef &ref,
|
|
DupliCacheManager &dupli_cache,
|
|
ExtractionGraph &extraction)
|
|
{
|
|
if (ref.is_dupli() == false) {
|
|
blender::draw::drw_batch_cache_validate(ref.object);
|
|
}
|
|
else {
|
|
dupli_cache.try_add(ref);
|
|
}
|
|
|
|
DRWContext &ctx = drw_get();
|
|
ctx.view_data_active->foreach_enabled_engine(
|
|
[&](DrawEngine &instance) { instance.object_sync(ref, *DRW_manager_get()); });
|
|
|
|
/* TODO: in the future it would be nice to generate once for all viewports.
|
|
* But we need threaded DRW manager first. */
|
|
if (ref.is_dupli() == false) {
|
|
blender::draw::drw_batch_cache_generate_requested(ref.object, *extraction.graph);
|
|
}
|
|
/* Batch generation for duplis happens after iter_callback. */
|
|
}
|
|
|
|
void DRWContext::sync(iter_callback_t iter_callback)
|
|
{
|
|
/* Enable modules and init for next sync. */
|
|
data->modules_begin_sync();
|
|
|
|
DupliCacheManager dupli_handler;
|
|
ExtractionGraph extraction;
|
|
|
|
/* Custom callback defines the set of object to sync. */
|
|
iter_callback(dupli_handler, extraction);
|
|
|
|
dupli_handler.extract_all(extraction);
|
|
extraction.work_and_wait(this->delayed_extraction);
|
|
|
|
DRW_curves_update(*view_data_active->manager);
|
|
}
|
|
|
|
void DRWContext::engines_init_and_sync(iter_callback_t iter_callback)
|
|
{
|
|
view_data_active->foreach_enabled_engine([&](DrawEngine &instance) { instance.init(); });
|
|
|
|
view_data_active->manager->begin_sync(this->obact);
|
|
|
|
view_data_active->foreach_enabled_engine([&](DrawEngine &instance) { instance.begin_sync(); });
|
|
|
|
sync(iter_callback);
|
|
|
|
view_data_active->foreach_enabled_engine([&](DrawEngine &instance) { instance.end_sync(); });
|
|
|
|
view_data_active->manager->end_sync();
|
|
}
|
|
|
|
void DRWContext::engines_draw_scene()
|
|
{
|
|
/* Start Drawing */
|
|
blender::draw::command::StateSet::set();
|
|
|
|
view_data_active->foreach_enabled_engine([&](DrawEngine &instance) {
|
|
GPU_debug_group_begin(instance.name_get().c_str());
|
|
instance.draw(*DRW_manager_get());
|
|
GPU_debug_group_end();
|
|
});
|
|
|
|
/* Reset state after drawing */
|
|
blender::draw::command::StateSet::set();
|
|
|
|
/* Fix 3D view "lagging" on APPLE and WIN32+NVIDIA. (See #56996, #61474) */
|
|
if (GPU_type_matches_ex(GPU_DEVICE_ANY, GPU_OS_ANY, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL)) {
|
|
GPU_flush();
|
|
}
|
|
}
|
|
|
|
void DRW_draw_region_engine_info(int xoffset, int *yoffset, int line_height)
|
|
{
|
|
DRWContext &ctx = drw_get();
|
|
ctx.view_data_active->foreach_enabled_engine([&](DrawEngine &instance) {
|
|
if (instance.info[0] != '\0') {
|
|
const char *buf_step = IFACE_(instance.info);
|
|
do {
|
|
const char *buf = buf_step;
|
|
buf_step = BLI_strchr_or_end(buf, '\n');
|
|
const int buf_len = buf_step - buf;
|
|
*yoffset -= line_height;
|
|
BLF_draw_default(xoffset, *yoffset, 0.0f, buf, buf_len);
|
|
} while (*buf_step ? ((void)buf_step++, true) : false);
|
|
}
|
|
});
|
|
}
|
|
|
|
void DRWContext::enable_engines(bool gpencil_engine_needed, RenderEngineType *render_engine_type)
|
|
{
|
|
DRWViewData &view_data = *this->view_data_active;
|
|
|
|
SpaceLink *space_data = this->space_data;
|
|
if (space_data && space_data->spacetype == SPACE_IMAGE) {
|
|
if (DRW_engine_external_acquire_for_image_editor(this)) {
|
|
view_data.external.set_used(true);
|
|
}
|
|
else {
|
|
view_data.image.set_used(true);
|
|
}
|
|
view_data.overlay.set_used(true);
|
|
return;
|
|
}
|
|
|
|
if (space_data && space_data->spacetype == SPACE_NODE) {
|
|
/* Only enable when drawing the space image backdrop. */
|
|
SpaceNode *snode = (SpaceNode *)space_data;
|
|
if ((snode->flag & SNODE_BACKDRAW) != 0) {
|
|
view_data.image.set_used(true);
|
|
view_data.overlay.set_used(true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (ELEM(this->mode, DRWContext::SELECT_OBJECT, DRWContext::SELECT_OBJECT_MATERIAL)) {
|
|
view_data.grease_pencil.set_used(gpencil_engine_needed);
|
|
view_data.object_select.set_used(true);
|
|
return;
|
|
}
|
|
|
|
if (ELEM(this->mode, DRWContext::SELECT_EDIT_MESH)) {
|
|
view_data.edit_select.set_used(true);
|
|
return;
|
|
}
|
|
|
|
if (ELEM(this->mode, DRWContext::DEPTH, DRWContext::DEPTH_ACTIVE_OBJECT)) {
|
|
view_data.grease_pencil.set_used(gpencil_engine_needed);
|
|
view_data.overlay.set_used(true);
|
|
return;
|
|
}
|
|
|
|
/* Regular V3D drawing. */
|
|
{
|
|
const eDrawType drawtype = eDrawType(this->v3d->shading.type);
|
|
const bool use_xray = XRAY_ENABLED(this->v3d);
|
|
|
|
/* Base engine. */
|
|
switch (drawtype) {
|
|
case OB_WIRE:
|
|
case OB_SOLID:
|
|
view_data.workbench.set_used(true);
|
|
break;
|
|
case OB_MATERIAL:
|
|
case OB_RENDER:
|
|
default:
|
|
if (render_engine_type == &DRW_engine_viewport_eevee_type) {
|
|
view_data.eevee.set_used(true);
|
|
}
|
|
else if (render_engine_type == &DRW_engine_viewport_workbench_type) {
|
|
view_data.workbench.set_used(true);
|
|
}
|
|
else if ((render_engine_type->flag & RE_INTERNAL) == 0) {
|
|
view_data.external.set_used(true);
|
|
}
|
|
else {
|
|
BLI_assert_unreachable();
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ((drawtype >= OB_SOLID) || !use_xray) {
|
|
view_data.grease_pencil.set_used(gpencil_engine_needed);
|
|
}
|
|
|
|
view_data.compositor.set_used(is_viewport_compositor_enabled());
|
|
|
|
view_data.overlay.set_used(true);
|
|
|
|
#ifdef WITH_DRAW_DEBUG
|
|
if (G.debug_value == 31) {
|
|
view_data.edit_select_debug.set_used(true);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void DRWContext::engines_data_validate()
|
|
{
|
|
DRW_view_data_free_unused(this->view_data_active);
|
|
}
|
|
|
|
static bool gpencil_object_is_excluded(View3D *v3d)
|
|
{
|
|
if (v3d) {
|
|
return ((v3d->object_type_exclude_viewport & (1 << OB_GREASE_PENCIL)) != 0);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool gpencil_any_exists(Depsgraph *depsgraph)
|
|
{
|
|
return (DEG_id_type_any_exists(depsgraph, ID_GD_LEGACY) ||
|
|
DEG_id_type_any_exists(depsgraph, ID_GP));
|
|
}
|
|
|
|
bool DRW_gpencil_engine_needed_viewport(Depsgraph *depsgraph, View3D *v3d)
|
|
{
|
|
if (gpencil_object_is_excluded(v3d)) {
|
|
return false;
|
|
}
|
|
return gpencil_any_exists(depsgraph);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Callbacks
|
|
* \{ */
|
|
|
|
static void drw_callbacks_pre_scene(DRWContext &draw_ctx)
|
|
{
|
|
RegionView3D *rv3d = draw_ctx.rv3d;
|
|
|
|
GPU_matrix_projection_set(rv3d->winmat);
|
|
GPU_matrix_set(rv3d->viewmat);
|
|
|
|
if (draw_ctx.evil_C) {
|
|
blender::draw::command::StateSet::set();
|
|
DRW_submission_start();
|
|
ED_region_draw_cb_draw(draw_ctx.evil_C, draw_ctx.region, REGION_DRAW_PRE_VIEW);
|
|
DRW_submission_end();
|
|
}
|
|
|
|
/* State is reset later at the beginning of `draw_ctx.engines_draw_scene()`. */
|
|
}
|
|
|
|
static void drw_callbacks_post_scene(DRWContext &draw_ctx)
|
|
{
|
|
RegionView3D *rv3d = draw_ctx.rv3d;
|
|
ARegion *region = draw_ctx.region;
|
|
View3D *v3d = draw_ctx.v3d;
|
|
Depsgraph *depsgraph = draw_ctx.depsgraph;
|
|
|
|
const bool do_annotations = draw_show_annotation();
|
|
|
|
/* State has been reset at the end `draw_ctx.engines_draw_scene()`. */
|
|
|
|
DRW_submission_start();
|
|
if (draw_ctx.evil_C) {
|
|
DefaultFramebufferList *dfbl = DRW_context_get()->viewport_framebuffer_list_get();
|
|
|
|
GPU_framebuffer_bind(dfbl->overlay_fb);
|
|
|
|
GPU_matrix_projection_set(rv3d->winmat);
|
|
GPU_matrix_set(rv3d->viewmat);
|
|
|
|
/* annotations - temporary drawing buffer (3d space) */
|
|
/* XXX: Or should we use a proper draw/overlay engine for this case? */
|
|
if (do_annotations) {
|
|
GPU_depth_test(GPU_DEPTH_NONE);
|
|
/* XXX: as `scene->gpd` is not copied for copy-on-eval yet. */
|
|
ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, true);
|
|
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
|
|
}
|
|
|
|
GPU_depth_test(GPU_DEPTH_NONE);
|
|
/* Apply state for callbacks. */
|
|
GPU_apply_state();
|
|
|
|
ED_region_draw_cb_draw(draw_ctx.evil_C, draw_ctx.region, REGION_DRAW_POST_VIEW);
|
|
|
|
#ifdef WITH_XR_OPENXR
|
|
/* XR callbacks (controllers, custom draw functions) for session mirror. */
|
|
if ((v3d->flag & V3D_XR_SESSION_MIRROR) != 0) {
|
|
if ((v3d->flag2 & V3D_XR_SHOW_CONTROLLERS) != 0) {
|
|
ARegionType *art = WM_xr_surface_controller_region_type_get();
|
|
if (art) {
|
|
ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW);
|
|
}
|
|
}
|
|
if ((v3d->flag2 & V3D_XR_SHOW_CUSTOM_OVERLAYS) != 0) {
|
|
SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D);
|
|
if (st) {
|
|
ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR);
|
|
if (art) {
|
|
ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Callback can be nasty and do whatever they want with the state.
|
|
* Don't trust them! */
|
|
blender::draw::command::StateSet::set();
|
|
|
|
/* Needed so gizmo isn't occluded. */
|
|
if ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) {
|
|
GPU_depth_test(GPU_DEPTH_NONE);
|
|
DRW_draw_gizmo_3d(draw_ctx.evil_C, region);
|
|
}
|
|
|
|
GPU_depth_test(GPU_DEPTH_NONE);
|
|
DRW_draw_region_info(draw_ctx.evil_C, region);
|
|
|
|
/* Annotations - temporary drawing buffer (screen-space). */
|
|
/* XXX: Or should we use a proper draw/overlay engine for this case? */
|
|
if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (do_annotations)) {
|
|
GPU_depth_test(GPU_DEPTH_NONE);
|
|
/* XXX: as `scene->gpd` is not copied for copy-on-eval yet */
|
|
ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, false);
|
|
}
|
|
|
|
if ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) {
|
|
/* Draw 2D after region info so we can draw on top of the camera passepartout overlay.
|
|
* 'DRW_draw_region_info' sets the projection in pixel-space. */
|
|
GPU_depth_test(GPU_DEPTH_NONE);
|
|
DRW_draw_gizmo_2d(draw_ctx.evil_C, region);
|
|
}
|
|
|
|
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
|
|
}
|
|
else {
|
|
if (v3d && ((v3d->flag2 & V3D_SHOW_ANNOTATION) != 0)) {
|
|
GPU_depth_test(GPU_DEPTH_NONE);
|
|
/* XXX: as `scene->gpd` is not copied for copy-on-eval yet */
|
|
ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, true);
|
|
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
|
|
}
|
|
|
|
#ifdef WITH_XR_OPENXR
|
|
if ((v3d->flag & V3D_XR_SESSION_SURFACE) != 0) {
|
|
DefaultFramebufferList *dfbl = DRW_context_get()->viewport_framebuffer_list_get();
|
|
|
|
blender::draw::command::StateSet::set();
|
|
|
|
GPU_framebuffer_bind(dfbl->overlay_fb);
|
|
|
|
GPU_matrix_projection_set(rv3d->winmat);
|
|
GPU_matrix_set(rv3d->viewmat);
|
|
|
|
/* XR callbacks (controllers, custom draw functions) for session surface. */
|
|
if (((v3d->flag2 & V3D_XR_SHOW_CONTROLLERS) != 0) ||
|
|
((v3d->flag2 & V3D_XR_SHOW_CUSTOM_OVERLAYS) != 0))
|
|
{
|
|
GPU_depth_test(GPU_DEPTH_NONE);
|
|
GPU_apply_state();
|
|
|
|
if ((v3d->flag2 & V3D_XR_SHOW_CONTROLLERS) != 0) {
|
|
ARegionType *art = WM_xr_surface_controller_region_type_get();
|
|
if (art) {
|
|
ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW);
|
|
}
|
|
}
|
|
if ((v3d->flag2 & V3D_XR_SHOW_CUSTOM_OVERLAYS) != 0) {
|
|
SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D);
|
|
if (st) {
|
|
ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR);
|
|
if (art) {
|
|
ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW);
|
|
}
|
|
}
|
|
}
|
|
|
|
blender::draw::command::StateSet::set();
|
|
}
|
|
|
|
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
|
|
}
|
|
#endif
|
|
}
|
|
DRW_submission_end();
|
|
|
|
blender::draw::command::StateSet::set();
|
|
}
|
|
|
|
static void drw_callbacks_pre_scene_2D(DRWContext &draw_ctx)
|
|
{
|
|
if (draw_ctx.evil_C) {
|
|
blender::draw::command::StateSet::set();
|
|
DRW_submission_start();
|
|
ED_region_draw_cb_draw(draw_ctx.evil_C, draw_ctx.region, REGION_DRAW_PRE_VIEW);
|
|
DRW_submission_end();
|
|
}
|
|
|
|
/* State is reset later at the beginning of `draw_ctx.engines_draw_scene()`. */
|
|
}
|
|
|
|
static void drw_callbacks_post_scene_2D(DRWContext &draw_ctx, View2D &v2d)
|
|
{
|
|
const bool do_annotations = draw_show_annotation();
|
|
const bool do_draw_gizmos = (draw_ctx.space_data->spacetype != SPACE_IMAGE);
|
|
|
|
/* State has been reset at the end `draw_ctx.engines_draw_scene()`. */
|
|
|
|
DRW_submission_start();
|
|
if (draw_ctx.evil_C) {
|
|
DefaultFramebufferList *dfbl = DRW_context_get()->viewport_framebuffer_list_get();
|
|
|
|
GPU_framebuffer_bind(dfbl->overlay_fb);
|
|
|
|
GPU_depth_test(GPU_DEPTH_NONE);
|
|
GPU_matrix_push_projection();
|
|
|
|
wmOrtho2(v2d.cur.xmin, v2d.cur.xmax, v2d.cur.ymin, v2d.cur.ymax);
|
|
|
|
if (do_annotations) {
|
|
ED_annotation_draw_view2d(draw_ctx.evil_C, true);
|
|
}
|
|
|
|
GPU_depth_test(GPU_DEPTH_NONE);
|
|
|
|
ED_region_draw_cb_draw(draw_ctx.evil_C, draw_ctx.region, REGION_DRAW_POST_VIEW);
|
|
|
|
GPU_matrix_pop_projection();
|
|
/* Callback can be nasty and do whatever they want with the state.
|
|
* Don't trust them! */
|
|
blender::draw::command::StateSet::set();
|
|
|
|
GPU_depth_test(GPU_DEPTH_NONE);
|
|
|
|
if (do_annotations) {
|
|
ED_annotation_draw_view2d(draw_ctx.evil_C, false);
|
|
}
|
|
}
|
|
|
|
ED_region_pixelspace(draw_ctx.region);
|
|
|
|
if (do_draw_gizmos) {
|
|
GPU_depth_test(GPU_DEPTH_NONE);
|
|
DRW_draw_gizmo_2d(draw_ctx.evil_C, draw_ctx.region);
|
|
}
|
|
|
|
DRW_submission_end();
|
|
|
|
blender::draw::command::StateSet::set();
|
|
}
|
|
|
|
DRWTextStore *DRW_text_cache_ensure()
|
|
{
|
|
DRWContext &draw_ctx = drw_get();
|
|
BLI_assert(draw_ctx.text_store_p);
|
|
if (*draw_ctx.text_store_p == nullptr) {
|
|
*draw_ctx.text_store_p = DRW_text_cache_create();
|
|
}
|
|
return *draw_ctx.text_store_p;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Main Draw Loops (DRW_draw)
|
|
* \{ */
|
|
|
|
/**
|
|
* Used for both regular and off-screen drawing.
|
|
* The global `DRWContext` needs to be set before calling this function.
|
|
*/
|
|
static void drw_draw_render_loop_3d(DRWContext &draw_ctx, RenderEngineType *engine_type)
|
|
{
|
|
using namespace blender::draw;
|
|
Depsgraph *depsgraph = draw_ctx.depsgraph;
|
|
View3D *v3d = draw_ctx.v3d;
|
|
|
|
const int object_type_exclude_viewport = v3d->object_type_exclude_viewport;
|
|
/* Check if scene needs to perform the populate loop */
|
|
const bool internal_engine = (engine_type->flag & RE_INTERNAL) != 0;
|
|
const bool draw_type_render = v3d->shading.type == OB_RENDER;
|
|
const bool overlays_on = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0;
|
|
const bool gpencil_engine_needed = DRW_gpencil_engine_needed_viewport(depsgraph, v3d);
|
|
const bool do_populate_loop = internal_engine || overlays_on || !draw_type_render ||
|
|
gpencil_engine_needed;
|
|
|
|
draw_ctx.enable_engines(gpencil_engine_needed, engine_type);
|
|
draw_ctx.engines_data_validate();
|
|
draw_ctx.engines_init_and_sync([&](DupliCacheManager &duplis, ExtractionGraph &extraction) {
|
|
/* Only iterate over objects for internal engines or when overlays are enabled */
|
|
if (do_populate_loop) {
|
|
DEGObjectIterSettings deg_iter_settings = {nullptr};
|
|
deg_iter_settings.depsgraph = depsgraph;
|
|
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
|
|
if (v3d->flag2 & V3D_SHOW_VIEWER) {
|
|
deg_iter_settings.viewer_path = &v3d->viewer_path;
|
|
}
|
|
DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) {
|
|
if ((object_type_exclude_viewport & (1 << ob->type)) != 0) {
|
|
continue;
|
|
}
|
|
if (!BKE_object_is_visible_in_viewport(v3d, ob)) {
|
|
continue;
|
|
}
|
|
blender::draw::ObjectRef ob_ref(data_, ob);
|
|
drw_engines_cache_populate(ob_ref, duplis, extraction);
|
|
}
|
|
DEG_OBJECT_ITER_END;
|
|
}
|
|
});
|
|
|
|
/* No frame-buffer allowed before drawing. */
|
|
BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
|
|
GPU_framebuffer_bind(draw_ctx.default_framebuffer());
|
|
GPU_framebuffer_clear_depth_stencil(draw_ctx.default_framebuffer(), 1.0f, 0xFF);
|
|
|
|
drw_callbacks_pre_scene(draw_ctx);
|
|
draw_ctx.engines_draw_scene();
|
|
drw_callbacks_post_scene(draw_ctx);
|
|
|
|
if (WM_draw_region_get_bound_viewport(draw_ctx.region)) {
|
|
/* Don't unbind the frame-buffer yet in this case and let
|
|
* GPU_viewport_unbind do it, so that we can still do further
|
|
* drawing of action zones on top. */
|
|
}
|
|
else {
|
|
GPU_framebuffer_restore();
|
|
}
|
|
}
|
|
|
|
static void drw_draw_render_loop_2d(DRWContext &draw_ctx)
|
|
{
|
|
Depsgraph *depsgraph = draw_ctx.depsgraph;
|
|
ARegion *region = draw_ctx.region;
|
|
|
|
/* TODO(jbakker): Only populate when editor needs to draw object.
|
|
* for the image editor this is when showing UVs. */
|
|
const bool do_populate_loop = (draw_ctx.space_data->spacetype == SPACE_IMAGE);
|
|
|
|
draw_ctx.enable_engines();
|
|
draw_ctx.engines_data_validate();
|
|
draw_ctx.engines_init_and_sync([&](DupliCacheManager &duplis, ExtractionGraph &extraction) {
|
|
/* Only iterate over objects when overlay uses object data. */
|
|
if (do_populate_loop) {
|
|
DEGObjectIterSettings deg_iter_settings = {nullptr};
|
|
deg_iter_settings.depsgraph = depsgraph;
|
|
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
|
|
DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) {
|
|
blender::draw::ObjectRef ob_ref(ob);
|
|
drw_engines_cache_populate(ob_ref, duplis, extraction);
|
|
}
|
|
DEG_OBJECT_ITER_END;
|
|
}
|
|
});
|
|
|
|
/* No frame-buffer allowed before drawing. */
|
|
BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
|
|
GPU_framebuffer_bind(draw_ctx.default_framebuffer());
|
|
GPU_framebuffer_clear_depth_stencil(draw_ctx.default_framebuffer(), 1.0f, 0xFF);
|
|
|
|
drw_callbacks_pre_scene_2D(draw_ctx);
|
|
draw_ctx.engines_draw_scene();
|
|
drw_callbacks_post_scene_2D(draw_ctx, region->v2d);
|
|
|
|
if (WM_draw_region_get_bound_viewport(region)) {
|
|
/* Don't unbind the frame-buffer yet in this case and let
|
|
* GPU_viewport_unbind do it, so that we can still do further
|
|
* drawing of action zones on top. */
|
|
}
|
|
else {
|
|
GPU_framebuffer_restore();
|
|
}
|
|
}
|
|
|
|
void DRW_draw_view(const bContext *C)
|
|
{
|
|
Depsgraph *depsgraph = CTX_data_expect_evaluated_depsgraph(C);
|
|
ARegion *region = CTX_wm_region(C);
|
|
GPUViewport *viewport = WM_draw_region_get_bound_viewport(region);
|
|
|
|
DRWContext draw_ctx(DRWContext::VIEWPORT, depsgraph, viewport, C);
|
|
draw_ctx.acquire_data();
|
|
|
|
if (draw_ctx.v3d) {
|
|
Scene *scene = DEG_get_evaluated_scene(depsgraph);
|
|
RenderEngineType *engine_type = ED_view3d_engine_type(scene, draw_ctx.v3d->shading.type);
|
|
|
|
draw_ctx.options.draw_background = (scene->r.alphamode == R_ADDSKY) ||
|
|
(draw_ctx.v3d->shading.type != OB_RENDER);
|
|
|
|
drw_draw_render_loop_3d(draw_ctx, engine_type);
|
|
}
|
|
else {
|
|
drw_draw_render_loop_2d(draw_ctx);
|
|
}
|
|
|
|
draw_ctx.release_data();
|
|
}
|
|
|
|
void DRW_draw_render_loop_offscreen(Depsgraph *depsgraph,
|
|
RenderEngineType *engine_type,
|
|
ARegion *region,
|
|
View3D *v3d,
|
|
const bool is_image_render,
|
|
const bool draw_background,
|
|
const bool do_color_management,
|
|
GPUOffScreen *ofs,
|
|
GPUViewport *viewport)
|
|
{
|
|
const bool is_xr_surface = ((v3d->flag & V3D_XR_SESSION_SURFACE) != 0);
|
|
|
|
/* Create temporary viewport if needed or update the existing viewport. */
|
|
GPUViewport *render_viewport = viewport;
|
|
if (viewport == nullptr) {
|
|
render_viewport = GPU_viewport_create();
|
|
}
|
|
|
|
GPU_viewport_bind_from_offscreen(render_viewport, ofs, is_xr_surface);
|
|
|
|
/* Just here to avoid an assert but shouldn't be required in practice. */
|
|
GPU_framebuffer_restore();
|
|
|
|
/* TODO(fclem): We might want to differentiate between render preview and offscreen render in the
|
|
* future. The later can do progressive rendering. */
|
|
BLI_assert(is_xr_surface == !is_image_render);
|
|
UNUSED_VARS_NDEBUG(is_image_render);
|
|
DRWContext::Mode mode = is_xr_surface ? DRWContext::VIEWPORT_XR : DRWContext::VIEWPORT_RENDER;
|
|
|
|
DRWContext draw_ctx(mode, depsgraph, render_viewport, nullptr, region, v3d);
|
|
draw_ctx.acquire_data();
|
|
draw_ctx.options.draw_background = draw_background;
|
|
|
|
drw_draw_render_loop_3d(draw_ctx, engine_type);
|
|
|
|
draw_ctx.release_data();
|
|
|
|
if (draw_background) {
|
|
/* HACK(@fclem): In this case we need to make sure the final alpha is 1.
|
|
* We use the blend mode to ensure that. A better way to fix that would
|
|
* be to do that in the color-management shader. */
|
|
GPU_offscreen_bind(ofs, false);
|
|
GPU_clear_color(0.0f, 0.0f, 0.0f, 1.0f);
|
|
/* Pre-multiply alpha over black background. */
|
|
GPU_blend(GPU_BLEND_ALPHA_PREMULT);
|
|
}
|
|
|
|
GPU_matrix_identity_set();
|
|
GPU_matrix_identity_projection_set();
|
|
const bool do_overlays = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0 ||
|
|
ELEM(v3d->shading.type, OB_WIRE, OB_SOLID) ||
|
|
(ELEM(v3d->shading.type, OB_MATERIAL) &&
|
|
(v3d->shading.flag & V3D_SHADING_SCENE_WORLD) == 0) ||
|
|
(ELEM(v3d->shading.type, OB_RENDER) &&
|
|
(v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER) == 0);
|
|
GPU_viewport_unbind_from_offscreen(render_viewport, ofs, do_color_management, do_overlays);
|
|
|
|
if (draw_background) {
|
|
/* Reset default. */
|
|
GPU_blend(GPU_BLEND_NONE);
|
|
}
|
|
|
|
/* Free temporary viewport. */
|
|
if (viewport == nullptr) {
|
|
GPU_viewport_free(render_viewport);
|
|
}
|
|
}
|
|
|
|
bool DRW_render_check_grease_pencil(Depsgraph *depsgraph)
|
|
{
|
|
if (gpencil_any_exists(depsgraph)) {
|
|
return true;
|
|
}
|
|
|
|
DEGObjectIterSettings deg_iter_settings = {nullptr};
|
|
deg_iter_settings.depsgraph = depsgraph;
|
|
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
|
|
DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) {
|
|
if (ob->type == OB_GREASE_PENCIL) {
|
|
if (BKE_object_visibility(ob, DAG_EVAL_RENDER) & OB_VISIBLE_SELF) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
DEG_OBJECT_ITER_END;
|
|
|
|
return false;
|
|
}
|
|
|
|
void DRW_render_gpencil(RenderEngine *engine, Depsgraph *depsgraph)
|
|
{
|
|
using namespace blender::draw;
|
|
/* This function should only be called if there are grease pencil objects,
|
|
* especially important to avoid failing in background renders without GPU context. */
|
|
BLI_assert(DRW_render_check_grease_pencil(depsgraph));
|
|
|
|
Scene *scene = DEG_get_evaluated_scene(depsgraph);
|
|
ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
|
|
RenderResult *render_result = RE_engine_get_result(engine);
|
|
RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name);
|
|
if (render_layer == nullptr) {
|
|
return;
|
|
}
|
|
|
|
Render *render = engine->re;
|
|
|
|
DRW_render_context_enable(render);
|
|
|
|
DRWContext draw_ctx(DRWContext::RENDER, depsgraph, {engine->resolution_x, engine->resolution_y});
|
|
draw_ctx.acquire_data();
|
|
draw_ctx.options.draw_background = scene->r.alphamode == R_ADDSKY;
|
|
|
|
/* Main rendering. */
|
|
rctf view_rect;
|
|
rcti render_rect;
|
|
RE_GetViewPlane(render, &view_rect, &render_rect);
|
|
if (BLI_rcti_is_empty(&render_rect)) {
|
|
BLI_rcti_init(&render_rect, 0, draw_ctx.size[0], 0, draw_ctx.size[1]);
|
|
}
|
|
|
|
for (RenderView *render_view = static_cast<RenderView *>(render_result->views.first);
|
|
render_view != nullptr;
|
|
render_view = render_view->next)
|
|
{
|
|
RE_SetActiveRenderView(render, render_view->name);
|
|
gpencil::Engine::render_to_image(engine, render_layer, render_rect);
|
|
}
|
|
|
|
command::StateSet::set();
|
|
|
|
GPU_depth_test(GPU_DEPTH_NONE);
|
|
|
|
blender::gpu::TexturePool::get().reset(true);
|
|
|
|
draw_ctx.release_data();
|
|
|
|
/* Restore Drawing area. */
|
|
GPU_framebuffer_restore();
|
|
|
|
DRW_render_context_disable(render);
|
|
}
|
|
|
|
void DRW_render_to_image(
|
|
RenderEngine *engine,
|
|
Depsgraph *depsgraph,
|
|
std::function<void(RenderEngine *, RenderLayer *, const rcti)> render_view_cb,
|
|
std::function<void(RenderResult *)> store_metadata_cb)
|
|
{
|
|
using namespace blender::draw;
|
|
Scene *scene = DEG_get_evaluated_scene(depsgraph);
|
|
ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
|
|
Render *render = engine->re;
|
|
|
|
/* IMPORTANT: We don't support immediate mode in render mode!
|
|
* This shall remain in effect until immediate mode supports
|
|
* multiple threads. */
|
|
|
|
/* Begin GPU workload Boundary */
|
|
GPU_render_begin();
|
|
|
|
DRWContext draw_ctx(DRWContext::RENDER, depsgraph, {engine->resolution_x, engine->resolution_y});
|
|
draw_ctx.acquire_data();
|
|
draw_ctx.options.draw_background = scene->r.alphamode == R_ADDSKY;
|
|
|
|
/* Main rendering. */
|
|
rctf view_rect;
|
|
rcti render_rect;
|
|
RE_GetViewPlane(render, &view_rect, &render_rect);
|
|
if (BLI_rcti_is_empty(&render_rect)) {
|
|
BLI_rcti_init(&render_rect, 0, draw_ctx.size[0], 0, draw_ctx.size[1]);
|
|
}
|
|
|
|
/* Reset state before drawing */
|
|
command::StateSet::set();
|
|
|
|
/* set default viewport */
|
|
GPU_viewport(0, 0, draw_ctx.size[0], draw_ctx.size[1]);
|
|
|
|
/* Init render result. */
|
|
RenderResult *render_result = RE_engine_begin_result(engine,
|
|
0,
|
|
0,
|
|
draw_ctx.size[0],
|
|
draw_ctx.size[1],
|
|
view_layer->name,
|
|
/*RR_ALL_VIEWS*/ nullptr);
|
|
RenderLayer *render_layer = static_cast<RenderLayer *>(render_result->layers.first);
|
|
for (RenderView *render_view = static_cast<RenderView *>(render_result->views.first);
|
|
render_view != nullptr;
|
|
render_view = render_view->next)
|
|
{
|
|
RE_SetActiveRenderView(render, render_view->name);
|
|
render_view_cb(engine, render_layer, render_rect);
|
|
}
|
|
|
|
RE_engine_end_result(engine, render_result, false, false, false);
|
|
|
|
store_metadata_cb(RE_engine_get_result(engine));
|
|
|
|
GPU_framebuffer_restore();
|
|
|
|
blender::gpu::TexturePool::get().reset(true);
|
|
|
|
draw_ctx.release_data();
|
|
DRW_cache_free_old_subdiv();
|
|
|
|
/* End GPU workload Boundary */
|
|
GPU_render_end();
|
|
}
|
|
|
|
void DRW_render_object_iter(
|
|
RenderEngine *engine,
|
|
Depsgraph *depsgraph,
|
|
std::function<void(blender::draw::ObjectRef &, RenderEngine *, Depsgraph *)> callback)
|
|
{
|
|
DRWContext &draw_ctx = drw_get();
|
|
|
|
const int object_type_exclude_viewport = draw_ctx.v3d ?
|
|
draw_ctx.v3d->object_type_exclude_viewport :
|
|
0;
|
|
|
|
draw_ctx.sync([&](DupliCacheManager &duplis, ExtractionGraph &extraction) {
|
|
DEGObjectIterSettings deg_iter_settings = {nullptr};
|
|
deg_iter_settings.depsgraph = depsgraph;
|
|
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
|
|
DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) {
|
|
if ((object_type_exclude_viewport & (1 << ob->type)) == 0) {
|
|
blender::draw::ObjectRef ob_ref(data_, ob);
|
|
if (ob_ref.is_dupli() == false) {
|
|
blender::draw::drw_batch_cache_validate(ob);
|
|
}
|
|
else {
|
|
duplis.try_add(ob_ref);
|
|
}
|
|
callback(ob_ref, engine, depsgraph);
|
|
if (ob_ref.is_dupli() == false) {
|
|
blender::draw::drw_batch_cache_generate_requested(ob, *extraction.graph);
|
|
}
|
|
/* Batch generation for duplis happens after iter_callback. */
|
|
}
|
|
}
|
|
DEG_OBJECT_ITER_END;
|
|
});
|
|
}
|
|
|
|
void DRW_custom_pipeline_begin(DRWContext &draw_ctx, Depsgraph * /*depsgraph*/)
|
|
{
|
|
draw_ctx.acquire_data();
|
|
draw_ctx.data->modules_begin_sync();
|
|
}
|
|
|
|
void DRW_custom_pipeline_end(DRWContext &draw_ctx)
|
|
{
|
|
GPU_framebuffer_restore();
|
|
|
|
/* The use of custom pipeline in other thread using the same
|
|
* resources as the main thread (viewport) may lead to data
|
|
* races and undefined behavior on certain drivers. Using
|
|
* GPU_finish to sync seems to fix the issue. (see #62997) */
|
|
eGPUBackendType type = GPU_backend_get_type();
|
|
if (type == GPU_BACKEND_OPENGL) {
|
|
GPU_finish();
|
|
}
|
|
|
|
blender::gpu::TexturePool::get().reset(true);
|
|
draw_ctx.release_data();
|
|
}
|
|
|
|
void DRW_cache_restart()
|
|
{
|
|
using namespace blender::draw;
|
|
DRWContext &draw_ctx = drw_get();
|
|
draw_ctx.data->modules_exit();
|
|
draw_ctx.acquire_data();
|
|
draw_ctx.data->modules_begin_sync();
|
|
}
|
|
|
|
void DRW_render_set_time(RenderEngine *engine, Depsgraph *depsgraph, int frame, float subframe)
|
|
{
|
|
DRWContext &draw_ctx = drw_get();
|
|
RE_engine_frame_set(engine, frame, subframe);
|
|
draw_ctx.scene = DEG_get_evaluated_scene(depsgraph);
|
|
draw_ctx.view_layer = DEG_get_evaluated_view_layer(depsgraph);
|
|
}
|
|
|
|
static struct DRWSelectBuffer {
|
|
GPUFrameBuffer *framebuffer_depth_only;
|
|
blender::gpu::Texture *texture_depth;
|
|
} g_select_buffer = {nullptr};
|
|
|
|
static void draw_select_framebuffer_depth_only_setup(const int size[2])
|
|
{
|
|
if (g_select_buffer.framebuffer_depth_only == nullptr) {
|
|
g_select_buffer.framebuffer_depth_only = GPU_framebuffer_create("framebuffer_depth_only");
|
|
}
|
|
|
|
if ((g_select_buffer.texture_depth != nullptr) &&
|
|
((GPU_texture_width(g_select_buffer.texture_depth) != size[0]) ||
|
|
(GPU_texture_height(g_select_buffer.texture_depth) != size[1])))
|
|
{
|
|
GPU_texture_free(g_select_buffer.texture_depth);
|
|
g_select_buffer.texture_depth = nullptr;
|
|
}
|
|
|
|
if (g_select_buffer.texture_depth == nullptr) {
|
|
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
|
|
g_select_buffer.texture_depth = GPU_texture_create_2d(
|
|
"select_depth",
|
|
size[0],
|
|
size[1],
|
|
1,
|
|
blender::gpu::TextureFormat::SFLOAT_32_DEPTH,
|
|
usage,
|
|
nullptr);
|
|
|
|
GPU_framebuffer_texture_attach(
|
|
g_select_buffer.framebuffer_depth_only, g_select_buffer.texture_depth, 0, 0);
|
|
|
|
GPU_framebuffer_check_valid(g_select_buffer.framebuffer_depth_only, nullptr);
|
|
}
|
|
}
|
|
|
|
void DRW_draw_select_loop(Depsgraph *depsgraph,
|
|
ARegion *region,
|
|
View3D *v3d,
|
|
bool use_obedit_skip,
|
|
bool draw_surface,
|
|
bool /*use_nearest*/,
|
|
const bool do_material_sub_selection,
|
|
const rcti *rect,
|
|
DRW_SelectPassFn select_pass_fn,
|
|
void *select_pass_user_data,
|
|
DRW_ObjectFilterFn object_filter_fn,
|
|
void *object_filter_user_data)
|
|
{
|
|
using namespace blender::draw;
|
|
Scene *scene = DEG_get_evaluated_scene(depsgraph);
|
|
ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
|
|
const int viewport_size[2] = {BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)};
|
|
|
|
Object *obact = BKE_view_layer_active_object_get(view_layer);
|
|
Object *obedit = use_obedit_skip ? nullptr : OBEDIT_FROM_OBACT(obact);
|
|
|
|
bool use_obedit = false;
|
|
/* obedit_ctx_mode is used for selecting the right draw engines */
|
|
// eContextObjectMode obedit_ctx_mode;
|
|
/* object_mode is used for filtering objects in the depsgraph */
|
|
eObjectMode object_mode = eObjectMode::OB_MODE_EDIT;
|
|
int object_type = 0;
|
|
if (obedit != nullptr) {
|
|
object_type = obedit->type;
|
|
object_mode = eObjectMode(obedit->mode);
|
|
if (obedit->type == OB_MBALL) {
|
|
use_obedit = true;
|
|
// obedit_ctx_mode = CTX_MODE_EDIT_METABALL;
|
|
}
|
|
else if (obedit->type == OB_ARMATURE) {
|
|
use_obedit = true;
|
|
// obedit_ctx_mode = CTX_MODE_EDIT_ARMATURE;
|
|
}
|
|
}
|
|
if (v3d->overlay.flag & V3D_OVERLAY_BONE_SELECT) {
|
|
if (!(v3d->flag2 & V3D_HIDE_OVERLAYS)) {
|
|
/* NOTE: don't use "BKE_object_pose_armature_get" here, it breaks selection. */
|
|
Object *obpose = OBPOSE_FROM_OBACT(obact);
|
|
if (obpose == nullptr) {
|
|
Object *obweight = OBWEIGHTPAINT_FROM_OBACT(obact);
|
|
if (obweight) {
|
|
/* Only use Armature pose selection, when connected armature is in pose mode. */
|
|
Object *ob_armature = BKE_modifiers_is_deformed_by_armature(obweight);
|
|
if (ob_armature && ob_armature->mode == OB_MODE_POSE) {
|
|
obpose = ob_armature;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obpose) {
|
|
use_obedit = true;
|
|
object_type = obpose->type;
|
|
object_mode = eObjectMode(obpose->mode);
|
|
// obedit_ctx_mode = CTX_MODE_POSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool use_gpencil = !use_obedit && !draw_surface &&
|
|
DRW_gpencil_engine_needed_viewport(depsgraph, v3d);
|
|
|
|
DRWContext::Mode mode = do_material_sub_selection ? DRWContext::SELECT_OBJECT_MATERIAL :
|
|
DRWContext::SELECT_OBJECT;
|
|
|
|
DRWContext draw_ctx(mode, depsgraph, viewport_size, nullptr, region, v3d);
|
|
draw_ctx.acquire_data();
|
|
draw_ctx.enable_engines(use_gpencil);
|
|
draw_ctx.engines_data_validate();
|
|
draw_ctx.engines_init_and_sync([&](DupliCacheManager &duplis, ExtractionGraph &extraction) {
|
|
if (use_obedit) {
|
|
FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, object_type, object_mode, ob_iter) {
|
|
/* Depsgraph usually does this, but we use a different iterator.
|
|
* So we have to do it manually. */
|
|
ob_iter->runtime->select_id = DEG_get_original(ob_iter)->runtime->select_id;
|
|
|
|
blender::draw::ObjectRef ob_ref(ob_iter);
|
|
drw_engines_cache_populate(ob_ref, duplis, extraction);
|
|
}
|
|
FOREACH_OBJECT_IN_MODE_END;
|
|
}
|
|
else {
|
|
/* When selecting pose-bones in pose mode, check for visibility not select-ability
|
|
* as pose-bones have their own selection restriction flag. */
|
|
const bool use_pose_exception = (draw_ctx.object_pose != nullptr);
|
|
|
|
const int object_type_exclude_select = (v3d->object_type_exclude_viewport |
|
|
v3d->object_type_exclude_select);
|
|
bool filter_exclude = false;
|
|
DEGObjectIterSettings deg_iter_settings = {nullptr};
|
|
deg_iter_settings.depsgraph = depsgraph;
|
|
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
|
|
if (v3d->flag2 & V3D_SHOW_VIEWER) {
|
|
deg_iter_settings.viewer_path = &v3d->viewer_path;
|
|
}
|
|
DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) {
|
|
if (!BKE_object_is_visible_in_viewport(v3d, ob)) {
|
|
continue;
|
|
}
|
|
|
|
if (use_pose_exception && (ob->mode & OB_MODE_POSE)) {
|
|
if ((ob->base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) == 0) {
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
if ((ob->base_flag & BASE_SELECTABLE) == 0) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((object_type_exclude_select & (1 << ob->type)) == 0) {
|
|
if (object_filter_fn != nullptr) {
|
|
if (ob->base_flag & BASE_FROM_DUPLI) {
|
|
/* pass (use previous filter_exclude value) */
|
|
}
|
|
else {
|
|
filter_exclude = (object_filter_fn(ob, object_filter_user_data) == false);
|
|
}
|
|
if (filter_exclude) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
blender::draw::ObjectRef ob_ref(data_, ob);
|
|
drw_engines_cache_populate(ob_ref, duplis, extraction);
|
|
}
|
|
}
|
|
DEG_OBJECT_ITER_END;
|
|
}
|
|
});
|
|
|
|
/* Setup frame-buffer. */
|
|
draw_select_framebuffer_depth_only_setup(viewport_size);
|
|
GPU_framebuffer_bind(g_select_buffer.framebuffer_depth_only);
|
|
GPU_framebuffer_clear_depth(g_select_buffer.framebuffer_depth_only, 1.0f);
|
|
|
|
/* WORKAROUND: Needed for Select-Next for keeping the same code-flow as Overlay-Next. */
|
|
/* TODO(pragma37): Some engines retrieve the depth texture before this point (See #132922).
|
|
* Check with @fclem. */
|
|
BLI_assert(DRW_context_get()->viewport_texture_list_get()->depth == nullptr);
|
|
DRW_context_get()->viewport_texture_list_get()->depth = g_select_buffer.texture_depth;
|
|
|
|
drw_callbacks_pre_scene(draw_ctx);
|
|
/* Only 1-2 passes. */
|
|
while (true) {
|
|
if (!select_pass_fn(DRW_SELECT_PASS_PRE, select_pass_user_data)) {
|
|
break;
|
|
}
|
|
draw_ctx.engines_draw_scene();
|
|
if (!select_pass_fn(DRW_SELECT_PASS_POST, select_pass_user_data)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* WORKAROUND: Do not leave ownership to the viewport list. */
|
|
DRW_context_get()->viewport_texture_list_get()->depth = nullptr;
|
|
|
|
draw_ctx.release_data();
|
|
|
|
GPU_framebuffer_restore();
|
|
}
|
|
|
|
void DRW_draw_depth_loop(Depsgraph *depsgraph,
|
|
ARegion *region,
|
|
View3D *v3d,
|
|
GPUViewport *viewport,
|
|
const bool use_gpencil,
|
|
const bool use_only_selected,
|
|
const bool use_only_active_object)
|
|
{
|
|
using namespace blender::draw;
|
|
|
|
DRWContext draw_ctx(use_only_active_object ? DRWContext::DEPTH_ACTIVE_OBJECT : DRWContext::DEPTH,
|
|
depsgraph,
|
|
viewport,
|
|
nullptr,
|
|
region,
|
|
v3d);
|
|
draw_ctx.acquire_data();
|
|
draw_ctx.enable_engines(use_gpencil);
|
|
draw_ctx.engines_init_and_sync([&](DupliCacheManager &duplis, ExtractionGraph &extraction) {
|
|
const int object_type_exclude_viewport = v3d->object_type_exclude_viewport;
|
|
DEGObjectIterSettings deg_iter_settings = {nullptr};
|
|
deg_iter_settings.depsgraph = draw_ctx.depsgraph;
|
|
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
|
|
if (v3d->flag2 & V3D_SHOW_VIEWER) {
|
|
deg_iter_settings.viewer_path = &v3d->viewer_path;
|
|
}
|
|
if (use_only_active_object) {
|
|
blender::draw::ObjectRef ob_ref(draw_ctx.obact);
|
|
drw_engines_cache_populate(ob_ref, duplis, extraction);
|
|
}
|
|
else {
|
|
DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) {
|
|
if ((object_type_exclude_viewport & (1 << ob->type)) != 0) {
|
|
continue;
|
|
}
|
|
if (!BKE_object_is_visible_in_viewport(v3d, ob)) {
|
|
continue;
|
|
}
|
|
if (use_only_selected && !(ob->base_flag & BASE_SELECTED)) {
|
|
continue;
|
|
}
|
|
if ((ob->base_flag & BASE_SELECTABLE) == 0) {
|
|
continue;
|
|
}
|
|
blender::draw::ObjectRef ob_ref(data_, ob);
|
|
drw_engines_cache_populate(ob_ref, duplis, extraction);
|
|
}
|
|
DEG_OBJECT_ITER_END;
|
|
}
|
|
});
|
|
|
|
/* Setup frame-buffer. */
|
|
blender::gpu::Texture *depth_tx = GPU_viewport_depth_texture(viewport);
|
|
GPUFrameBuffer *depth_fb = nullptr;
|
|
GPU_framebuffer_ensure_config(&depth_fb,
|
|
{
|
|
GPU_ATTACHMENT_TEXTURE(depth_tx),
|
|
GPU_ATTACHMENT_NONE,
|
|
});
|
|
GPU_framebuffer_bind(depth_fb);
|
|
GPU_framebuffer_clear_depth(depth_fb, 1.0f);
|
|
|
|
draw_ctx.engines_draw_scene();
|
|
|
|
/* TODO: Reading depth for operators should be done here. */
|
|
|
|
GPU_framebuffer_restore();
|
|
GPU_framebuffer_free(depth_fb);
|
|
|
|
draw_ctx.release_data();
|
|
}
|
|
|
|
void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d)
|
|
{
|
|
SELECTID_Context *sel_ctx = DRW_select_engine_context_get();
|
|
GPUViewport *viewport = WM_draw_region_get_viewport(region);
|
|
if (!viewport) {
|
|
/* Selection engine requires a viewport.
|
|
* TODO(@germano): This should be done internally in the engine. */
|
|
sel_ctx->max_index_drawn_len = 1;
|
|
return;
|
|
}
|
|
|
|
/* Make sure select engine gets the correct vertex size. */
|
|
UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW);
|
|
|
|
DRWContext draw_ctx(DRWContext::SELECT_EDIT_MESH, depsgraph, viewport, nullptr, region, v3d);
|
|
draw_ctx.acquire_data();
|
|
draw_ctx.enable_engines();
|
|
draw_ctx.engines_init_and_sync([&](DupliCacheManager &duplis, ExtractionGraph &extraction) {
|
|
for (Object *obj_eval : sel_ctx->objects) {
|
|
blender::draw::ObjectRef ob_ref(obj_eval);
|
|
drw_engines_cache_populate(ob_ref, duplis, extraction);
|
|
}
|
|
|
|
if (RETOPOLOGY_ENABLED(v3d) && !XRAY_ENABLED(v3d)) {
|
|
DEGObjectIterSettings deg_iter_settings = {nullptr};
|
|
deg_iter_settings.depsgraph = depsgraph;
|
|
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
|
|
DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) {
|
|
if (ob->type != OB_MESH) {
|
|
/* The iterator has evaluated meshes for all solid objects.
|
|
* It also has non-mesh objects however, which are not supported here. */
|
|
continue;
|
|
}
|
|
if (DRW_object_is_in_edit_mode(ob)) {
|
|
/* Only background (non-edit) objects are used for occlusion. */
|
|
continue;
|
|
}
|
|
if (!BKE_object_is_visible_in_viewport(v3d, ob)) {
|
|
continue;
|
|
}
|
|
blender::draw::ObjectRef ob_ref(data_, ob);
|
|
drw_engines_cache_populate(ob_ref, duplis, extraction);
|
|
}
|
|
DEG_OBJECT_ITER_END;
|
|
}
|
|
});
|
|
|
|
draw_ctx.engines_draw_scene();
|
|
|
|
draw_ctx.release_data();
|
|
}
|
|
|
|
bool DRW_draw_in_progress()
|
|
{
|
|
return DRWContext::is_active();
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Draw Manager State
|
|
* \{ */
|
|
|
|
const DRWContext *DRW_context_get()
|
|
{
|
|
return &drw_get();
|
|
}
|
|
|
|
bool DRWContext::is_playback() const
|
|
{
|
|
if (this->evil_C != nullptr) {
|
|
wmWindowManager *wm = CTX_wm_manager(this->evil_C);
|
|
return ED_screen_animation_playing(wm) != nullptr;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DRWContext::is_navigating() const
|
|
{
|
|
return (rv3d) && (rv3d->rflag & (RV3D_NAVIGATING | RV3D_PAINTING));
|
|
}
|
|
|
|
bool DRWContext::is_painting() const
|
|
{
|
|
return (rv3d) && (rv3d->rflag & (RV3D_PAINTING));
|
|
}
|
|
|
|
bool DRWContext::is_transforming() const
|
|
{
|
|
return (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) != 0;
|
|
}
|
|
|
|
bool DRWContext::is_viewport_compositor_enabled() const
|
|
{
|
|
if (!this->v3d) {
|
|
return false;
|
|
}
|
|
|
|
if (this->v3d->shading.use_compositor == V3D_SHADING_USE_COMPOSITOR_DISABLED) {
|
|
return false;
|
|
}
|
|
|
|
if (!(this->v3d->shading.type >= OB_MATERIAL)) {
|
|
return false;
|
|
}
|
|
|
|
if (!this->scene->compositing_node_group) {
|
|
return false;
|
|
}
|
|
|
|
if (!this->rv3d) {
|
|
return false;
|
|
}
|
|
|
|
if (this->v3d->shading.use_compositor == V3D_SHADING_USE_COMPOSITOR_CAMERA &&
|
|
this->rv3d->persp != RV3D_CAMOB)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name DRW_engines
|
|
* \{ */
|
|
|
|
void DRW_engines_register()
|
|
{
|
|
RE_engines_register(&DRW_engine_viewport_eevee_type);
|
|
RE_engines_register(&DRW_engine_viewport_workbench_type);
|
|
}
|
|
|
|
void DRW_engines_free()
|
|
{
|
|
blender::eevee::Engine::free_static();
|
|
blender::workbench::Engine::free_static();
|
|
blender::draw::gpencil::Engine::free_static();
|
|
blender::image_engine::Engine::free_static();
|
|
blender::draw::overlay::Engine::free_static();
|
|
blender::draw::edit_select::Engine::free_static();
|
|
#ifdef WITH_DRAW_DEBUG
|
|
blender::draw::edit_select_debug::Engine::free_static();
|
|
#endif
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name DRW_module
|
|
* \{ */
|
|
|
|
void DRW_module_init()
|
|
{
|
|
using namespace blender::draw;
|
|
/* setup callbacks */
|
|
BKE_curve_batch_cache_dirty_tag_cb = DRW_curve_batch_cache_dirty_tag;
|
|
BKE_curve_batch_cache_free_cb = DRW_curve_batch_cache_free;
|
|
|
|
BKE_mesh_batch_cache_dirty_tag_cb = DRW_mesh_batch_cache_dirty_tag;
|
|
BKE_mesh_batch_cache_free_cb = DRW_mesh_batch_cache_free;
|
|
|
|
BKE_lattice_batch_cache_dirty_tag_cb = DRW_lattice_batch_cache_dirty_tag;
|
|
BKE_lattice_batch_cache_free_cb = DRW_lattice_batch_cache_free;
|
|
|
|
BKE_particle_batch_cache_dirty_tag_cb = DRW_particle_batch_cache_dirty_tag;
|
|
BKE_particle_batch_cache_free_cb = DRW_particle_batch_cache_free;
|
|
|
|
BKE_curves_batch_cache_dirty_tag_cb = DRW_curves_batch_cache_dirty_tag;
|
|
BKE_curves_batch_cache_free_cb = DRW_curves_batch_cache_free;
|
|
|
|
BKE_pointcloud_batch_cache_dirty_tag_cb = DRW_pointcloud_batch_cache_dirty_tag;
|
|
BKE_pointcloud_batch_cache_free_cb = DRW_pointcloud_batch_cache_free;
|
|
|
|
BKE_volume_batch_cache_dirty_tag_cb = DRW_volume_batch_cache_dirty_tag;
|
|
BKE_volume_batch_cache_free_cb = DRW_volume_batch_cache_free;
|
|
|
|
BKE_grease_pencil_batch_cache_dirty_tag_cb = DRW_grease_pencil_batch_cache_dirty_tag;
|
|
BKE_grease_pencil_batch_cache_free_cb = DRW_grease_pencil_batch_cache_free;
|
|
|
|
BKE_subsurf_modifier_free_gpu_cache_cb = DRW_subdiv_cache_free;
|
|
}
|
|
|
|
void DRW_module_exit()
|
|
{
|
|
GPU_TEXTURE_FREE_SAFE(g_select_buffer.texture_depth);
|
|
GPU_FRAMEBUFFER_FREE_SAFE(g_select_buffer.framebuffer_depth_only);
|
|
|
|
DRW_shaders_free();
|
|
}
|
|
|
|
/** \} */
|