Blender V5.0
external_engine.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2017 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11
12#include "BKE_paint.hh"
13#include "DRW_engine.hh"
14#include "DRW_render.hh"
15
16#include "BLI_string.h"
17
18#include "BLT_translation.hh"
19
20#include "DNA_particle_types.h"
21#include "DNA_screen_types.h"
22#include "DNA_view3d_types.h"
23
24#include "ED_image.hh"
25#include "ED_render.hh"
26#include "ED_screen.hh"
27#include "ED_view3d.hh"
28
29#include "GPU_debug.hh"
30#include "GPU_matrix.hh"
31#include "GPU_state.hh"
32
33#include "RE_engine.h"
34#include "RE_pipeline.h"
35
36#include "draw_cache.hh"
37#include "draw_cache_impl.hh"
38#include "draw_command.hh"
39#include "draw_common.hh"
40#include "draw_pass.hh"
41#include "draw_sculpt.hh"
42#include "draw_view.hh"
43#include "draw_view_data.hh"
44
45#include "external_engine.h" /* own include */
46
47/* Shaders */
48
50
56class Prepass {
57 private:
58 PassMain ps_ = {"prepass"};
59 PassMain::Sub *mesh_ps_ = nullptr;
60 PassMain::Sub *curves_ps_ = nullptr;
61 PassMain::Sub *pointcloud_ps_ = nullptr;
62
63 /* Reuse overlay shaders. */
64 gpu::StaticShader depth_mesh = {"overlay_depth_mesh"};
65 gpu::StaticShader depth_curves = {"overlay_depth_curves"};
66 gpu::StaticShader depth_pointcloud = {"overlay_depth_pointcloud"};
67
69
70 public:
72 {
73 dummy_buf.push_update();
74
75 ps_.init();
77 /* Dummy binds. They are unused in the variant we use.
78 * Just avoid validation layers complaining. */
79 ps_.bind_ubo(OVERLAY_GLOBALS_SLOT, &dummy_buf);
80 ps_.bind_ubo(DRW_CLIPPING_UBO_SLOT, &dummy_buf);
81 {
82 auto &sub = ps_.sub("Mesh");
83 sub.shader_set(depth_mesh.get());
84 mesh_ps_ = ⊂
85 }
86 {
87 auto &sub = ps_.sub("Curves");
88 sub.shader_set(depth_curves.get());
89 curves_ps_ = ⊂
90 }
91 {
92 auto &sub = ps_.sub("PointCloud");
93 sub.shader_set(depth_pointcloud.get());
94 pointcloud_ps_ = ⊂
95 }
96 }
97
98 void particle_sync(Manager &manager, const ObjectRef &ob_ref)
99 {
100 Object *ob = ob_ref.object;
101
102 ResourceHandleRange handle = {};
103
106 continue;
107 }
108
109 const ParticleSettings *part = psys->part;
110 const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
111 if (draw_as == PART_DRAW_PATH && part->draw_as == PART_DRAW_REND) {
112 /* Case where the render engine should have rendered it, but we need to draw it for
113 * selection purpose. */
114 if (!handle.is_valid()) {
115 handle = manager.resource_handle_for_psys(ob_ref, ob_ref.particles_matrix());
116 }
117
118 gpu::Batch *geom = DRW_cache_particles_get_hair(ob, psys, nullptr);
119 mesh_ps_->draw(geom, handle);
120 break;
121 }
122 }
123 }
124
125 void sculpt_sync(Manager &manager, const ObjectRef &ob_ref)
126 {
127 ResourceHandleRange handle = manager.unique_handle_for_sculpt(ob_ref);
128
130 mesh_ps_->draw(batch.batch, handle);
131 }
132 }
133
134 void object_sync(Manager &manager, const ObjectRef &ob_ref, const DRWContext &draw_ctx)
135 {
136 bool is_solid = ob_ref.object->dt >= OB_SOLID ||
138
139 if (!is_solid) {
140 return;
141 }
142
143 particle_sync(manager, ob_ref);
144
145 const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob_ref.object, draw_ctx.rv3d);
146
147 if (use_sculpt_pbvh) {
148 sculpt_sync(manager, ob_ref);
149 return;
150 }
151
152 gpu::Batch *geom_single = nullptr;
153 Span<gpu::Batch *> geom_list(&geom_single, 1);
154
155 PassMain::Sub *pass = nullptr;
156 switch (ob_ref.object->type) {
157 case OB_MESH:
158 geom_single = DRW_cache_mesh_surface_get(ob_ref.object);
159 pass = mesh_ps_;
160 break;
161 case OB_POINTCLOUD:
162 geom_single = pointcloud_sub_pass_setup(*pointcloud_ps_, ob_ref.object);
163 pass = pointcloud_ps_;
164 break;
165 case OB_CURVES: {
166 const char *error = nullptr;
167 /* We choose to ignore the error here as the external engine can display them properly.
168 * The overlays can still be broken but it should be detected in solid mode. */
169 geom_single = curves_sub_pass_setup(*curves_ps_, draw_ctx.scene, ob_ref.object, error);
170 pass = curves_ps_;
171 break;
172 }
173 default:
174 break;
175 }
176
177 if (pass == nullptr) {
178 return;
179 }
180
181 ResourceHandleRange res_handle = manager.unique_handle(ob_ref);
182
183 for (int material_id : geom_list.index_range()) {
184 pass->draw(geom_list[material_id], res_handle);
185 }
186 }
187
188 void submit(Manager &manager, View &view)
189 {
190 manager.submit(ps_, view);
191 }
192};
193
194class Instance : public DrawEngine {
195 const DRWContext *draw_ctx = nullptr;
196
197 Prepass prepass;
198 /* Only do prepass if there is a need for it.
199 * This is only needed for GPencil integration. */
200 bool do_prepass = false;
201
203 {
204 return "External";
205 }
206
207 void init() final
208 {
209 draw_ctx = DRW_context_get();
210 do_prepass = DRW_gpencil_engine_needed_viewport(draw_ctx->depsgraph, draw_ctx->v3d);
211 }
212
213 void begin_sync() final
214 {
215 if (do_prepass) {
216 prepass.begin_sync();
217 }
218 }
219
220 void object_sync(blender::draw::ObjectRef &ob_ref, blender::draw::Manager &manager) final
221 {
222 if (do_prepass) {
223 prepass.object_sync(manager, ob_ref, *draw_ctx);
224 }
225 }
226
227 void end_sync() final {}
228
229 void draw_scene_do_v3d(blender::draw::Manager &manager, draw::View &view)
230 {
231 RegionView3D *rv3d = draw_ctx->rv3d;
232 ARegion *region = draw_ctx->region;
233
235
236 /* The external engine can use the OpenGL rendering API directly, so make sure the state is
237 * already applied. */
239
240 /* Create render engine. */
241 RenderEngine *render_engine = nullptr;
242 if (!rv3d->view_render) {
243 RenderEngineType *engine_type = ED_view3d_engine_type(draw_ctx->scene,
244 draw_ctx->v3d->shading.type);
245
246 if (!(engine_type->view_update && engine_type->view_draw)) {
247 return;
248 }
249
250 rv3d->view_render = RE_NewViewRender(engine_type);
251 render_engine = RE_view_engine_get(rv3d->view_render);
252 engine_type->view_update(render_engine, draw_ctx->evil_C, draw_ctx->depsgraph);
253 }
254 else {
255 render_engine = RE_view_engine_get(rv3d->view_render);
256 }
257
258 /* Rendered draw. */
261 ED_region_pixelspace(region);
262
263 /* Render result draw. */
264 const RenderEngineType *type = render_engine->type;
265 type->view_draw(render_engine, draw_ctx->evil_C, draw_ctx->depsgraph);
266
269
270 if (do_prepass) {
271 prepass.submit(manager, view);
272 }
273
274 /* Set render info. */
275 if (render_engine->text[0] != '\0') {
276 STRNCPY(info, render_engine->text);
277 }
278 else {
279 info[0] = '\0';
280 }
281 }
282
283 /* Configure current matrix stack so that the external engine can use the same drawing code for
284 * both viewport and image editor drawing.
285 *
286 * The engine draws result in the pixel space, and is applying render offset. For image editor we
287 * need to switch from normalized space to pixel space, and "un-apply" offset. */
288 void external_image_space_matrix_set(const RenderEngine *engine)
289 {
290 BLI_assert(engine != nullptr);
291
292 SpaceImage *space_image = (SpaceImage *)draw_ctx->space_data;
293
294 /* Apply current view as transformation matrix.
295 * This will configure drawing for normalized space with current zoom and pan applied. */
296
298 float4x4 projection_matrix = blender::draw::View::default_get().winmat();
299
300 GPU_matrix_projection_set(projection_matrix.ptr());
301 GPU_matrix_set(view_matrix.ptr());
302
303 /* Switch from normalized space to pixel space. */
304 {
305 int width, height;
306 ED_space_image_get_size(space_image, &width, &height);
307
308 const float width_inv = width ? 1.0f / width : 0.0f;
309 const float height_inv = height ? 1.0f / height : 0.0f;
310 GPU_matrix_scale_2f(width_inv, height_inv);
311 }
312
313 /* Un-apply render offset. */
314 {
315 Render *render = engine->re;
316 rctf view_rect;
317 rcti render_rect;
318 RE_GetViewPlane(render, &view_rect, &render_rect);
319
320 GPU_matrix_translate_2f(-render_rect.xmin, -render_rect.ymin);
321 }
322 }
323
324 void draw_scene_do_image()
325 {
326 /* Get scene from the render job, to show progress for scenes render as part
327 * of compositor or sequencer. */
328 Scene *scene = ED_render_job_get_current_scene(draw_ctx->evil_C);
329 if (scene == nullptr) {
330 scene = draw_ctx->scene;
331 }
332
333 Render *re = RE_GetSceneRender(scene);
334 RenderEngine *engine = RE_engine_get(re);
335
336 /* Is tested before enabling the drawing engine. */
337 BLI_assert(re != nullptr);
338 BLI_assert(engine != nullptr);
339
341
342 /* The external engine can use the OpenGL rendering API directly, so make sure the state is
343 * already applied. */
345
346 const DefaultFramebufferList *dfbl = draw_ctx->viewport_framebuffer_list_get();
347
348 /* Clear the depth buffer to the value used by the background overlay so that the overlay is
349 * not happening outside of the drawn image.
350 *
351 * NOTE: The external engine only draws color. The depth is taken care of using the depth pass
352 * which initialized the depth to the values expected by the background overlay. */
354
357
358 external_image_space_matrix_set(engine);
359
360 GPU_debug_group_begin("External Engine");
361
362 const RenderEngineType *engine_type = engine->type;
363 BLI_assert(engine_type != nullptr);
364 BLI_assert(engine_type->draw != nullptr);
365
366 engine_type->draw(engine, draw_ctx->evil_C, draw_ctx->depsgraph);
367
369
372
374
376 }
377
378 void draw_scene_do(blender::draw::Manager &manager, View &view)
379 {
380 if (draw_ctx->v3d != nullptr) {
381 draw_scene_do_v3d(manager, view);
382 return;
383 }
384
385 if (draw_ctx->space_data == nullptr) {
386 return;
387 }
388
389 const eSpace_Type space_type = eSpace_Type(draw_ctx->space_data->spacetype);
390 if (space_type == SPACE_IMAGE) {
391 draw_scene_do_image();
392 return;
393 }
394 }
395
396 void draw(blender::draw::Manager &manager) final
397 {
398 /* TODO(fclem): Remove global access. */
400
401 const DefaultFramebufferList *dfbl = draw_ctx->viewport_framebuffer_list_get();
402
403 /* Will be nullptr during OpenGL render.
404 * OpenGL render is used for quick preview (thumbnails or sequencer preview)
405 * where using the rendering engine to preview doesn't make so much sense. */
406 if (draw_ctx->evil_C) {
407 const float clear_col[4] = {0, 0, 0, 0};
408 /* This is to keep compatibility with external engine. */
409 /* TODO(fclem): remove it eventually. */
411 GPU_framebuffer_clear_color(dfbl->default_fb, clear_col);
412
414 draw_scene_do(manager, view);
416 }
417 }
418};
419
421{
422 return new Instance();
423}
424
425} // namespace blender::draw::external
426
427/* Functions */
428
429/* NOTE: currently unused,
430 * we should not register unless we want to see this when debugging the view. */
431
433 /*next*/ nullptr,
434 /*prev*/ nullptr,
435 /*idname*/ "BLENDER_EXTERNAL",
436 /*name*/ N_("External"),
438 /*update*/ nullptr,
439 /*render*/ nullptr,
440 /*render_frame_finish*/ nullptr,
441 /*draw*/ nullptr,
442 /*bake*/ nullptr,
443 /*view_update*/ nullptr,
444 /*view_draw*/ nullptr,
445 /*update_script_node*/ nullptr,
446 /*update_render_passes*/ nullptr,
447 /*update_custom_camera*/ nullptr,
448 /*draw_engine*/ nullptr,
449 /*rna_ext*/
450 {
451 /*data*/ nullptr,
452 /*srna*/ nullptr,
453 /*call*/ nullptr,
454 },
455};
456
458{
459 /* Get scene from the render job, to show progress for scenes render as part
460 * of compositor or sequencer. */
461 Scene *scene = ED_render_job_get_current_scene(draw_ctx->evil_C);
462 if (scene == nullptr) {
463 scene = draw_ctx->scene;
464 }
465
466 const SpaceLink *space_data = draw_ctx->space_data;
467 if (space_data == nullptr) {
468 return false;
469 }
470
471 const eSpace_Type space_type = eSpace_Type(draw_ctx->space_data->spacetype);
472 if (space_type != SPACE_IMAGE) {
473 return false;
474 }
475
476 SpaceImage *space_image = (SpaceImage *)space_data;
477 const Image *image = ED_space_image(space_image);
478 if (image == nullptr || image->type != IMA_TYPE_R_RESULT) {
479 return false;
480 }
481
482 if (image->render_slot != image->last_render_slot) {
483 return false;
484 }
485
486 /* Render is allocated on main thread, so it is safe to access it from here. */
487 Render *re = RE_GetSceneRender(scene);
488
489 if (re == nullptr) {
490 return false;
491 }
492
493 return RE_engine_draw_acquire(re);
494}
495
497{
498 if (rv3d->view_render) {
499 /* Free engine with DRW context enabled, as this may clean up per-context
500 * resources like VAOs. */
501 bool swap_context = !DRW_gpu_context_is_enabled();
502 if (swap_context) {
504 }
506 rv3d->view_render = nullptr;
507 if (swap_context) {
509 }
510 }
511}
bool BKE_sculptsession_use_pbvh_draw(const Object *ob, const RegionView3D *rv3d)
Definition paint.cc:3068
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
@ IMA_TYPE_R_RESULT
@ OB_SOLID
@ OB_HIDE_CAMERA
@ OB_MESH
@ OB_POINTCLOUD
@ OB_CURVES
@ PART_DRAW_PATH
@ PART_DRAW_REND
eSpace_Type
@ SPACE_IMAGE
void DRW_submission_end()
bool DRW_gpu_context_is_enabled()
bool DRW_gpencil_engine_needed_viewport(Depsgraph *depsgraph, View3D *v3d)
void DRW_gpu_context_disable_ex(bool restore)
void DRW_gpu_context_enable_ex(bool restore)
void DRW_engine_external_free(RegionView3D *rv3d)
void DRW_submission_start()
void ED_space_image_get_size(SpaceImage *sima, int *r_width, int *r_height)
Image * ED_space_image(const SpaceImage *sima)
Definition image_edit.cc:40
Scene * ED_render_job_get_current_scene(const bContext *C)
void ED_region_pixelspace(const ARegion *region)
Definition area.cc:87
RenderEngineType * ED_view3d_engine_type(const Scene *scene, int drawtype)
static AppView * view
void GPU_debug_group_end()
Definition gpu_debug.cc:33
void GPU_debug_group_begin(const char *name)
Definition gpu_debug.cc:22
void GPU_framebuffer_clear_depth(blender::gpu::FrameBuffer *fb, float clear_depth)
void GPU_framebuffer_clear_color(blender::gpu::FrameBuffer *fb, const float clear_col[4])
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
void GPU_matrix_scale_2f(float x, float y)
#define GPU_matrix_set(x)
void GPU_matrix_push()
void GPU_matrix_push_projection()
void GPU_matrix_pop_projection()
#define GPU_matrix_projection_set(x)
void GPU_matrix_pop()
void GPU_matrix_translate_2f(float x, float y)
void GPU_apply_state()
Definition gpu_state.cc:315
@ RE_INTERNAL
Definition RE_engine.h:43
@ RE_USE_STEREO_VIEWPORT
Definition RE_engine.h:49
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
ResourceHandle resource_handle_for_psys(const ObjectRef &ref, const float4x4 &model_matrix)
ResourceHandleRange unique_handle_for_sculpt(const ObjectRef &ref)
ResourceHandleRange unique_handle(const ObjectRef &ref)
void submit(PassSimple &pass, View &view)
float4x4 particles_matrix() const
const float4x4 & winmat(int view_id=0) const
Definition draw_view.hh:148
const float4x4 & viewmat(int view_id=0) const
Definition draw_view.hh:136
static View & default_get()
Definition draw_view.cc:317
void draw(gpu::Batch *batch, uint instance_len=-1, uint vertex_len=-1, uint vertex_first=-1, ResourceIndexRange res_index={}, uint custom_id=0)
Definition draw_pass.hh:893
detail::PassBase< command::DrawMultiBuf > Sub
Definition draw_pass.hh:499
void sculpt_sync(Manager &manager, const ObjectRef &ob_ref)
void particle_sync(Manager &manager, const ObjectRef &ob_ref)
void object_sync(Manager &manager, const ObjectRef &ob_ref, const DRWContext &draw_ctx)
void submit(Manager &manager, View &view)
const DRWContext * DRW_context_get()
bool DRW_object_is_visible_psys_in_active_context(const Object *object, const ParticleSystem *psys)
#define DRW_CLIPPING_UBO_SLOT
#define OVERLAY_GLOBALS_SLOT
@ DRW_STATE_WRITE_DEPTH
Definition draw_state.hh:29
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
@ DRW_STATE_DEPTH_LESS_EQUAL
Definition draw_state.hh:38
bool DRW_engine_external_acquire_for_image_editor(const DRWContext *draw_ctx)
RenderEngineType DRW_engine_viewport_external_type
struct @021025263243242147216143265077100330027142264337::@225245033123204053237120173316075113304004012000 batch
void RE_GetViewPlane(Render *re, rctf *r_viewplane, rcti *r_disprect)
void RE_engine_draw_release(Render *re)
RenderEngine * RE_view_engine_get(const ViewRender *view_render)
RenderEngine * RE_engine_get(const Render *re)
bool RE_engine_draw_acquire(Render *re)
static void error(const char *str)
detail::Pass< command::DrawMultiBuf > PassMain
gpu::Batch * curves_sub_pass_setup(PassMain::Sub &ps, const Scene *scene, Object *ob, const char *&r_error, GPUMaterial *gpu_material=nullptr)
gpu::Batch * pointcloud_sub_pass_setup(PassMain::Sub &sub_ps, Object *object, GPUMaterial *gpu_material=nullptr)
Vector< SculptBatch > sculpt_batches_get(const Object *ob, SculptBatchFeature features)
gpu::Batch * DRW_cache_particles_get_hair(Object *object, ParticleSystem *psys, ModifierData *md)
gpu::Batch * DRW_cache_mesh_surface_get(Object *ob)
MatBase< float, 4, 4 > float4x4
ViewRender * RE_NewViewRender(RenderEngineType *engine_type)
Render * RE_GetSceneRender(const Scene *scene)
void RE_FreeViewRender(ViewRender *view_render)
RegionView3D * rv3d
Scene * scene
const bContext * evil_C
SpaceLink * space_data
blender::gpu::FrameBuffer * default_fb
virtual void begin_sync()=0
virtual void init()=0
char info[GPU_INFO_SIZE]
Definition DRW_render.hh:72
virtual blender::StringRefNull name_get()=0
virtual void end_sync()=0
short last_render_slot
short render_slot
ListBase particlesystem
short visibility_flag
struct ViewRender * view_render
void(* view_draw)(struct RenderEngine *engine, const struct bContext *context, struct Depsgraph *depsgraph)
Definition RE_engine.h:103
void(* view_update)(struct RenderEngine *engine, const struct bContext *context, struct Depsgraph *depsgraph)
Definition RE_engine.h:100
void(* draw)(struct RenderEngine *engine, const struct bContext *context, struct Depsgraph *depsgraph)
Definition RE_engine.h:88
struct Depsgraph * depsgraph
Definition RE_engine.h:154
RenderEngineType * type
Definition RE_engine.h:130
char text[512]
Definition RE_engine.h:139
struct Render * re
Definition RE_engine.h:137
const c_style_mat & ptr() const
static void set(DRWState state=DRW_STATE_DEFAULT)
DrawEngine * create_instance() final
int ymin
int xmin
#define N_(msgid)