Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/draw/intern/draw_manager.c')
-rw-r--r--source/blender/draw/intern/draw_manager.c2239
1 files changed, 2239 insertions, 0 deletions
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
new file mode 100644
index 00000000000..002333e5d34
--- /dev/null
+++ b/source/blender/draw/intern/draw_manager.c
@@ -0,0 +1,2239 @@
+/*
+ * Copyright 2016, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Blender Institute
+ *
+ */
+
+/** \file blender/draw/intern/draw_manager.c
+ * \ingroup draw
+ */
+
+#include <stdio.h>
+
+#include "BLI_listbase.h"
+#include "BLI_mempool.h"
+#include "BLI_rect.h"
+#include "BLI_string.h"
+#include "BLI_threads.h"
+
+#include "BLF_api.h"
+
+#include "BKE_global.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
+#include "BKE_workspace.h"
+
+#include "draw_manager.h"
+#include "DNA_camera_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "ED_space_api.h"
+#include "ED_screen.h"
+#include "ED_particle.h"
+#include "ED_view3d.h"
+
+#include "GPU_draw.h"
+#include "GPU_extensions.h"
+#include "GPU_framebuffer.h"
+#include "GPU_immediate.h"
+#include "GPU_uniformbuffer.h"
+#include "GPU_viewport.h"
+#include "GPU_matrix.h"
+
+#include "IMB_colormanagement.h"
+
+#include "RE_engine.h"
+#include "RE_pipeline.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "WM_api.h"
+#include "wm_window.h"
+
+#include "draw_manager_text.h"
+#include "draw_manager_profiling.h"
+
+/* only for callbacks */
+#include "draw_cache_impl.h"
+
+#include "draw_mode_engines.h"
+#include "engines/eevee/eevee_engine.h"
+#include "engines/basic/basic_engine.h"
+#include "engines/workbench/workbench_engine.h"
+#include "engines/external/external_engine.h"
+
+#include "../../../intern/gawain/gawain/gwn_context.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#ifdef USE_GPU_SELECT
+# include "GPU_select.h"
+#endif
+
+/** Render State: No persistent data between draw calls. */
+DRWManager DST = {NULL};
+
+ListBase DRW_engines = {NULL, NULL};
+
+extern struct GPUUniformBuffer *view_ubo; /* draw_manager_exec.c */
+
+static void drw_state_prepare_clean_for_draw(DRWManager *dst)
+{
+ memset(dst, 0x0, offsetof(DRWManager, gl_context));
+}
+
+/* This function is used to reset draw manager to a state
+ * where we don't re-use data by accident across different
+ * draw calls.
+ */
+#ifdef DEBUG
+static void drw_state_ensure_not_reused(DRWManager *dst)
+{
+ memset(dst, 0xff, offsetof(DRWManager, gl_context));
+}
+#endif
+
+/* -------------------------------------------------------------------- */
+
+void DRW_draw_callbacks_pre_scene(void)
+{
+ RegionView3D *rv3d = DST.draw_ctx.rv3d;
+
+ gpuLoadProjectionMatrix(rv3d->winmat);
+ gpuLoadMatrix(rv3d->viewmat);
+}
+
+void DRW_draw_callbacks_post_scene(void)
+{
+ RegionView3D *rv3d = DST.draw_ctx.rv3d;
+
+ gpuLoadProjectionMatrix(rv3d->winmat);
+ gpuLoadMatrix(rv3d->viewmat);
+}
+
+struct DRWTextStore *DRW_text_cache_ensure(void)
+{
+ BLI_assert(DST.text_store_p);
+ if (*DST.text_store_p == NULL) {
+ *DST.text_store_p = DRW_text_cache_create();
+ }
+ return *DST.text_store_p;
+}
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Settings
+ * \{ */
+
+bool DRW_object_is_renderable(Object *ob)
+{
+ BLI_assert(BKE_object_is_visible(ob, OB_VISIBILITY_CHECK_UNKNOWN_RENDER_MODE));
+
+ if (ob->type == OB_MESH) {
+ if ((ob == DST.draw_ctx.object_edit) || BKE_object_is_in_editmode(ob)) {
+ View3D *v3d = DST.draw_ctx.v3d;
+ const int mask = (V3D_OVERLAY_EDIT_OCCLUDE_WIRE | V3D_OVERLAY_EDIT_WEIGHT);
+
+ if (v3d && v3d->overlay.edit_flag & mask) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Return whether this object is visible depending if
+ * we are rendering or drawing in the viewport.
+ */
+bool DRW_check_object_visible_within_active_context(Object *ob)
+{
+ const eObjectVisibilityCheck mode = DRW_state_is_scene_render() ?
+ OB_VISIBILITY_CHECK_FOR_RENDER :
+ OB_VISIBILITY_CHECK_FOR_VIEWPORT;
+ return BKE_object_is_visible(ob, mode);
+}
+
+bool DRW_object_is_flat_normal(const Object *ob)
+{
+ if (ob->type == OB_MESH) {
+ const Mesh *me = ob->data;
+ if (me->mpoly && me->mpoly[0].flag & ME_SMOOTH) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DRW_check_psys_visible_within_active_context(
+ Object *object,
+ struct ParticleSystem *psys)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_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;
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Color Management
+ * \{ */
+
+/* Use color management profile to draw texture to framebuffer */
+void DRW_transform_to_display(GPUTexture *tex)
+{
+ drw_state_set(DRW_STATE_WRITE_COLOR);
+
+ Gwn_VertFormat *vert_format = immVertexFormat();
+ uint pos = GWN_vertformat_attr_add(vert_format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+ uint texco = GWN_vertformat_attr_add(vert_format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+
+ const float dither = 1.0f;
+
+ bool use_ocio = false;
+
+ /* View transform is already applied for offscreen, don't apply again, see: T52046 */
+ if (!(DST.options.is_image_render && !DST.options.is_scene_render)) {
+ Scene *scene = DST.draw_ctx.scene;
+ use_ocio = IMB_colormanagement_setup_glsl_draw_from_space(
+ &scene->view_settings, &scene->display_settings, NULL, dither, false);
+ }
+
+ if (!use_ocio) {
+ /* View transform is already applied for offscreen, don't apply again, see: T52046 */
+ if (DST.options.is_image_render && !DST.options.is_scene_render) {
+ immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
+ immUniformColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ }
+ else {
+ immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_LINEAR_TO_SRGB);
+ }
+ immUniform1i("image", 0);
+ }
+
+ GPU_texture_bind(tex, 0); /* OCIO texture bind point is 0 */
+
+ float mat[4][4];
+ unit_m4(mat);
+ immUniformMatrix4fv("ModelViewProjectionMatrix", mat);
+
+ /* Full screen triangle */
+ immBegin(GWN_PRIM_TRIS, 3);
+ immAttrib2f(texco, 0.0f, 0.0f);
+ immVertex2f(pos, -1.0f, -1.0f);
+
+ immAttrib2f(texco, 2.0f, 0.0f);
+ immVertex2f(pos, 3.0f, -1.0f);
+
+ immAttrib2f(texco, 0.0f, 2.0f);
+ immVertex2f(pos, -1.0f, 3.0f);
+ immEnd();
+
+ GPU_texture_unbind(tex);
+
+ if (use_ocio) {
+ IMB_colormanagement_finish_glsl_draw();
+ }
+ else {
+ immUnbindProgram();
+ }
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Multisample Resolve
+ * \{ */
+
+/* Use manual multisample resolve pass.
+ * Much quicker than blitting back and forth.
+ * Assume destination fb is bound*/
+void DRW_multisamples_resolve(GPUTexture *src_depth, GPUTexture *src_color)
+{
+ drw_state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_PREMUL |
+ DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL);
+
+ int samples = GPU_texture_samples(src_depth);
+
+ BLI_assert(samples > 0);
+ BLI_assert(GPU_texture_samples(src_color) == samples);
+
+ Gwn_Batch *geom = DRW_cache_fullscreen_quad_get();
+
+ int builtin;
+ switch (samples) {
+ case 2: builtin = GPU_SHADER_2D_IMAGE_MULTISAMPLE_2; break;
+ case 4: builtin = GPU_SHADER_2D_IMAGE_MULTISAMPLE_4; break;
+ case 8: builtin = GPU_SHADER_2D_IMAGE_MULTISAMPLE_8; break;
+ case 16: builtin = GPU_SHADER_2D_IMAGE_MULTISAMPLE_16; break;
+ default:
+ BLI_assert(0);
+ builtin = GPU_SHADER_2D_IMAGE_MULTISAMPLE_2;
+ break;
+ }
+
+ GWN_batch_program_set_builtin(geom, builtin);
+
+ GPU_texture_bind(src_depth, 0);
+ GPU_texture_bind(src_color, 1);
+ GWN_batch_uniform_1i(geom, "depthMulti", 0);
+ GWN_batch_uniform_1i(geom, "colorMulti", 1);
+
+ float mat[4][4];
+ unit_m4(mat);
+ GWN_batch_uniform_mat4(geom, "ModelViewProjectionMatrix", mat);
+
+ /* avoid gpuMatrix calls */
+ GWN_batch_program_use_begin(geom);
+ GWN_batch_draw_range_ex(geom, 0, 0, false);
+ GWN_batch_program_use_end(geom);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+
+/** \name Viewport (DRW_viewport)
+ * \{ */
+
+void *drw_viewport_engine_data_ensure(void *engine_type)
+{
+ void *data = GPU_viewport_engine_data_get(DST.viewport, engine_type);
+
+ if (data == NULL) {
+ data = GPU_viewport_engine_data_create(DST.viewport, engine_type);
+ }
+ return data;
+}
+
+void DRW_engine_viewport_data_size_get(
+ const void *engine_type_v,
+ int *r_fbl_len, int *r_txl_len, int *r_psl_len, int *r_stl_len)
+{
+ const DrawEngineType *engine_type = engine_type_v;
+
+ if (r_fbl_len) {
+ *r_fbl_len = engine_type->vedata_size->fbl_len;
+ }
+ if (r_txl_len) {
+ *r_txl_len = engine_type->vedata_size->txl_len;
+ }
+ if (r_psl_len) {
+ *r_psl_len = engine_type->vedata_size->psl_len;
+ }
+ if (r_stl_len) {
+ *r_stl_len = engine_type->vedata_size->stl_len;
+ }
+}
+
+const float *DRW_viewport_size_get(void)
+{
+ return DST.size;
+}
+
+const float *DRW_viewport_invert_size_get(void)
+{
+ return DST.inv_size;
+}
+
+const float *DRW_viewport_screenvecs_get(void)
+{
+ return &DST.screenvecs[0][0];
+}
+
+const float *DRW_viewport_pixelsize_get(void)
+{
+ return &DST.pixsize;
+}
+
+static void drw_viewport_cache_resize(void)
+{
+ /* Release the memiter before clearing the mempools that references them */
+ GPU_viewport_cache_release(DST.viewport);
+
+ if (DST.vmempool != NULL) {
+ BLI_mempool_clear_ex(DST.vmempool->calls, BLI_mempool_len(DST.vmempool->calls));
+ BLI_mempool_clear_ex(DST.vmempool->states, BLI_mempool_len(DST.vmempool->states));
+ BLI_mempool_clear_ex(DST.vmempool->shgroups, BLI_mempool_len(DST.vmempool->shgroups));
+ BLI_mempool_clear_ex(DST.vmempool->uniforms, BLI_mempool_len(DST.vmempool->uniforms));
+ BLI_mempool_clear_ex(DST.vmempool->passes, BLI_mempool_len(DST.vmempool->passes));
+ }
+
+ DRW_instance_data_list_free_unused(DST.idatalist);
+ DRW_instance_data_list_resize(DST.idatalist);
+}
+
+/* Not a viewport variable, we could split this out. */
+static void drw_context_state_init(void)
+{
+ if (DST.draw_ctx.obact) {
+ DST.draw_ctx.object_mode = DST.draw_ctx.obact->mode;
+ }
+ else {
+ DST.draw_ctx.object_mode = OB_MODE_OBJECT;
+ }
+
+ /* Edit object. */
+ if (DST.draw_ctx.object_mode & OB_MODE_EDIT) {
+ DST.draw_ctx.object_edit = DST.draw_ctx.obact;
+ }
+ else {
+ DST.draw_ctx.object_edit = NULL;
+ }
+
+ /* Pose object. */
+ if (DST.draw_ctx.object_mode & OB_MODE_POSE) {
+ DST.draw_ctx.object_pose = DST.draw_ctx.obact;
+ }
+ else if (DST.draw_ctx.object_mode & OB_MODE_WEIGHT_PAINT) {
+ DST.draw_ctx.object_pose = BKE_object_pose_armature_get(DST.draw_ctx.obact);
+ }
+ else {
+ DST.draw_ctx.object_pose = NULL;
+ }
+}
+
+/* It also stores viewport variable to an immutable place: DST
+ * This is because a cache uniform only store reference
+ * to its value. And we don't want to invalidate the cache
+ * if this value change per viewport */
+static void drw_viewport_var_init(void)
+{
+ RegionView3D *rv3d = DST.draw_ctx.rv3d;
+ /* Refresh DST.size */
+ if (DST.viewport) {
+ int size[2];
+ GPU_viewport_size_get(DST.viewport, size);
+ DST.size[0] = size[0];
+ DST.size[1] = size[1];
+ DST.inv_size[0] = 1.0f / size[0];
+ DST.inv_size[1] = 1.0f / size[1];
+
+ DefaultFramebufferList *fbl = (DefaultFramebufferList *)GPU_viewport_framebuffer_list_get(DST.viewport);
+ DST.default_framebuffer = fbl->default_fb;
+
+ DST.vmempool = GPU_viewport_mempool_get(DST.viewport);
+
+ if (DST.vmempool->calls == NULL) {
+ DST.vmempool->calls = BLI_mempool_create(sizeof(DRWCall), 0, 512, 0);
+ }
+ if (DST.vmempool->states == NULL) {
+ DST.vmempool->states = BLI_mempool_create(sizeof(DRWCallState), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
+ }
+ if (DST.vmempool->shgroups == NULL) {
+ DST.vmempool->shgroups = BLI_mempool_create(sizeof(DRWShadingGroup), 0, 256, 0);
+ }
+ if (DST.vmempool->uniforms == NULL) {
+ DST.vmempool->uniforms = BLI_mempool_create(sizeof(DRWUniform), 0, 512, 0);
+ }
+ if (DST.vmempool->passes == NULL) {
+ DST.vmempool->passes = BLI_mempool_create(sizeof(DRWPass), 0, 64, 0);
+ }
+
+ DST.idatalist = GPU_viewport_instance_data_list_get(DST.viewport);
+ DRW_instance_data_list_reset(DST.idatalist);
+ }
+ else {
+ DST.size[0] = 0;
+ DST.size[1] = 0;
+
+ DST.inv_size[0] = 0;
+ DST.inv_size[1] = 0;
+
+ DST.default_framebuffer = NULL;
+ DST.vmempool = NULL;
+ }
+
+ if (rv3d != NULL) {
+ /* Refresh DST.screenvecs */
+ copy_v3_v3(DST.screenvecs[0], rv3d->viewinv[0]);
+ copy_v3_v3(DST.screenvecs[1], rv3d->viewinv[1]);
+ normalize_v3(DST.screenvecs[0]);
+ normalize_v3(DST.screenvecs[1]);
+
+ /* Refresh DST.pixelsize */
+ DST.pixsize = rv3d->pixsize;
+
+ copy_m4_m4(DST.original_mat.mat[DRW_MAT_PERS], rv3d->persmat);
+ copy_m4_m4(DST.original_mat.mat[DRW_MAT_PERSINV], rv3d->persinv);
+ copy_m4_m4(DST.original_mat.mat[DRW_MAT_VIEW], rv3d->viewmat);
+ copy_m4_m4(DST.original_mat.mat[DRW_MAT_VIEWINV], rv3d->viewinv);
+ copy_m4_m4(DST.original_mat.mat[DRW_MAT_WIN], rv3d->winmat);
+ invert_m4_m4(DST.original_mat.mat[DRW_MAT_WININV], rv3d->winmat);
+
+ memcpy(DST.view_data.matstate.mat, DST.original_mat.mat, sizeof(DST.original_mat.mat));
+
+ copy_v4_v4(DST.view_data.viewcamtexcofac, rv3d->viewcamtexcofac);
+ }
+ else {
+ copy_v4_fl4(DST.view_data.viewcamtexcofac, 1.0f, 1.0f, 0.0f, 0.0f);
+ }
+
+ /* Reset facing */
+ DST.frontface = GL_CCW;
+ DST.backface = GL_CW;
+ glFrontFace(DST.frontface);
+
+ if (DST.draw_ctx.object_edit) {
+ ED_view3d_init_mats_rv3d(DST.draw_ctx.object_edit, rv3d);
+ }
+
+ /* Alloc array of texture reference. */
+ if (DST.RST.bound_texs == NULL) {
+ DST.RST.bound_texs = MEM_callocN(sizeof(GPUTexture *) * GPU_max_textures(), "Bound GPUTexture refs");
+ }
+ if (DST.RST.bound_tex_slots == NULL) {
+ DST.RST.bound_tex_slots = MEM_callocN(sizeof(char) * GPU_max_textures(), "Bound Texture Slots");
+ }
+ if (DST.RST.bound_ubos == NULL) {
+ DST.RST.bound_ubos = MEM_callocN(sizeof(GPUUniformBuffer *) * GPU_max_ubo_binds(), "Bound GPUUniformBuffer refs");
+ }
+ if (DST.RST.bound_ubo_slots == NULL) {
+ DST.RST.bound_ubo_slots = MEM_callocN(sizeof(char) * GPU_max_ubo_binds(), "Bound Ubo Slots");
+ }
+
+ if (view_ubo == NULL) {
+ view_ubo = DRW_uniformbuffer_create(sizeof(ViewUboStorage), NULL);
+ }
+
+ DST.override_mat = 0;
+ DST.dirty_mat = true;
+ DST.state_cache_id = 1;
+
+ DST.clipping.updated = false;
+
+ memset(DST.object_instance_data, 0x0, sizeof(DST.object_instance_data));
+}
+
+void DRW_viewport_matrix_get(float mat[4][4], DRWViewportMatrixType type)
+{
+ BLI_assert(type >= 0 && type < DRW_MAT_COUNT);
+ /* Can't use this in render mode. */
+ BLI_assert(((DST.override_mat & (1 << type)) != 0) || DST.draw_ctx.rv3d != NULL);
+
+ copy_m4_m4(mat, DST.view_data.matstate.mat[type]);
+}
+
+void DRW_viewport_matrix_get_all(DRWMatrixState *state)
+{
+ memcpy(state, DST.view_data.matstate.mat, sizeof(DRWMatrixState));
+}
+
+void DRW_viewport_matrix_override_set(const float mat[4][4], DRWViewportMatrixType type)
+{
+ BLI_assert(type < DRW_MAT_COUNT);
+ copy_m4_m4(DST.view_data.matstate.mat[type], mat);
+ DST.override_mat |= (1 << type);
+ DST.dirty_mat = true;
+ DST.clipping.updated = false;
+}
+
+void DRW_viewport_matrix_override_unset(DRWViewportMatrixType type)
+{
+ BLI_assert(type < DRW_MAT_COUNT);
+ copy_m4_m4(DST.view_data.matstate.mat[type], DST.original_mat.mat[type]);
+ DST.override_mat &= ~(1 << type);
+ DST.dirty_mat = true;
+ DST.clipping.updated = false;
+}
+
+void DRW_viewport_matrix_override_set_all(DRWMatrixState *state)
+{
+ memcpy(DST.view_data.matstate.mat, state, sizeof(DRWMatrixState));
+ DST.override_mat = 0xFFFFFF;
+ DST.dirty_mat = true;
+ DST.clipping.updated = false;
+}
+
+void DRW_viewport_matrix_override_unset_all(void)
+{
+ memcpy(DST.view_data.matstate.mat, DST.original_mat.mat, sizeof(DRWMatrixState));
+ DST.override_mat = 0;
+ DST.dirty_mat = true;
+ DST.clipping.updated = false;
+}
+
+bool DRW_viewport_is_persp_get(void)
+{
+ RegionView3D *rv3d = DST.draw_ctx.rv3d;
+ if (rv3d) {
+ return rv3d->is_persp;
+ }
+ else {
+ return DST.view_data.matstate.mat[DRW_MAT_WIN][3][3] == 0.0f;
+ }
+}
+
+float DRW_viewport_near_distance_get(void)
+{
+ float projmat[4][4];
+ DRW_viewport_matrix_get(projmat, DRW_MAT_WIN);
+
+ if (DRW_viewport_is_persp_get()) {
+ return -projmat[3][2] / (projmat[2][2] - 1.0f);
+ }
+ else {
+ return -(projmat[3][2] + 1.0f) / projmat[2][2];
+ }
+}
+
+float DRW_viewport_far_distance_get(void)
+{
+ float projmat[4][4];
+ DRW_viewport_matrix_get(projmat, DRW_MAT_WIN);
+
+ if (DRW_viewport_is_persp_get()) {
+ return -projmat[3][2] / (projmat[2][2] + 1.0f);
+ }
+ else {
+ return -(projmat[3][2] - 1.0f) / projmat[2][2];
+ }
+}
+
+DefaultFramebufferList *DRW_viewport_framebuffer_list_get(void)
+{
+ return GPU_viewport_framebuffer_list_get(DST.viewport);
+}
+
+DefaultTextureList *DRW_viewport_texture_list_get(void)
+{
+ return GPU_viewport_texture_list_get(DST.viewport);
+}
+
+void DRW_viewport_request_redraw(void)
+{
+ GPU_viewport_tag_update(DST.viewport);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+/** \name ViewLayers (DRW_scenelayer)
+ * \{ */
+
+void *DRW_view_layer_engine_data_get(DrawEngineType *engine_type)
+{
+ for (ViewLayerEngineData *sled = DST.draw_ctx.view_layer->drawdata.first; sled; sled = sled->next) {
+ if (sled->engine_type == engine_type) {
+ return sled->storage;
+ }
+ }
+ return NULL;
+}
+
+void **DRW_view_layer_engine_data_ensure_ex(
+ ViewLayer *view_layer, DrawEngineType *engine_type, void (*callback)(void *storage))
+{
+ ViewLayerEngineData *sled;
+
+ for (sled = view_layer->drawdata.first; sled; sled = sled->next) {
+ if (sled->engine_type == engine_type) {
+ return &sled->storage;
+ }
+ }
+
+ sled = MEM_callocN(sizeof(ViewLayerEngineData), "ViewLayerEngineData");
+ sled->engine_type = engine_type;
+ sled->free = callback;
+ BLI_addtail(&view_layer->drawdata, sled);
+
+ return &sled->storage;
+}
+
+void **DRW_view_layer_engine_data_ensure(DrawEngineType *engine_type, void (*callback)(void *storage))
+{
+ return DRW_view_layer_engine_data_ensure_ex(DST.draw_ctx.view_layer, engine_type, callback);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Objects (DRW_object)
+ * \{ */
+
+ObjectEngineData *DRW_object_engine_data_get(Object *ob, DrawEngineType *engine_type)
+{
+ for (ObjectEngineData *oed = ob->drawdata.first; oed; oed = oed->next) {
+ if (oed->engine_type == engine_type) {
+ return oed;
+ }
+ }
+ return NULL;
+}
+
+ObjectEngineData *DRW_object_engine_data_ensure(
+ Object *ob,
+ DrawEngineType *engine_type,
+ size_t size,
+ ObjectEngineDataInitCb init_cb,
+ ObjectEngineDataFreeCb free_cb)
+{
+ BLI_assert(size >= sizeof(ObjectEngineData));
+ /* Try to re-use existing data. */
+ ObjectEngineData *oed = DRW_object_engine_data_get(ob, engine_type);
+ if (oed != NULL) {
+ return oed;
+ }
+ /* Allocate new data. */
+ if ((ob->base_flag & BASE_FROMDUPLI) != 0) {
+ /* NOTE: data is not persistent in this case. It is reset each redraw. */
+ BLI_assert(free_cb == NULL); /* No callback allowed. */
+ /* Round to sizeof(float) for DRW_instance_data_request(). */
+ const size_t t = sizeof(float) - 1;
+ size = (size + t) & ~t;
+ size_t fsize = size / sizeof(float);
+ BLI_assert(fsize < MAX_INSTANCE_DATA_SIZE);
+ if (DST.object_instance_data[fsize] == NULL) {
+ DST.object_instance_data[fsize] = DRW_instance_data_request(DST.idatalist, fsize);
+ }
+ oed = (ObjectEngineData *)DRW_instance_data_next(DST.object_instance_data[fsize]);
+ memset(oed, 0, size);
+ }
+ else {
+ oed = MEM_callocN(size, "ObjectEngineData");
+ }
+ oed->engine_type = engine_type;
+ oed->free = free_cb;
+ /* Perform user-side initialization, if needed. */
+ if (init_cb != NULL) {
+ init_cb(oed);
+ }
+ /* Register in the list. */
+ BLI_addtail(&ob->drawdata, oed);
+ return oed;
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Rendering (DRW_engines)
+ * \{ */
+
+static void drw_engines_init(void)
+{
+ for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
+ DrawEngineType *engine = link->data;
+ ViewportEngineData *data = drw_viewport_engine_data_ensure(engine);
+ PROFILE_START(stime);
+
+ if (engine->engine_init) {
+ engine->engine_init(data);
+ }
+
+ PROFILE_END_UPDATE(data->init_time, stime);
+ }
+}
+
+static void drw_engines_cache_init(void)
+{
+ for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
+ DrawEngineType *engine = link->data;
+ ViewportEngineData *data = drw_viewport_engine_data_ensure(engine);
+
+ if (data->text_draw_cache) {
+ DRW_text_cache_destroy(data->text_draw_cache);
+ data->text_draw_cache = NULL;
+ }
+ if (DST.text_store_p == NULL) {
+ DST.text_store_p = &data->text_draw_cache;
+ }
+
+ if (engine->cache_init) {
+ engine->cache_init(data);
+ }
+ }
+}
+
+static void drw_engines_cache_populate(Object *ob)
+{
+ DST.ob_state = NULL;
+
+ for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
+ DrawEngineType *engine = link->data;
+ ViewportEngineData *data = drw_viewport_engine_data_ensure(engine);
+
+ if (engine->id_update) {
+ engine->id_update(data, &ob->id);
+ }
+
+ if (engine->cache_populate) {
+ engine->cache_populate(data, ob);
+ }
+ }
+}
+
+static void drw_engines_cache_finish(void)
+{
+ for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
+ DrawEngineType *engine = link->data;
+ ViewportEngineData *data = drw_viewport_engine_data_ensure(engine);
+
+ if (engine->cache_finish) {
+ engine->cache_finish(data);
+ }
+ }
+}
+
+static void drw_engines_draw_background(void)
+{
+ for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
+ DrawEngineType *engine = link->data;
+ ViewportEngineData *data = drw_viewport_engine_data_ensure(engine);
+
+ if (engine->draw_background) {
+ PROFILE_START(stime);
+
+ DRW_stats_group_start(engine->idname);
+ engine->draw_background(data);
+ DRW_stats_group_end();
+
+ PROFILE_END_UPDATE(data->background_time, stime);
+ return;
+ }
+ }
+
+ /* No draw_background found, doing default background */
+ if (DRW_state_draw_background()) {
+ DRW_draw_background();
+ }
+}
+
+static void drw_engines_draw_scene(void)
+{
+ for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
+ DrawEngineType *engine = link->data;
+ ViewportEngineData *data = drw_viewport_engine_data_ensure(engine);
+ PROFILE_START(stime);
+
+ if (engine->draw_scene) {
+ DRW_stats_group_start(engine->idname);
+ engine->draw_scene(data);
+ /* Restore for next engine */
+ if (DRW_state_is_fbo()) {
+ GPU_framebuffer_bind(DST.default_framebuffer);
+ }
+ DRW_stats_group_end();
+ }
+
+ PROFILE_END_UPDATE(data->render_time, stime);
+ }
+}
+
+static void drw_engines_draw_text(void)
+{
+ for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
+ DrawEngineType *engine = link->data;
+ ViewportEngineData *data = drw_viewport_engine_data_ensure(engine);
+ PROFILE_START(stime);
+
+ if (data->text_draw_cache) {
+ DRW_text_cache_draw(data->text_draw_cache, DST.draw_ctx.v3d, DST.draw_ctx.ar, false);
+ }
+
+ PROFILE_END_UPDATE(data->render_time, stime);
+ }
+}
+
+#define MAX_INFO_LINES 10
+
+/**
+ * Returns the offset required for the drawing of engines info.
+ */
+int DRW_draw_region_engine_info_offset(void)
+{
+ int lines = 0;
+ for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
+ DrawEngineType *engine = link->data;
+ ViewportEngineData *data = drw_viewport_engine_data_ensure(engine);
+
+ /* Count the number of lines. */
+ if (data->info[0] != '\0') {
+ lines++;
+ char *c = data->info;
+ while (*c++ != '\0') {
+ if (*c == '\n') {
+ lines++;
+ }
+ }
+ }
+ }
+ return MIN2(MAX_INFO_LINES, lines) * UI_UNIT_Y;
+}
+
+/**
+ * Actual drawing;
+ */
+void DRW_draw_region_engine_info(void)
+{
+ const char *info_array_final[MAX_INFO_LINES + 1];
+ /* This should be maxium number of engines running at the same time. */
+ char info_array[MAX_INFO_LINES][GPU_INFO_SIZE];
+ int i = 0;
+
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ ARegion *ar = draw_ctx->ar;
+ float fill_color[4] = {0.0f, 0.0f, 0.0f, 0.25f};
+
+ UI_GetThemeColor3fv(TH_HIGH_GRAD, fill_color);
+ mul_v3_fl(fill_color, fill_color[3]);
+
+ for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
+ DrawEngineType *engine = link->data;
+ ViewportEngineData *data = drw_viewport_engine_data_ensure(engine);
+
+ if (data->info[0] != '\0') {
+ char *chr_current = data->info;
+ char *chr_start = chr_current;
+ int line_len = 0;
+
+ while (*chr_current++ != '\0') {
+ line_len++;
+ if (*chr_current == '\n') {
+ BLI_strncpy(info_array[i++], chr_start, line_len + 1);
+ /* Re-start counting. */
+ chr_start = chr_current + 1;
+ line_len = -1;
+ }
+ }
+
+ BLI_strncpy(info_array[i++], chr_start, line_len + 1);
+
+ if (i >= MAX_INFO_LINES) {
+ break;
+ }
+ }
+ }
+
+ for (int j = 0; j < i; j++) {
+ info_array_final[j] = info_array[j];
+ }
+ info_array_final[i] = NULL;
+
+ if (info_array[0] != NULL) {
+ ED_region_info_draw_multiline(ar, info_array_final, fill_color, true);
+ }
+}
+
+#undef MAX_INFO_LINES
+
+static void use_drw_engine(DrawEngineType *engine)
+{
+ LinkData *ld = MEM_callocN(sizeof(LinkData), "enabled engine link data");
+ ld->data = engine;
+ BLI_addtail(&DST.enabled_engines, ld);
+}
+
+/**
+ * Use for external render engines.
+ */
+static void drw_engines_enable_external(void)
+{
+ use_drw_engine(DRW_engine_viewport_external_type.draw_engine);
+}
+
+/* TODO revisit this when proper layering is implemented */
+/* Gather all draw engines needed and store them in DST.enabled_engines
+ * That also define the rendering order of engines */
+static void drw_engines_enable_from_engine(RenderEngineType *engine_type, int drawtype, int shading_flags)
+{
+ switch (drawtype) {
+ case OB_WIRE:
+ break;
+
+ case OB_SOLID:
+ if (shading_flags & V3D_SHADING_XRAY) {
+ use_drw_engine(&draw_engine_workbench_transparent);
+ }
+ else {
+ use_drw_engine(&draw_engine_workbench_solid);
+ }
+ break;
+
+ case OB_MATERIAL:
+ case OB_RENDER:
+ default:
+ /* TODO layers */
+ if (engine_type->draw_engine != NULL) {
+ use_drw_engine(engine_type->draw_engine);
+ }
+
+ if ((engine_type->flag & RE_INTERNAL) == 0) {
+ drw_engines_enable_external();
+ }
+ break;
+ }
+}
+
+static void drw_engines_enable_from_object_mode(void)
+{
+ use_drw_engine(&draw_engine_object_type);
+ /* TODO(fclem) remove this, it does not belong to it's own engine. */
+ use_drw_engine(&draw_engine_motion_path_type);
+}
+
+static void drw_engines_enable_from_mode(int mode)
+{
+ switch (mode) {
+ case CTX_MODE_EDIT_MESH:
+ use_drw_engine(&draw_engine_edit_mesh_type);
+ break;
+ case CTX_MODE_EDIT_CURVE:
+ use_drw_engine(&draw_engine_edit_curve_type);
+ break;
+ case CTX_MODE_EDIT_SURFACE:
+ use_drw_engine(&draw_engine_edit_surface_type);
+ break;
+ case CTX_MODE_EDIT_TEXT:
+ use_drw_engine(&draw_engine_edit_text_type);
+ break;
+ case CTX_MODE_EDIT_ARMATURE:
+ use_drw_engine(&draw_engine_edit_armature_type);
+ break;
+ case CTX_MODE_EDIT_METABALL:
+ use_drw_engine(&draw_engine_edit_metaball_type);
+ break;
+ case CTX_MODE_EDIT_LATTICE:
+ use_drw_engine(&draw_engine_edit_lattice_type);
+ break;
+ case CTX_MODE_POSE:
+ use_drw_engine(&draw_engine_pose_type);
+ break;
+ case CTX_MODE_SCULPT:
+ use_drw_engine(&draw_engine_sculpt_type);
+ break;
+ case CTX_MODE_PAINT_WEIGHT:
+ use_drw_engine(&draw_engine_pose_type);
+ use_drw_engine(&draw_engine_paint_weight_type);
+ break;
+ case CTX_MODE_PAINT_VERTEX:
+ use_drw_engine(&draw_engine_paint_vertex_type);
+ break;
+ case CTX_MODE_PAINT_TEXTURE:
+ use_drw_engine(&draw_engine_paint_texture_type);
+ break;
+ case CTX_MODE_PARTICLE:
+ use_drw_engine(&draw_engine_particle_type);
+ break;
+ case CTX_MODE_OBJECT:
+ break;
+ default:
+ BLI_assert(!"Draw mode invalid");
+ break;
+ }
+}
+
+static void drw_engines_enable_from_overlays(int overlay_flag)
+{
+ if (overlay_flag) {
+ use_drw_engine(&draw_engine_overlay_type);
+ }
+}
+/**
+ * Use for select and depth-drawing.
+ */
+static void drw_engines_enable_basic(void)
+{
+ use_drw_engine(DRW_engine_viewport_basic_type.draw_engine);
+}
+
+static void drw_engines_enable(ViewLayer *view_layer, RenderEngineType *engine_type)
+{
+ Object *obact = OBACT(view_layer);
+ const int mode = CTX_data_mode_enum_ex(DST.draw_ctx.object_edit, obact, DST.draw_ctx.object_mode);
+ View3D * v3d = DST.draw_ctx.v3d;
+ const int drawtype = v3d->drawtype;
+
+ drw_engines_enable_from_engine(engine_type, drawtype, v3d->shading.flag);
+
+ if (DRW_state_draw_support()) {
+ drw_engines_enable_from_overlays(v3d->overlay.flag);
+ drw_engines_enable_from_object_mode();
+ drw_engines_enable_from_mode(mode);
+ }
+}
+
+static void drw_engines_disable(void)
+{
+ BLI_freelistN(&DST.enabled_engines);
+}
+
+static uint DRW_engines_get_hash(void)
+{
+ uint hash = 0;
+ /* The cache depends on enabled engines */
+ /* FIXME : if collision occurs ... segfault */
+ for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
+ DrawEngineType *engine = link->data;
+ hash += BLI_ghashutil_strhash_p(engine->idname);
+ }
+
+ return hash;
+}
+
+/* -------------------------------------------------------------------- */
+
+/** \name View Update
+ * \{ */
+
+void DRW_notify_view_update(const DRWUpdateContext *update_ctx)
+{
+ RenderEngineType *engine_type = update_ctx->engine_type;
+ ARegion *ar = update_ctx->ar;
+ View3D *v3d = update_ctx->v3d;
+ RegionView3D *rv3d = ar->regiondata;
+ Depsgraph *depsgraph = update_ctx->depsgraph;
+ Scene *scene = update_ctx->scene;
+ ViewLayer *view_layer = update_ctx->view_layer;
+
+ /* Separate update for each stereo view. */
+ for (int view = 0; view < 2; view++) {
+ GPUViewport *viewport = WM_draw_region_get_viewport(ar, view);
+ if (!viewport) {
+ continue;
+ }
+
+ /* XXX Really nasty locking. But else this could
+ * be executed by the material previews thread
+ * while rendering a viewport. */
+ BLI_mutex_lock(&DST.gl_context_mutex);
+
+ /* Reset before using it. */
+ drw_state_prepare_clean_for_draw(&DST);
+
+ DST.viewport = viewport;
+ DST.draw_ctx = (DRWContextState){
+ .ar = ar, .rv3d = rv3d, .v3d = v3d,
+ .scene = scene, .view_layer = view_layer, .obact = OBACT(view_layer),
+ .engine_type = engine_type,
+ .depsgraph = depsgraph, .object_mode = OB_MODE_OBJECT,
+ };
+
+ drw_engines_enable(view_layer, engine_type);
+
+ for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
+ DrawEngineType *draw_engine = link->data;
+ ViewportEngineData *data = drw_viewport_engine_data_ensure(draw_engine);
+
+ if (draw_engine->view_update) {
+ draw_engine->view_update(data);
+ }
+ }
+
+ DST.viewport = NULL;
+
+ drw_engines_disable();
+
+ BLI_mutex_unlock(&DST.gl_context_mutex);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+
+/** \name Main Draw Loops (DRW_draw)
+ * \{ */
+
+/* Everything starts here.
+ * This function takes care of calling all cache and rendering functions
+ * for each relevant engine / mode engine. */
+void DRW_draw_view(const bContext *C)
+{
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ ARegion *ar = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ RenderEngineType *engine_type = ED_view3d_engine_type(scene, v3d->drawtype);
+ GPUViewport *viewport = WM_draw_region_get_bound_viewport(ar);
+
+ /* Reset before using it. */
+ drw_state_prepare_clean_for_draw(&DST);
+ DST.options.draw_text = (
+ (v3d->flag2 & V3D_RENDER_OVERRIDE) == 0 &&
+ (v3d->overlay.flag & V3D_OVERLAY_HIDE_TEXT) != 0);
+ DRW_draw_render_loop_ex(depsgraph, engine_type, ar, v3d, viewport, C);
+}
+
+/**
+ * Used for both regular and off-screen drawing.
+ * Need to reset DST before calling this function
+ */
+void DRW_draw_render_loop_ex(
+ struct Depsgraph *depsgraph,
+ RenderEngineType *engine_type,
+ ARegion *ar, View3D *v3d,
+ GPUViewport *viewport,
+ const bContext *evil_C)
+{
+
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
+ RegionView3D *rv3d = ar->regiondata;
+
+ DST.draw_ctx.evil_C = evil_C;
+
+ DST.viewport = viewport;
+ v3d->zbuf = true;
+
+ /* Setup viewport */
+ GPU_viewport_engines_data_validate(DST.viewport, DRW_engines_get_hash());
+
+ DST.draw_ctx = (DRWContextState){
+ .ar = ar, .rv3d = rv3d, .v3d = v3d,
+ .scene = scene, .view_layer = view_layer, .obact = OBACT(view_layer),
+ .engine_type = engine_type,
+ .depsgraph = depsgraph,
+
+ /* reuse if caller sets */
+ .evil_C = DST.draw_ctx.evil_C,
+ };
+ drw_context_state_init();
+ drw_viewport_var_init();
+
+ /* Get list of enabled engines */
+ drw_engines_enable(view_layer, engine_type);
+
+ /* Update ubos */
+ DRW_globals_update();
+
+ drw_debug_init();
+ DRW_hair_init();
+
+ /* No framebuffer allowed before drawing. */
+ BLI_assert(GPU_framebuffer_current_get() == 0);
+
+ /* Init engines */
+ drw_engines_init();
+
+ /* Cache filling */
+ {
+ PROFILE_START(stime);
+ drw_engines_cache_init();
+
+ DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN(depsgraph, ob)
+ {
+ drw_engines_cache_populate(ob);
+ }
+ DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END;
+
+ drw_engines_cache_finish();
+
+ DRW_render_instance_buffer_finish();
+
+#ifdef USE_PROFILE
+ double *cache_time = GPU_viewport_cache_time_get(DST.viewport);
+ PROFILE_END_UPDATE(*cache_time, stime);
+#endif
+ }
+
+ DRW_stats_begin();
+
+ GPU_framebuffer_bind(DST.default_framebuffer);
+
+ /* Start Drawing */
+ DRW_state_reset();
+
+ DRW_hair_update();
+
+ drw_engines_draw_background();
+
+ /* WIP, single image drawn over the camera view (replace) */
+ bool do_bg_image = false;
+ if (rv3d->persp == RV3D_CAMOB) {
+ Object *cam_ob = v3d->camera;
+ if (cam_ob && cam_ob->type == OB_CAMERA) {
+ Camera *cam = cam_ob->data;
+ if (!BLI_listbase_is_empty(&cam->bg_images)) {
+ do_bg_image = true;
+ }
+ }
+ }
+
+ if (do_bg_image) {
+ ED_view3d_draw_bgpic_test(scene, depsgraph, ar, v3d, false, true);
+ }
+
+
+ DRW_draw_callbacks_pre_scene();
+ if (DST.draw_ctx.evil_C) {
+ ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.ar, REGION_DRAW_PRE_VIEW);
+ }
+
+ drw_engines_draw_scene();
+
+ DRW_draw_callbacks_post_scene();
+ if (DST.draw_ctx.evil_C) {
+ ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.ar, REGION_DRAW_POST_VIEW);
+ }
+
+ DRW_state_reset();
+
+ drw_debug_draw();
+
+ glDisable(GL_DEPTH_TEST);
+ drw_engines_draw_text();
+ glEnable(GL_DEPTH_TEST);
+
+ if (DST.draw_ctx.evil_C) {
+ /* needed so manipulator isn't obscured */
+ if (((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) &&
+ ((v3d->twflag & V3D_MANIPULATOR_DRAW) != 0))
+ {
+ glDisable(GL_DEPTH_TEST);
+ DRW_draw_manipulator_3d();
+ }
+
+ DRW_draw_region_info();
+
+ if ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 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. */
+ DRW_draw_manipulator_2d();
+ glEnable(GL_DEPTH_TEST);
+ }
+ }
+
+ DRW_stats_reset();
+
+ if (do_bg_image) {
+ ED_view3d_draw_bgpic_test(scene, depsgraph, ar, v3d, true, true);
+ }
+
+ if (G.debug_value > 20) {
+ glDisable(GL_DEPTH_TEST);
+ rcti rect; /* local coordinate visible rect inside region, to accomodate overlapping ui */
+ ED_region_visible_rect(DST.draw_ctx.ar, &rect);
+ DRW_stats_draw(&rect);
+ glEnable(GL_DEPTH_TEST);
+ }
+
+ if (WM_draw_region_get_bound_viewport(ar)) {
+ /* Don't unbind the framebuffer 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();
+ }
+
+ DRW_state_reset();
+ drw_engines_disable();
+
+ drw_viewport_cache_resize();
+
+#ifdef DEBUG
+ /* Avoid accidental reuse. */
+ drw_state_ensure_not_reused(&DST);
+#endif
+}
+
+void DRW_draw_render_loop(
+ struct Depsgraph *depsgraph,
+ ARegion *ar, View3D *v3d,
+ GPUViewport *viewport)
+{
+ /* Reset before using it. */
+ drw_state_prepare_clean_for_draw(&DST);
+
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ RenderEngineType *engine_type = ED_view3d_engine_type(scene, v3d->drawtype);
+
+ DRW_draw_render_loop_ex(depsgraph, engine_type, ar, v3d, viewport, NULL);
+}
+
+/* @viewport CAN be NULL, in this case we create one. */
+void DRW_draw_render_loop_offscreen(
+ struct Depsgraph *depsgraph, RenderEngineType *engine_type,
+ ARegion *ar, View3D *v3d,
+ const bool draw_background, GPUOffScreen *ofs,
+ GPUViewport *viewport)
+{
+ /* Create temporary viewport if needed. */
+ GPUViewport *render_viewport = viewport;
+ if (viewport == NULL) {
+ render_viewport = GPU_viewport_create_from_offscreen(ofs);
+ }
+
+ GPU_framebuffer_restore();
+
+ /* Reset before using it. */
+ drw_state_prepare_clean_for_draw(&DST);
+ DST.options.is_image_render = true;
+ DST.options.draw_background = draw_background;
+ DRW_draw_render_loop_ex(depsgraph, engine_type, ar, v3d, render_viewport, NULL);
+
+ /* Free temporary viewport. */
+ if (viewport == NULL) {
+ /* don't free data owned by 'ofs' */
+ GPU_viewport_clear_from_offscreen(render_viewport);
+ GPU_viewport_free(render_viewport);
+ }
+
+ /* we need to re-bind (annoying!) */
+ GPU_offscreen_bind(ofs, false);
+}
+
+void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph)
+{
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
+ RenderEngineType *engine_type = engine->type;
+ DrawEngineType *draw_engine_type = engine_type->draw_engine;
+ RenderData *r = &scene->r;
+ Render *render = engine->re;
+
+ if (G.background && DST.gl_context == NULL) {
+ WM_init_opengl(G_MAIN);
+ }
+
+ void *re_gl_context = RE_gl_context_get(render);
+ void *re_gwn_context = NULL;
+
+ /* Changing Context */
+ if (re_gl_context != NULL) {
+ /* TODO get rid of the blocking. Only here because of the static global DST. */
+ BLI_mutex_lock(&DST.gl_context_mutex);
+ WM_opengl_context_activate(re_gl_context);
+ re_gwn_context = RE_gwn_context_get(render);
+ if (GWN_context_active_get() == NULL) {
+ GWN_context_active_set(re_gwn_context);
+ }
+ DRW_shape_cache_reset(); /* XXX fix that too. */
+ }
+ else {
+ DRW_opengl_context_enable();
+ }
+
+ /* IMPORTANT: We dont support immediate mode in render mode!
+ * This shall remain in effect until immediate mode supports
+ * multiple threads. */
+
+ /* Reset before using it. */
+ drw_state_prepare_clean_for_draw(&DST);
+ DST.options.is_image_render = true;
+ DST.options.is_scene_render = true;
+ DST.options.draw_background = scene->r.alphamode == R_ADDSKY;
+
+ DST.draw_ctx = (DRWContextState){
+ .scene = scene, .view_layer = view_layer,
+ .engine_type = engine_type,
+ .depsgraph = depsgraph, .object_mode = OB_MODE_OBJECT,
+ };
+ drw_context_state_init();
+
+ DST.viewport = GPU_viewport_create();
+ const int size[2] = {(r->size * r->xsch) / 100, (r->size * r->ysch) / 100};
+ GPU_viewport_size_set(DST.viewport, size);
+
+ drw_viewport_var_init();
+
+ ViewportEngineData *data = drw_viewport_engine_data_ensure(draw_engine_type);
+
+ /* set default viewport */
+ glViewport(0, 0, size[0], size[1]);
+
+ /* 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, size[0], 0, size[1]);
+ }
+
+ /* Init render result. */
+ RenderResult *render_result = RE_engine_begin_result(
+ engine,
+ 0,
+ 0,
+ (int)size[0],
+ (int)size[1],
+ view_layer->name,
+ /* RR_ALL_VIEWS */ NULL);
+
+ RenderLayer *render_layer = render_result->layers.first;
+ for (RenderView *render_view = render_result->views.first;
+ render_view != NULL;
+ render_view = render_view->next)
+ {
+ RE_SetActiveRenderView(render, render_view->name);
+ engine_type->draw_engine->render_to_image(data, engine, render_layer, &render_rect);
+ DST.buffer_finish_called = false;
+ }
+
+ RE_engine_end_result(engine, render_result, false, false, false);
+
+ /* Force cache to reset. */
+ drw_viewport_cache_resize();
+
+ /* TODO grease pencil */
+
+ GPU_viewport_free(DST.viewport);
+ GPU_framebuffer_restore();
+
+#ifdef DEBUG
+ /* Avoid accidental reuse. */
+ drw_state_ensure_not_reused(&DST);
+#endif
+
+ /* Changing Context */
+ if (re_gl_context != NULL) {
+ DRW_shape_cache_reset(); /* XXX fix that too. */
+ glFlush();
+ GWN_context_active_set(NULL);
+ WM_opengl_context_release(re_gl_context);
+ /* TODO get rid of the blocking. */
+ BLI_mutex_unlock(&DST.gl_context_mutex);
+ }
+ else {
+ DRW_opengl_context_disable();
+ }
+}
+
+void DRW_render_object_iter(
+ void *vedata, RenderEngine *engine, struct Depsgraph *depsgraph,
+ void (*callback)(void *vedata, Object *ob, RenderEngine *engine, struct Depsgraph *depsgraph))
+{
+ DRW_hair_init();
+
+ DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN(depsgraph, ob)
+ {
+ DST.ob_state = NULL;
+ callback(vedata, ob, engine, depsgraph);
+ }
+ DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END
+}
+
+static struct DRWSelectBuffer {
+ struct GPUFrameBuffer *framebuffer;
+ struct GPUTexture *texture_depth;
+} g_select_buffer = {NULL};
+
+static void draw_select_framebuffer_setup(const rcti *rect)
+{
+ if (g_select_buffer.framebuffer == NULL) {
+ g_select_buffer.framebuffer = GPU_framebuffer_create();
+ }
+
+ /* If size mismatch recreate the texture. */
+ if ((g_select_buffer.texture_depth != NULL) &&
+ ((GPU_texture_width(g_select_buffer.texture_depth) != BLI_rcti_size_x(rect)) ||
+ (GPU_texture_height(g_select_buffer.texture_depth) != BLI_rcti_size_y(rect))))
+ {
+ GPU_texture_free(g_select_buffer.texture_depth);
+ g_select_buffer.texture_depth = NULL;
+ }
+
+ if (g_select_buffer.texture_depth == NULL) {
+ g_select_buffer.texture_depth = GPU_texture_create_2D(
+ BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), GPU_DEPTH_COMPONENT24, NULL, NULL);
+
+ GPU_framebuffer_texture_attach(g_select_buffer.framebuffer, g_select_buffer.texture_depth, 0, 0);
+
+ if (!GPU_framebuffer_check_valid(g_select_buffer.framebuffer, NULL)) {
+ printf("Error invalid selection framebuffer\n");
+ }
+ }
+}
+
+/* Must run after all instance datas have been added. */
+void DRW_render_instance_buffer_finish(void)
+{
+ BLI_assert(!DST.buffer_finish_called && "DRW_render_instance_buffer_finish called twice!");
+ DST.buffer_finish_called = true;
+ DRW_instance_buffer_finish(DST.idatalist);
+}
+
+/**
+ * object mode select-loop, see: ED_view3d_draw_select_loop (legacy drawing).
+ */
+void DRW_draw_select_loop(
+ struct Depsgraph *depsgraph,
+ ARegion *ar, View3D *v3d,
+ bool UNUSED(use_obedit_skip), bool UNUSED(use_nearest), const rcti *rect,
+ DRW_SelectPassFn select_pass_fn, void *select_pass_user_data,
+ DRW_ObjectFilterFn object_filter_fn, void *object_filter_user_data)
+{
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ RenderEngineType *engine_type = ED_view3d_engine_type(scene, v3d->drawtype);
+ ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
+ Object *obact = OBACT(view_layer);
+ Object *obedit = OBEDIT_FROM_OBACT(obact);
+#ifndef USE_GPU_SELECT
+ UNUSED_VARS(vc, scene, view_layer, v3d, ar, rect);
+#else
+ RegionView3D *rv3d = ar->regiondata;
+
+ /* Reset before using it. */
+ drw_state_prepare_clean_for_draw(&DST);
+
+ bool use_obedit = false;
+ int obedit_mode = 0;
+ if (obedit != NULL) {
+ if (obedit->type == OB_MBALL) {
+ use_obedit = true;
+ obedit_mode = CTX_MODE_EDIT_METABALL;
+ }
+ else if (obedit->type == OB_ARMATURE) {
+ use_obedit = true;
+ obedit_mode = CTX_MODE_EDIT_ARMATURE;
+ }
+ }
+ if (v3d->overlay.flag & V3D_OVERLAY_BONE_SELECTION) {
+ if (!(v3d->flag2 & V3D_RENDER_OVERRIDE)) {
+ Object *obpose = OBPOSE_FROM_OBACT(obact);
+ if (obpose) {
+ use_obedit = true;
+ obedit_mode = CTX_MODE_POSE;
+ }
+ }
+ }
+
+ struct GPUViewport *viewport = GPU_viewport_create();
+ GPU_viewport_size_set(viewport, (const int[2]){BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)});
+
+ DST.viewport = viewport;
+ v3d->zbuf = true;
+
+ DST.options.is_select = true;
+
+ /* Get list of enabled engines */
+ if (use_obedit) {
+ drw_engines_enable_from_mode(obedit_mode);
+ }
+ else {
+ drw_engines_enable_basic();
+ drw_engines_enable_from_object_mode();
+ }
+
+ /* Setup viewport */
+
+ /* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */
+ DST.draw_ctx = (DRWContextState){
+ .ar = ar, .rv3d = rv3d, .v3d = v3d,
+ .scene = scene, .view_layer = view_layer, .obact = obact,
+ .engine_type = engine_type,
+ .depsgraph = depsgraph,
+ };
+ drw_context_state_init();
+ drw_viewport_var_init();
+
+ /* Update ubos */
+ DRW_globals_update();
+
+ /* Init engines */
+ drw_engines_init();
+ DRW_hair_init();
+
+ {
+ drw_engines_cache_init();
+
+ if (use_obedit) {
+#if 0
+ drw_engines_cache_populate(obact);
+#else
+ FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, obact->mode, ob_iter) {
+ drw_engines_cache_populate(ob_iter);
+ }
+ FOREACH_OBJECT_IN_MODE_END;
+#endif
+ }
+ else {
+ bool filter_exclude = false;
+ DEG_OBJECT_ITER_BEGIN(
+ depsgraph, ob,
+ DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY |
+ DEG_ITER_OBJECT_FLAG_VISIBLE |
+ DEG_ITER_OBJECT_FLAG_DUPLI)
+ {
+ if ((ob->base_flag & BASE_SELECTABLED) != 0) {
+
+ if (object_filter_fn != NULL) {
+ if (ob->base_flag & BASE_FROMDUPLI) {
+ /* pass (use previous filter_exclude value) */
+ }
+ else {
+ filter_exclude = (object_filter_fn(ob, object_filter_user_data) == false);
+ }
+ if (filter_exclude) {
+ continue;
+ }
+ }
+
+ /* This relies on dupli instances being after their instancing object. */
+ if ((ob->base_flag & BASE_FROMDUPLI) == 0) {
+ Object *ob_orig = DEG_get_original_object(ob);
+ DRW_select_load_id(ob_orig->select_color);
+ }
+ drw_engines_cache_populate(ob);
+ }
+ }
+ DEG_OBJECT_ITER_END;
+ }
+
+ drw_engines_cache_finish();
+
+ DRW_render_instance_buffer_finish();
+ }
+
+ /* Setup framebuffer */
+ draw_select_framebuffer_setup(rect);
+ GPU_framebuffer_bind(g_select_buffer.framebuffer);
+ GPU_framebuffer_clear_depth(g_select_buffer.framebuffer, 1.0f);
+
+ /* Start Drawing */
+ DRW_state_reset();
+ DRW_draw_callbacks_pre_scene();
+
+ DRW_hair_update();
+
+ DRW_state_lock(
+ DRW_STATE_WRITE_DEPTH |
+ DRW_STATE_DEPTH_ALWAYS |
+ DRW_STATE_DEPTH_LESS_EQUAL |
+ DRW_STATE_DEPTH_EQUAL |
+ DRW_STATE_DEPTH_GREATER |
+ DRW_STATE_DEPTH_ALWAYS);
+
+ /* Only 1-2 passes. */
+ while (true) {
+ if (!select_pass_fn(DRW_SELECT_PASS_PRE, select_pass_user_data)) {
+ break;
+ }
+
+ drw_engines_draw_scene();
+
+ if (!select_pass_fn(DRW_SELECT_PASS_POST, select_pass_user_data)) {
+ break;
+ }
+ }
+
+ DRW_state_lock(0);
+
+ DRW_draw_callbacks_post_scene();
+
+ DRW_state_reset();
+ drw_engines_disable();
+
+#ifdef DEBUG
+ /* Avoid accidental reuse. */
+ drw_state_ensure_not_reused(&DST);
+#endif
+ GPU_framebuffer_restore();
+
+ /* Cleanup for selection state */
+ GPU_viewport_free(viewport);
+#endif /* USE_GPU_SELECT */
+}
+
+static void draw_depth_texture_to_screen(GPUTexture *texture)
+{
+ const float w = (float)GPU_texture_width(texture);
+ const float h = (float)GPU_texture_height(texture);
+
+ Gwn_VertFormat *format = immVertexFormat();
+ uint texcoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+ uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_DEPTH_COPY);
+
+ GPU_texture_bind(texture, 0);
+
+ immUniform1i("image", 0); /* default GL_TEXTURE0 unit */
+
+ immBegin(GWN_PRIM_TRI_STRIP, 4);
+
+ immAttrib2f(texcoord, 0.0f, 0.0f);
+ immVertex2f(pos, 0.0f, 0.0f);
+
+ immAttrib2f(texcoord, 1.0f, 0.0f);
+ immVertex2f(pos, w, 0.0f);
+
+ immAttrib2f(texcoord, 0.0f, 1.0f);
+ immVertex2f(pos, 0.0f, h);
+
+ immAttrib2f(texcoord, 1.0f, 1.0f);
+ immVertex2f(pos, w, h);
+
+ immEnd();
+
+ GPU_texture_unbind(texture);
+
+ immUnbindProgram();
+}
+
+/**
+ * object mode select-loop, see: ED_view3d_draw_depth_loop (legacy drawing).
+ */
+void DRW_draw_depth_loop(
+ Depsgraph *depsgraph,
+ ARegion *ar, View3D *v3d)
+{
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ RenderEngineType *engine_type = ED_view3d_engine_type(scene, v3d->drawtype);
+ ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
+ RegionView3D *rv3d = ar->regiondata;
+
+ DRW_opengl_context_enable();
+
+ /* Reset before using it. */
+ drw_state_prepare_clean_for_draw(&DST);
+
+ struct GPUViewport *viewport = GPU_viewport_create();
+ GPU_viewport_size_set(viewport, (const int[2]){ar->winx, ar->winy});
+
+ /* Setup framebuffer */
+ draw_select_framebuffer_setup(&ar->winrct);
+ GPU_framebuffer_bind(g_select_buffer.framebuffer);
+ GPU_framebuffer_clear_depth(g_select_buffer.framebuffer, 1.0f);
+
+ bool cache_is_dirty;
+ DST.viewport = viewport;
+ v3d->zbuf = true;
+
+ DST.options.is_depth = true;
+
+ /* Get list of enabled engines */
+ {
+ drw_engines_enable_basic();
+ drw_engines_enable_from_object_mode();
+ }
+
+ /* Setup viewport */
+ cache_is_dirty = true;
+
+ /* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */
+ DST.draw_ctx = (DRWContextState){
+ .ar = ar, .rv3d = rv3d, .v3d = v3d,
+ .scene = scene, .view_layer = view_layer, .obact = OBACT(view_layer),
+ .engine_type = engine_type,
+ .depsgraph = depsgraph,
+ };
+ drw_context_state_init();
+ drw_viewport_var_init();
+
+ /* Update ubos */
+ DRW_globals_update();
+
+ /* Init engines */
+ drw_engines_init();
+ DRW_hair_init();
+
+ /* TODO : tag to refresh by the dependency graph */
+ /* ideally only refresh when objects are added/removed */
+ /* or render properties / materials change */
+ if (cache_is_dirty) {
+ drw_engines_cache_init();
+
+ DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN(depsgraph, ob)
+ {
+ drw_engines_cache_populate(ob);
+ }
+ DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END;
+
+ drw_engines_cache_finish();
+
+ DRW_render_instance_buffer_finish();
+ }
+
+ /* Start Drawing */
+ DRW_state_reset();
+
+ DRW_hair_update();
+
+ DRW_draw_callbacks_pre_scene();
+ drw_engines_draw_scene();
+ DRW_draw_callbacks_post_scene();
+
+ DRW_state_reset();
+ drw_engines_disable();
+
+#ifdef DEBUG
+ /* Avoid accidental reuse. */
+ drw_state_ensure_not_reused(&DST);
+#endif
+
+ /* TODO: Reading depth for operators should be done here. */
+
+ GPU_framebuffer_restore();
+
+ /* Cleanup for selection state */
+ GPU_viewport_free(viewport);
+
+ /* Changin context */
+ DRW_opengl_context_disable();
+
+ /* XXX Drawing the resulting buffer to the BACK_BUFFER */
+ gpuPushMatrix();
+ gpuPushProjectionMatrix();
+ wmOrtho2_region_pixelspace(ar);
+ gpuLoadIdentity();
+
+ glEnable(GL_DEPTH_TEST); /* Cannot write to depth buffer without testing */
+ glDepthFunc(GL_ALWAYS);
+ draw_depth_texture_to_screen(g_select_buffer.texture_depth);
+ glDepthFunc(GL_LEQUAL);
+
+ gpuPopMatrix();
+ gpuPopProjectionMatrix();
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Draw Manager State (DRW_state)
+ * \{ */
+
+void DRW_state_dfdy_factors_get(float dfdyfac[2])
+{
+ GPU_get_dfdy_factors(dfdyfac);
+}
+
+/**
+ * When false, drawing doesn't output to a pixel buffer
+ * eg: Occlusion queries, or when we have setup a context to draw in already.
+ */
+bool DRW_state_is_fbo(void)
+{
+ return ((DST.default_framebuffer != NULL) || DST.options.is_image_render);
+}
+
+/**
+ * For when engines need to know if this is drawing for selection or not.
+ */
+bool DRW_state_is_select(void)
+{
+ return DST.options.is_select;
+}
+
+bool DRW_state_is_depth(void)
+{
+ return DST.options.is_depth;
+}
+
+/**
+ * Whether we are rendering for an image
+ */
+bool DRW_state_is_image_render(void)
+{
+ return DST.options.is_image_render;
+}
+
+/**
+ * Whether we are rendering only the render engine,
+ * or if we should also render the mode engines.
+ */
+bool DRW_state_is_scene_render(void)
+{
+ BLI_assert(DST.options.is_scene_render ?
+ DST.options.is_image_render : true);
+ return DST.options.is_scene_render;
+}
+
+/**
+* Whether we are rendering simple opengl render
+*/
+bool DRW_state_is_opengl_render(void)
+{
+ return DST.options.is_image_render && !DST.options.is_scene_render;
+}
+
+/**
+ * Should text draw in this mode?
+ */
+bool DRW_state_show_text(void)
+{
+ return (DST.options.is_select) == 0 &&
+ (DST.options.is_depth) == 0 &&
+ (DST.options.is_scene_render) == 0 &&
+ (DST.options.draw_text) == 0;
+}
+
+/**
+ * Should draw support elements
+ * Objects center, selection outline, probe data, ...
+ */
+bool DRW_state_draw_support(void)
+{
+ View3D *v3d = DST.draw_ctx.v3d;
+ return (DRW_state_is_scene_render() == false) &&
+ (v3d != NULL) &&
+ ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0);
+}
+
+/**
+ * Whether we should render the background
+ */
+bool DRW_state_draw_background(void)
+{
+ if (DRW_state_is_image_render() == false) {
+ return true;
+ }
+ return DST.options.draw_background;
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Context State (DRW_context_state)
+ * \{ */
+
+const DRWContextState *DRW_context_state_get(void)
+{
+ return &DST.draw_ctx;
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Init/Exit (DRW_engines)
+ * \{ */
+
+bool DRW_engine_render_support(DrawEngineType *draw_engine_type)
+{
+ return draw_engine_type->render_to_image;
+}
+
+void DRW_engine_register(DrawEngineType *draw_engine_type)
+{
+ BLI_addtail(&DRW_engines, draw_engine_type);
+}
+
+void DRW_engines_register(void)
+{
+ RE_engines_register(&DRW_engine_viewport_eevee_type);
+ RE_engines_register(&DRW_engine_viewport_workbench_type);
+
+ DRW_engine_register(&draw_engine_workbench_solid);
+ DRW_engine_register(&draw_engine_workbench_transparent);
+
+ DRW_engine_register(&draw_engine_object_type);
+ DRW_engine_register(&draw_engine_edit_armature_type);
+ DRW_engine_register(&draw_engine_edit_curve_type);
+ DRW_engine_register(&draw_engine_edit_lattice_type);
+ DRW_engine_register(&draw_engine_edit_mesh_type);
+ DRW_engine_register(&draw_engine_edit_metaball_type);
+ DRW_engine_register(&draw_engine_edit_surface_type);
+ DRW_engine_register(&draw_engine_edit_text_type);
+ DRW_engine_register(&draw_engine_motion_path_type);
+ DRW_engine_register(&draw_engine_overlay_type);
+ DRW_engine_register(&draw_engine_paint_texture_type);
+ DRW_engine_register(&draw_engine_paint_vertex_type);
+ DRW_engine_register(&draw_engine_paint_weight_type);
+ DRW_engine_register(&draw_engine_particle_type);
+ DRW_engine_register(&draw_engine_pose_type);
+ DRW_engine_register(&draw_engine_sculpt_type);
+
+ /* setup callbacks */
+ {
+ /* BKE: mball.c */
+ extern void *BKE_mball_batch_cache_dirty_cb;
+ extern void *BKE_mball_batch_cache_free_cb;
+ /* BKE: curve.c */
+ extern void *BKE_curve_batch_cache_dirty_cb;
+ extern void *BKE_curve_batch_cache_free_cb;
+ /* BKE: mesh.c */
+ extern void *BKE_mesh_batch_cache_dirty_cb;
+ extern void *BKE_mesh_batch_cache_free_cb;
+ /* BKE: lattice.c */
+ extern void *BKE_lattice_batch_cache_dirty_cb;
+ extern void *BKE_lattice_batch_cache_free_cb;
+ /* BKE: particle.c */
+ extern void *BKE_particle_batch_cache_dirty_cb;
+ extern void *BKE_particle_batch_cache_free_cb;
+
+ BKE_mball_batch_cache_dirty_cb = DRW_mball_batch_cache_dirty;
+ BKE_mball_batch_cache_free_cb = DRW_mball_batch_cache_free;
+
+ BKE_curve_batch_cache_dirty_cb = DRW_curve_batch_cache_dirty;
+ BKE_curve_batch_cache_free_cb = DRW_curve_batch_cache_free;
+
+ BKE_mesh_batch_cache_dirty_cb = DRW_mesh_batch_cache_dirty;
+ BKE_mesh_batch_cache_free_cb = DRW_mesh_batch_cache_free;
+
+ BKE_lattice_batch_cache_dirty_cb = DRW_lattice_batch_cache_dirty;
+ BKE_lattice_batch_cache_free_cb = DRW_lattice_batch_cache_free;
+
+ BKE_particle_batch_cache_dirty_cb = DRW_particle_batch_cache_dirty;
+ BKE_particle_batch_cache_free_cb = DRW_particle_batch_cache_free;
+ }
+}
+
+extern struct Gwn_VertFormat *g_pos_format; /* draw_shgroup.c */
+extern struct GPUUniformBuffer *globals_ubo; /* draw_common.c */
+extern struct GPUTexture *globals_ramp; /* draw_common.c */
+void DRW_engines_free(void)
+{
+ DRW_opengl_context_enable();
+
+ DRW_TEXTURE_FREE_SAFE(g_select_buffer.texture_depth);
+ GPU_FRAMEBUFFER_FREE_SAFE(g_select_buffer.framebuffer);
+
+ DRW_hair_free();
+ DRW_shape_cache_free();
+ DRW_stats_free();
+ DRW_globals_free();
+
+ DrawEngineType *next;
+ for (DrawEngineType *type = DRW_engines.first; type; type = next) {
+ next = type->next;
+ BLI_remlink(&R_engines, type);
+
+ if (type->engine_free) {
+ type->engine_free();
+ }
+ }
+
+ DRW_UBO_FREE_SAFE(globals_ubo);
+ DRW_UBO_FREE_SAFE(view_ubo);
+ DRW_TEXTURE_FREE_SAFE(globals_ramp);
+ MEM_SAFE_FREE(g_pos_format);
+
+ MEM_SAFE_FREE(DST.RST.bound_texs);
+ MEM_SAFE_FREE(DST.RST.bound_tex_slots);
+ MEM_SAFE_FREE(DST.RST.bound_ubos);
+ MEM_SAFE_FREE(DST.RST.bound_ubo_slots);
+
+ DRW_opengl_context_disable();
+}
+
+/** \} */
+
+/** \name Init/Exit (DRW_opengl_ctx)
+ * \{ */
+
+void DRW_opengl_context_create(void)
+{
+ BLI_assert(DST.gl_context == NULL); /* Ensure it's called once */
+
+ BLI_mutex_init(&DST.gl_context_mutex);
+ if (!G.background) {
+ immDeactivate();
+ }
+ /* This changes the active context. */
+ DST.gl_context = WM_opengl_context_create();
+ /* Make the context active for this thread (main thread) */
+ WM_opengl_context_activate(DST.gl_context);
+ /* Be sure to create gawain.context too. */
+ DST.gwn_context = GWN_context_create();
+ if (!G.background) {
+ immActivate();
+ }
+ /* Set default Blender OpenGL state */
+ GPU_state_init();
+ /* So we activate the window's one afterwards. */
+ wm_window_reset_drawable();
+}
+
+void DRW_opengl_context_destroy(void)
+{
+ BLI_assert(BLI_thread_is_main());
+ if (DST.gl_context != NULL) {
+ WM_opengl_context_activate(DST.gl_context);
+ GWN_context_active_set(DST.gwn_context);
+ GWN_context_discard(DST.gwn_context);
+ WM_opengl_context_dispose(DST.gl_context);
+ BLI_mutex_end(&DST.gl_context_mutex);
+ }
+}
+
+void DRW_opengl_context_enable(void)
+{
+ if (DST.gl_context != NULL) {
+ /* IMPORTANT: We dont support immediate mode in render mode!
+ * This shall remain in effect until immediate mode supports
+ * multiple threads. */
+ BLI_mutex_lock(&DST.gl_context_mutex);
+ if (BLI_thread_is_main()) {
+ if (!G.background) {
+ immDeactivate();
+ }
+ }
+ WM_opengl_context_activate(DST.gl_context);
+ GWN_context_active_set(DST.gwn_context);
+ if (BLI_thread_is_main()) {
+ if (!G.background) {
+ immActivate();
+ }
+ BLF_batch_reset();
+ }
+ }
+}
+
+void DRW_opengl_context_disable(void)
+{
+ if (DST.gl_context != NULL) {
+#ifdef __APPLE__
+ /* Need to flush before disabling draw context, otherwise it does not
+ * always finish drawing and viewport can be empty or partially drawn */
+ glFlush();
+#endif
+
+ if (BLI_thread_is_main()) {
+ wm_window_reset_drawable();
+ }
+ else {
+ WM_opengl_context_release(DST.gl_context);
+ GWN_context_active_set(NULL);
+ }
+
+ BLI_mutex_unlock(&DST.gl_context_mutex);
+ }
+}
+
+/** \} */