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.c4102
1 files changed, 4102 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..e6064c202ae
--- /dev/null
+++ b/source/blender/draw/intern/draw_manager.c
@@ -0,0 +1,4102 @@
+/*
+ * 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_string_utils.h"
+
+#include "BIF_glutil.h"
+
+#include "BKE_curve.h"
+#include "BKE_global.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+#include "BKE_pbvh.h"
+#include "BKE_paint.h"
+#include "BKE_workspace.h"
+
+#include "BLT_translation.h"
+#include "BLF_api.h"
+
+#include "DRW_engine.h"
+#include "DRW_render.h"
+
+#include "DNA_camera_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_meta_types.h"
+
+#include "ED_space_api.h"
+#include "ED_screen.h"
+
+#include "intern/gpu_codegen.h"
+#include "GPU_batch.h"
+#include "GPU_draw.h"
+#include "GPU_extensions.h"
+#include "GPU_framebuffer.h"
+#include "GPU_immediate.h"
+#include "GPU_lamp.h"
+#include "GPU_material.h"
+#include "GPU_shader.h"
+#include "GPU_texture.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_types.h"
+
+#include "draw_manager_text.h"
+#include "draw_manager_profiling.h"
+
+/* only for callbacks */
+#include "draw_cache_impl.h"
+
+#include "draw_instance_data.h"
+
+#include "draw_mode_engines.h"
+#include "engines/clay/clay_engine.h"
+#include "engines/eevee/eevee_engine.h"
+#include "engines/basic/basic_engine.h"
+#include "engines/external/external_engine.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Local Features
+ * \{ */
+
+#define USE_PROFILE
+
+#ifdef USE_PROFILE
+# include "PIL_time.h"
+
+# define PROFILE_TIMER_FALLOFF 0.1
+
+# define PROFILE_START(time_start) \
+ double time_start = PIL_check_seconds_timer();
+
+# define PROFILE_END_ACCUM(time_accum, time_start) { \
+ time_accum += (PIL_check_seconds_timer() - time_start) * 1e3; \
+} ((void)0)
+
+/* exp average */
+# define PROFILE_END_UPDATE(time_update, time_start) { \
+ double _time_delta = (PIL_check_seconds_timer() - time_start) * 1e3; \
+ time_update = (time_update * (1.0 - PROFILE_TIMER_FALLOFF)) + \
+ (_time_delta * PROFILE_TIMER_FALLOFF); \
+} ((void)0)
+
+#else /* USE_PROFILE */
+
+# define PROFILE_START(time_start) ((void)0)
+# define PROFILE_END_ACCUM(time_accum, time_start) ((void)0)
+# define PROFILE_END_UPDATE(time_update, time_start) ((void)0)
+
+#endif /* USE_PROFILE */
+
+
+/* Use draw manager to call GPU_select, see: DRW_draw_select_loop */
+#define USE_GPU_SELECT
+
+#ifdef USE_GPU_SELECT
+# include "ED_view3d.h"
+# include "ED_armature.h"
+# include "GPU_select.h"
+#endif
+
+/** \} */
+
+
+#define MAX_ATTRIB_NAME 32
+#define MAX_ATTRIB_COUNT 6 /* Can be adjusted for more */
+#define MAX_PASS_NAME 32
+#define MAX_CLIP_PLANES 6 /* GL_MAX_CLIP_PLANES is at least 6 */
+
+extern char datatoc_gpu_shader_2D_vert_glsl[];
+extern char datatoc_gpu_shader_3D_vert_glsl[];
+extern char datatoc_gpu_shader_fullscreen_vert_glsl[];
+
+/* Prototypes. */
+static void drw_engines_enable_external(void);
+
+/* Structures */
+typedef enum {
+ DRW_UNIFORM_BOOL,
+ DRW_UNIFORM_SHORT_TO_INT,
+ DRW_UNIFORM_SHORT_TO_FLOAT,
+ DRW_UNIFORM_INT,
+ DRW_UNIFORM_FLOAT,
+ DRW_UNIFORM_TEXTURE,
+ DRW_UNIFORM_BUFFER,
+ DRW_UNIFORM_MAT3,
+ DRW_UNIFORM_MAT4,
+ DRW_UNIFORM_BLOCK
+} DRWUniformType;
+
+typedef enum {
+ DRW_ATTRIB_INT,
+ DRW_ATTRIB_FLOAT,
+} DRWAttribType;
+
+struct DRWUniform {
+ struct DRWUniform *next;
+ DRWUniformType type;
+ int location;
+ int length;
+ int arraysize;
+ const void *value;
+};
+
+struct DRWInterface {
+ DRWUniform *uniforms; /* DRWUniform, single-linked list */
+ int attribs_count;
+ int attribs_stride;
+ int attribs_size[16];
+ int attribs_loc[16];
+ /* matrices locations */
+ int model;
+ int modelinverse;
+ int modelview;
+ int modelviewinverse;
+ int projection;
+ int projectioninverse;
+ int view;
+ int viewinverse;
+ int modelviewprojection;
+ int viewprojection;
+ int viewprojectioninverse;
+ int normal;
+ int worldnormal;
+ int camtexfac;
+ int orcotexfac;
+ int eye;
+ int clipplanes;
+ /* Dynamic batch */
+ Gwn_Batch *instance_batch; /* contains instances attributes */
+ GLuint instance_vbo; /* same as instance_batch but generated from DRWCalls */
+ struct DRWInstanceData *inst_data;
+#ifdef USE_GPU_SELECT
+ struct DRWInstanceData *inst_selectid;
+ /* Override for single object instances. */
+ int override_selectid;
+#endif
+ int instance_count;
+ Gwn_VertFormat vbo_format;
+};
+
+struct DRWPass {
+ /* Single linked list with last member to append */
+ DRWShadingGroup *shgroups;
+ DRWShadingGroup *shgroups_last;
+
+ DRWState state;
+ char name[MAX_PASS_NAME];
+};
+
+typedef struct DRWCallHeader {
+ void *prev;
+
+#ifdef USE_GPU_SELECT
+ int select_id;
+#endif
+ uchar type;
+} DRWCallHeader;
+
+typedef struct DRWCall {
+ DRWCallHeader head;
+
+ float obmat[4][4];
+ Gwn_Batch *geometry;
+
+ Object *ob; /* Optional */
+ ID *ob_data; /* Optional. */
+} DRWCall;
+
+typedef struct DRWCallGenerate {
+ DRWCallHeader head;
+
+ float obmat[4][4];
+
+ DRWCallGenerateFn *geometry_fn;
+ void *user_data;
+} DRWCallGenerate;
+
+struct DRWShadingGroup {
+ struct DRWShadingGroup *next;
+
+ GPUShader *shader; /* Shader to bind */
+ DRWInterface interface; /* Uniforms pointers */
+
+ /* DRWCall or DRWCallDynamic depending of type */
+ void *calls;
+ void *calls_first; /* To be able to traverse the list in the order of addition */
+
+ DRWState state_extra; /* State changes for this batch only (or'd with the pass's state) */
+ DRWState state_extra_disable; /* State changes for this batch only (and'd with the pass's state) */
+ unsigned int stencil_mask; /* Stencil mask to use for stencil test / write operations */
+ int type;
+
+ ID *instance_data; /* Object->data to instance */
+ Gwn_Batch *instance_geom; /* Geometry to instance */
+ Gwn_Batch *batch_geom; /* Result of call batching */
+
+#ifdef USE_GPU_SELECT
+ /* backlink to pass we're in */
+ DRWPass *pass_parent;
+#endif
+};
+
+/* Used by DRWShadingGroup.type */
+enum {
+ DRW_SHG_NORMAL,
+ DRW_SHG_POINT_BATCH,
+ DRW_SHG_LINE_BATCH,
+ DRW_SHG_TRIANGLE_BATCH,
+ DRW_SHG_INSTANCE,
+};
+
+/* Used by DRWCall.type */
+enum {
+ /* A single batch */
+ DRW_CALL_SINGLE,
+ /* Uses a callback to draw with any number of batches. */
+ DRW_CALL_GENERATE,
+ /* Arbitrary number of multiple args. */
+ DRW_CALL_DYNAMIC,
+};
+
+/** Render State: No persistent data between draw calls. */
+static struct DRWGlobalState {
+ /* Cache generation */
+ ViewportMemoryPool *vmempool;
+ DRWUniform *last_uniform;
+ DRWCall *last_call;
+ DRWCallGenerate *last_callgenerate;
+ DRWShadingGroup *last_shgroup;
+ DRWInstanceDataList *idatalist;
+
+ /* Rendering state */
+ GPUShader *shader;
+
+ /* Managed by `DRW_state_set`, `DRW_state_reset` */
+ DRWState state;
+ unsigned int stencil_mask;
+
+ /* Per viewport */
+ GPUViewport *viewport;
+ struct GPUFrameBuffer *default_framebuffer;
+ float size[2];
+ float screenvecs[2][3];
+ float pixsize;
+
+ GLenum backface, frontface;
+
+ /* Clip planes */
+ int num_clip_planes;
+ float clip_planes_eq[MAX_CLIP_PLANES][4];
+
+ struct {
+ unsigned int is_select : 1;
+ unsigned int is_depth : 1;
+ unsigned int is_image_render : 1;
+ unsigned int is_scene_render : 1;
+ unsigned int draw_background : 1;
+ } options;
+
+ /* Current rendering context */
+ DRWContextState draw_ctx;
+
+ /* Convenience pointer to text_store owned by the viewport */
+ struct DRWTextStore **text_store_p;
+
+ ListBase enabled_engines; /* RenderEngineType */
+
+ /* Profiling */
+ double cache_time;
+} DST = {NULL};
+
+/** GPU Resource State: Memory storage between drawing. */
+static struct DRWResourceState {
+ GPUTexture **bound_texs;
+
+ bool *bound_tex_slots;
+
+ int bind_tex_inc;
+ int bind_ubo_inc;
+} RST = {NULL};
+
+static struct DRWMatrixOveride {
+ float mat[6][4][4];
+ bool override[6];
+} viewport_matrix_override = {{{{0}}}};
+
+ListBase DRW_engines = {NULL, NULL};
+
+#ifdef USE_GPU_SELECT
+static unsigned int g_DRW_select_id = (unsigned int)-1;
+
+void DRW_select_load_id(unsigned int id)
+{
+ BLI_assert(G.f & G_PICKSEL);
+ g_DRW_select_id = id;
+}
+#endif
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Textures (DRW_texture)
+ * \{ */
+
+static void drw_texture_get_format(
+ DRWTextureFormat format,
+ GPUTextureFormat *r_data_type, int *r_channels)
+{
+ switch (format) {
+ case DRW_TEX_RGBA_8: *r_data_type = GPU_RGBA8; break;
+ case DRW_TEX_RGBA_16: *r_data_type = GPU_RGBA16F; break;
+ case DRW_TEX_RGB_16: *r_data_type = GPU_RGB16F; break;
+ case DRW_TEX_RGB_11_11_10: *r_data_type = GPU_R11F_G11F_B10F; break;
+ case DRW_TEX_RG_8: *r_data_type = GPU_RG8; break;
+ case DRW_TEX_RG_16: *r_data_type = GPU_RG16F; break;
+ case DRW_TEX_RG_16I: *r_data_type = GPU_RG16I; break;
+ case DRW_TEX_RG_32: *r_data_type = GPU_RG32F; break;
+ case DRW_TEX_R_8: *r_data_type = GPU_R8; break;
+ case DRW_TEX_R_16: *r_data_type = GPU_R16F; break;
+ case DRW_TEX_R_32: *r_data_type = GPU_R32F; break;
+#if 0
+ case DRW_TEX_RGBA_32: *r_data_type = GPU_RGBA32F; break;
+ case DRW_TEX_RGB_8: *r_data_type = GPU_RGB8; break;
+ case DRW_TEX_RGB_32: *r_data_type = GPU_RGB32F; break;
+#endif
+ case DRW_TEX_DEPTH_16: *r_data_type = GPU_DEPTH_COMPONENT16; break;
+ case DRW_TEX_DEPTH_24: *r_data_type = GPU_DEPTH_COMPONENT24; break;
+ case DRW_TEX_DEPTH_24_STENCIL_8: *r_data_type = GPU_DEPTH24_STENCIL8; break;
+ case DRW_TEX_DEPTH_32: *r_data_type = GPU_DEPTH_COMPONENT32F; break;
+ default :
+ /* file type not supported you must uncomment it from above */
+ BLI_assert(false);
+ break;
+ }
+
+ switch (format) {
+ case DRW_TEX_RGBA_8:
+ case DRW_TEX_RGBA_16:
+ case DRW_TEX_RGBA_32:
+ *r_channels = 4;
+ break;
+ case DRW_TEX_RGB_8:
+ case DRW_TEX_RGB_16:
+ case DRW_TEX_RGB_32:
+ case DRW_TEX_RGB_11_11_10:
+ *r_channels = 3;
+ break;
+ case DRW_TEX_RG_8:
+ case DRW_TEX_RG_16:
+ case DRW_TEX_RG_16I:
+ case DRW_TEX_RG_32:
+ *r_channels = 2;
+ break;
+ default:
+ *r_channels = 1;
+ break;
+ }
+}
+
+static void drw_texture_set_parameters(GPUTexture *tex, DRWTextureFlag flags)
+{
+ GPU_texture_bind(tex, 0);
+ if (flags & DRW_TEX_MIPMAP) {
+ GPU_texture_mipmap_mode(tex, true, flags & DRW_TEX_FILTER);
+ DRW_texture_generate_mipmaps(tex);
+ }
+ else {
+ GPU_texture_filter_mode(tex, flags & DRW_TEX_FILTER);
+ }
+ GPU_texture_wrap_mode(tex, flags & DRW_TEX_WRAP);
+ GPU_texture_compare_mode(tex, flags & DRW_TEX_COMPARE);
+ GPU_texture_unbind(tex);
+}
+
+GPUTexture *DRW_texture_create_1D(int w, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels)
+{
+ GPUTexture *tex;
+ GPUTextureFormat data_type;
+ int channels;
+
+ drw_texture_get_format(format, &data_type, &channels);
+ tex = GPU_texture_create_1D_custom(w, channels, data_type, fpixels, NULL);
+ drw_texture_set_parameters(tex, flags);
+
+ return tex;
+}
+
+GPUTexture *DRW_texture_create_2D(int w, int h, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels)
+{
+ GPUTexture *tex;
+ GPUTextureFormat data_type;
+ int channels;
+
+ drw_texture_get_format(format, &data_type, &channels);
+ tex = GPU_texture_create_2D_custom(w, h, channels, data_type, fpixels, NULL);
+ drw_texture_set_parameters(tex, flags);
+
+ return tex;
+}
+
+GPUTexture *DRW_texture_create_2D_array(
+ int w, int h, int d, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels)
+{
+ GPUTexture *tex;
+ GPUTextureFormat data_type;
+ int channels;
+
+ drw_texture_get_format(format, &data_type, &channels);
+ tex = GPU_texture_create_2D_array_custom(w, h, d, channels, data_type, fpixels, NULL);
+ drw_texture_set_parameters(tex, flags);
+
+ return tex;
+}
+
+GPUTexture *DRW_texture_create_3D(
+ int w, int h, int d, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels)
+{
+ GPUTexture *tex;
+ GPUTextureFormat data_type;
+ int channels;
+
+ drw_texture_get_format(format, &data_type, &channels);
+ tex = GPU_texture_create_3D_custom(w, h, d, channels, data_type, fpixels, NULL);
+ drw_texture_set_parameters(tex, flags);
+
+ return tex;
+}
+
+GPUTexture *DRW_texture_create_cube(int w, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels)
+{
+ GPUTexture *tex;
+ GPUTextureFormat data_type;
+ int channels;
+
+ drw_texture_get_format(format, &data_type, &channels);
+ tex = GPU_texture_create_cube_custom(w, channels, data_type, fpixels, NULL);
+ drw_texture_set_parameters(tex, flags);
+
+ return tex;
+}
+
+void DRW_texture_generate_mipmaps(GPUTexture *tex)
+{
+ GPU_texture_bind(tex, 0);
+ GPU_texture_generate_mipmap(tex);
+ GPU_texture_unbind(tex);
+}
+
+void DRW_texture_update(GPUTexture *tex, const float *pixels)
+{
+ GPU_texture_update(tex, pixels);
+}
+
+void DRW_texture_free(GPUTexture *tex)
+{
+ GPU_texture_free(tex);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Uniform Buffer Object (DRW_uniformbuffer)
+ * \{ */
+
+GPUUniformBuffer *DRW_uniformbuffer_create(int size, const void *data)
+{
+ return GPU_uniformbuffer_create(size, data, NULL);
+}
+
+void DRW_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data)
+{
+ GPU_uniformbuffer_update(ubo, data);
+}
+
+void DRW_uniformbuffer_free(GPUUniformBuffer *ubo)
+{
+ GPU_uniformbuffer_free(ubo);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Shaders (DRW_shader)
+ * \{ */
+
+GPUShader *DRW_shader_create(const char *vert, const char *geom, const char *frag, const char *defines)
+{
+ return GPU_shader_create(vert, frag, geom, NULL, defines);
+}
+
+GPUShader *DRW_shader_create_with_lib(
+ const char *vert, const char *geom, const char *frag, const char *lib, const char *defines)
+{
+ GPUShader *sh;
+ char *vert_with_lib = NULL;
+ char *frag_with_lib = NULL;
+ char *geom_with_lib = NULL;
+
+ vert_with_lib = BLI_string_joinN(lib, vert);
+ frag_with_lib = BLI_string_joinN(lib, frag);
+
+ if (geom) {
+ geom_with_lib = BLI_string_joinN(lib, geom);
+ }
+
+ sh = GPU_shader_create(vert_with_lib, frag_with_lib, geom_with_lib, NULL, defines);
+
+ MEM_freeN(vert_with_lib);
+ MEM_freeN(frag_with_lib);
+ if (geom) {
+ MEM_freeN(geom_with_lib);
+ }
+
+ return sh;
+}
+
+GPUShader *DRW_shader_create_2D(const char *frag, const char *defines)
+{
+ return GPU_shader_create(datatoc_gpu_shader_2D_vert_glsl, frag, NULL, NULL, defines);
+}
+
+GPUShader *DRW_shader_create_3D(const char *frag, const char *defines)
+{
+ return GPU_shader_create(datatoc_gpu_shader_3D_vert_glsl, frag, NULL, NULL, defines);
+}
+
+GPUShader *DRW_shader_create_fullscreen(const char *frag, const char *defines)
+{
+ return GPU_shader_create(datatoc_gpu_shader_fullscreen_vert_glsl, frag, NULL, NULL, defines);
+}
+
+GPUShader *DRW_shader_create_3D_depth_only(void)
+{
+ return GPU_shader_get_builtin_shader(GPU_SHADER_3D_DEPTH_ONLY);
+}
+
+void DRW_shader_free(GPUShader *shader)
+{
+ GPU_shader_free(shader);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Interface (DRW_interface)
+ * \{ */
+
+static void drw_interface_create(DRWInterface *interface, GPUShader *shader)
+{
+ interface->model = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_MODEL);
+ interface->modelinverse = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_MODEL_INV);
+ interface->modelview = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_MODELVIEW);
+ interface->modelviewinverse = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_MODELVIEW_INV);
+ interface->projection = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_PROJECTION);
+ interface->projectioninverse = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_PROJECTION_INV);
+ interface->view = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_VIEW);
+ interface->viewinverse = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_VIEW_INV);
+ interface->viewprojection = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_VIEWPROJECTION);
+ interface->viewprojectioninverse = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_VIEWPROJECTION_INV);
+ interface->modelviewprojection = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_MVP);
+ interface->normal = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_NORMAL);
+ interface->worldnormal = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_WORLDNORMAL);
+ interface->camtexfac = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_CAMERATEXCO);
+ interface->orcotexfac = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_ORCO);
+ interface->clipplanes = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_CLIPPLANES);
+ interface->eye = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_EYE);
+ interface->instance_count = 0;
+ interface->attribs_count = 0;
+ interface->attribs_stride = 0;
+ interface->instance_vbo = 0;
+ interface->instance_batch = NULL;
+ interface->inst_data = NULL;
+ interface->uniforms = NULL;
+#ifdef USE_GPU_SELECT
+ interface->inst_selectid = NULL;
+ interface->override_selectid = -1;
+#endif
+
+ memset(&interface->vbo_format, 0, sizeof(Gwn_VertFormat));
+}
+
+
+static void drw_interface_uniform(DRWShadingGroup *shgroup, const char *name,
+ DRWUniformType type, const void *value, int length, int arraysize)
+{
+ int location;
+ if (type == DRW_UNIFORM_BLOCK) {
+ location = GPU_shader_get_uniform_block(shgroup->shader, name);
+ }
+ else {
+ location = GPU_shader_get_uniform(shgroup->shader, name);
+ }
+
+ if (location == -1) {
+ if (G.debug & G_DEBUG)
+ fprintf(stderr, "Uniform '%s' not found!\n", name);
+ /* Nice to enable eventually, for now eevee uses uniforms that might not exist. */
+ // BLI_assert(0);
+ return;
+ }
+
+ DRWUniform *uni = BLI_mempool_alloc(DST.vmempool->uniforms);
+
+ BLI_assert(arraysize > 0);
+
+ uni->location = location;
+ uni->type = type;
+ uni->value = value;
+ uni->length = length;
+ uni->arraysize = arraysize;
+
+ /* Prepend */
+ uni->next = shgroup->interface.uniforms;
+ shgroup->interface.uniforms = uni;
+}
+
+static void drw_interface_attrib(DRWShadingGroup *shgroup, const char *name, DRWAttribType UNUSED(type), int size, bool dummy)
+{
+ unsigned int attrib_id = shgroup->interface.attribs_count;
+ GLuint program = GPU_shader_get_program(shgroup->shader);
+
+ shgroup->interface.attribs_loc[attrib_id] = glGetAttribLocation(program, name);
+ shgroup->interface.attribs_size[attrib_id] = size;
+ shgroup->interface.attribs_stride += size;
+ shgroup->interface.attribs_count += 1;
+
+ if (shgroup->type != DRW_SHG_INSTANCE) {
+ BLI_assert(size <= 4); /* Matrices are not supported by Gawain. */
+ GWN_vertformat_attr_add(&shgroup->interface.vbo_format, name, GWN_COMP_F32, size, GWN_FETCH_FLOAT);
+ }
+
+ BLI_assert(shgroup->interface.attribs_count < MAX_ATTRIB_COUNT);
+
+/* Adding attribute even if not found for now (to keep memory alignment).
+ * Should ideally take vertex format automatically from batch eventually */
+#if 0
+ if (attrib->location == -1 && !dummy) {
+ if (G.debug & G_DEBUG)
+ fprintf(stderr, "Attribute '%s' not found!\n", name);
+ BLI_assert(0);
+ MEM_freeN(attrib);
+ return;
+ }
+#else
+ UNUSED_VARS(dummy);
+#endif
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Shading Group (DRW_shgroup)
+ * \{ */
+
+DRWShadingGroup *DRW_shgroup_create(struct GPUShader *shader, DRWPass *pass)
+{
+ DRWShadingGroup *shgroup = BLI_mempool_alloc(DST.vmempool->shgroups);
+
+ /* Append */
+ if (pass->shgroups != NULL) {
+ pass->shgroups_last->next = shgroup;
+ }
+ else {
+ pass->shgroups = shgroup;
+ }
+ pass->shgroups_last = shgroup;
+ shgroup->next = NULL;
+
+ drw_interface_create(&shgroup->interface, shader);
+
+ shgroup->type = DRW_SHG_NORMAL;
+ shgroup->shader = shader;
+ shgroup->state_extra = 0;
+ shgroup->state_extra_disable = ~0x0;
+ shgroup->stencil_mask = 0;
+ shgroup->batch_geom = NULL;
+ shgroup->instance_geom = NULL;
+ shgroup->instance_data = NULL;
+
+ shgroup->calls = NULL;
+ shgroup->calls_first = NULL;
+
+#ifdef USE_GPU_SELECT
+ shgroup->pass_parent = pass;
+#endif
+
+ return shgroup;
+}
+
+DRWShadingGroup *DRW_shgroup_material_create(struct GPUMaterial *material, DRWPass *pass)
+{
+ double time = 0.0; /* TODO make time variable */
+
+ /* TODO : Ideally we should not convert. But since the whole codegen
+ * is relying on GPUPass we keep it as is for now. */
+ GPUPass *gpupass = GPU_material_get_pass(material);
+
+ if (!gpupass) {
+ /* Shader compilation error */
+ return NULL;
+ }
+
+ struct GPUShader *shader = GPU_pass_shader(gpupass);
+
+ DRWShadingGroup *grp = DRW_shgroup_create(shader, pass);
+
+ /* Converting dynamic GPUInput to DRWUniform */
+ ListBase *inputs = &gpupass->inputs;
+
+ for (GPUInput *input = inputs->first; input; input = input->next) {
+ /* Textures */
+ if (input->ima) {
+ GPUTexture *tex = GPU_texture_from_blender(
+ input->ima, input->iuser, input->textarget, input->image_isdata, time, 1);
+
+ if (input->bindtex) {
+ DRW_shgroup_uniform_texture(grp, input->shadername, tex);
+ }
+ }
+ /* Color Ramps */
+ else if (input->tex) {
+ DRW_shgroup_uniform_texture(grp, input->shadername, input->tex);
+ }
+ /* Floats */
+ else {
+ switch (input->type) {
+ case GPU_FLOAT:
+ case GPU_VEC2:
+ case GPU_VEC3:
+ case GPU_VEC4:
+ /* Should already be in the material ubo. */
+ break;
+ case GPU_MAT3:
+ DRW_shgroup_uniform_mat3(grp, input->shadername, (float *)input->dynamicvec);
+ break;
+ case GPU_MAT4:
+ DRW_shgroup_uniform_mat4(grp, input->shadername, (float *)input->dynamicvec);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ GPUUniformBuffer *ubo = GPU_material_get_uniform_buffer(material);
+ if (ubo != NULL) {
+ DRW_shgroup_uniform_block(grp, GPU_UBO_BLOCK_NAME, ubo);
+ }
+
+ return grp;
+}
+
+DRWShadingGroup *DRW_shgroup_material_instance_create(
+ struct GPUMaterial *material, DRWPass *pass, Gwn_Batch *geom, Object *ob)
+{
+ DRWShadingGroup *shgroup = DRW_shgroup_material_create(material, pass);
+
+ if (shgroup) {
+ shgroup->type = DRW_SHG_INSTANCE;
+ shgroup->instance_geom = geom;
+ shgroup->instance_data = ob->data;
+ }
+
+ return shgroup;
+}
+
+DRWShadingGroup *DRW_shgroup_material_empty_tri_batch_create(
+ struct GPUMaterial *material, DRWPass *pass, int size)
+{
+ DRWShadingGroup *shgroup = DRW_shgroup_material_create(material, pass);
+
+ if (shgroup) {
+ shgroup->type = DRW_SHG_TRIANGLE_BATCH;
+ shgroup->interface.instance_count = size * 3;
+ drw_interface_attrib(shgroup, "dummy", DRW_ATTRIB_FLOAT, 1, true);
+ }
+
+ return shgroup;
+}
+
+DRWShadingGroup *DRW_shgroup_instance_create(struct GPUShader *shader, DRWPass *pass, Gwn_Batch *geom)
+{
+ DRWShadingGroup *shgroup = DRW_shgroup_create(shader, pass);
+
+ shgroup->type = DRW_SHG_INSTANCE;
+ shgroup->instance_geom = geom;
+
+ return shgroup;
+}
+
+DRWShadingGroup *DRW_shgroup_point_batch_create(struct GPUShader *shader, DRWPass *pass)
+{
+ DRWShadingGroup *shgroup = DRW_shgroup_create(shader, pass);
+
+ shgroup->type = DRW_SHG_POINT_BATCH;
+ DRW_shgroup_attrib_float(shgroup, "pos", 3);
+
+ return shgroup;
+}
+
+DRWShadingGroup *DRW_shgroup_line_batch_create(struct GPUShader *shader, DRWPass *pass)
+{
+ DRWShadingGroup *shgroup = DRW_shgroup_create(shader, pass);
+
+ shgroup->type = DRW_SHG_LINE_BATCH;
+ DRW_shgroup_attrib_float(shgroup, "pos", 3);
+
+ return shgroup;
+}
+
+/* Very special batch. Use this if you position
+ * your vertices with the vertex shader
+ * and dont need any VBO attrib */
+DRWShadingGroup *DRW_shgroup_empty_tri_batch_create(struct GPUShader *shader, DRWPass *pass, int size)
+{
+ DRWShadingGroup *shgroup = DRW_shgroup_create(shader, pass);
+
+ shgroup->type = DRW_SHG_TRIANGLE_BATCH;
+ shgroup->interface.instance_count = size * 3;
+ drw_interface_attrib(shgroup, "dummy", DRW_ATTRIB_FLOAT, 1, true);
+
+ return shgroup;
+}
+
+void DRW_shgroup_free(struct DRWShadingGroup *shgroup)
+{
+ if (shgroup->interface.instance_vbo &&
+ (shgroup->interface.instance_batch == 0))
+ {
+ glDeleteBuffers(1, &shgroup->interface.instance_vbo);
+ }
+
+ GWN_BATCH_DISCARD_SAFE(shgroup->batch_geom);
+}
+
+#define CALL_PREPEND(shgroup, call) { \
+ if (shgroup->calls == NULL) { \
+ shgroup->calls = call; \
+ shgroup->calls_first = call; \
+ } \
+ else { \
+ ((DRWCall *)(shgroup->calls))->head.prev = call; \
+ shgroup->calls = call; \
+ } \
+ call->head.prev = NULL; \
+} ((void)0)
+
+void DRW_shgroup_instance_batch(DRWShadingGroup *shgroup, struct Gwn_Batch *instances)
+{
+ BLI_assert(shgroup->type == DRW_SHG_INSTANCE);
+ BLI_assert(shgroup->interface.instance_batch == NULL);
+
+ shgroup->interface.instance_batch = instances;
+
+#ifdef USE_GPU_SELECT
+ DRWCall *call = BLI_mempool_alloc(DST.vmempool->calls);
+ call->head.select_id = g_DRW_select_id;
+
+ CALL_PREPEND(shgroup, call);
+#endif
+}
+
+void DRW_shgroup_call_add(DRWShadingGroup *shgroup, Gwn_Batch *geom, float (*obmat)[4])
+{
+ BLI_assert(geom != NULL);
+ BLI_assert(shgroup->type == DRW_SHG_NORMAL);
+
+ DRWCall *call = BLI_mempool_alloc(DST.vmempool->calls);
+
+ CALL_PREPEND(shgroup, call);
+
+ call->head.type = DRW_CALL_SINGLE;
+#ifdef USE_GPU_SELECT
+ call->head.select_id = g_DRW_select_id;
+#endif
+
+ if (obmat != NULL) {
+ copy_m4_m4(call->obmat, obmat);
+ }
+
+ call->geometry = geom;
+ call->ob_data = NULL;
+}
+
+void DRW_shgroup_call_object_add(DRWShadingGroup *shgroup, Gwn_Batch *geom, Object *ob)
+{
+ BLI_assert(geom != NULL);
+ BLI_assert(shgroup->type == DRW_SHG_NORMAL);
+
+ DRWCall *call = BLI_mempool_alloc(DST.vmempool->calls);
+
+ CALL_PREPEND(shgroup, call);
+
+ call->head.type = DRW_CALL_SINGLE;
+#ifdef USE_GPU_SELECT
+ call->head.select_id = g_DRW_select_id;
+#endif
+
+ copy_m4_m4(call->obmat, ob->obmat);
+ call->geometry = geom;
+ call->ob_data = ob->data;
+}
+
+void DRW_shgroup_call_generate_add(
+ DRWShadingGroup *shgroup,
+ DRWCallGenerateFn *geometry_fn, void *user_data,
+ float (*obmat)[4])
+{
+ BLI_assert(geometry_fn != NULL);
+ BLI_assert(shgroup->type == DRW_SHG_NORMAL);
+
+ DRWCallGenerate *call = BLI_mempool_alloc(DST.vmempool->calls_generate);
+
+ CALL_PREPEND(shgroup, call);
+
+ call->head.type = DRW_CALL_GENERATE;
+#ifdef USE_GPU_SELECT
+ call->head.select_id = g_DRW_select_id;
+#endif
+
+ if (obmat != NULL) {
+ copy_m4_m4(call->obmat, obmat);
+ }
+
+ call->geometry_fn = geometry_fn;
+ call->user_data = user_data;
+}
+
+static void sculpt_draw_cb(
+ DRWShadingGroup *shgroup,
+ void (*draw_fn)(DRWShadingGroup *shgroup, Gwn_Batch *geom),
+ void *user_data)
+{
+ Object *ob = user_data;
+ PBVH *pbvh = ob->sculpt->pbvh;
+
+ if (pbvh) {
+ BKE_pbvh_draw_cb(
+ pbvh, NULL, NULL, false,
+ (void (*)(void *, Gwn_Batch *))draw_fn, shgroup);
+ }
+}
+
+void DRW_shgroup_call_sculpt_add(DRWShadingGroup *shgroup, Object *ob, float (*obmat)[4])
+{
+ DRW_shgroup_call_generate_add(shgroup, sculpt_draw_cb, ob, obmat);
+}
+
+void DRW_shgroup_call_dynamic_add_array(DRWShadingGroup *shgroup, const void *attr[], unsigned int attr_len)
+{
+ DRWInterface *interface = &shgroup->interface;
+
+#ifdef USE_GPU_SELECT
+ if (G.f & G_PICKSEL) {
+ if (interface->inst_selectid == NULL) {
+ interface->inst_selectid = DRW_instance_data_request(DST.idatalist, 1, 128);
+ }
+
+ int *select_id = DRW_instance_data_next(interface->inst_selectid);
+ *select_id = g_DRW_select_id;
+ }
+#endif
+
+ BLI_assert(attr_len == interface->attribs_count);
+ UNUSED_VARS_NDEBUG(attr_len);
+
+ if (interface->attribs_stride > 0) {
+ if (interface->inst_data == NULL) {
+ interface->inst_data = DRW_instance_data_request(DST.idatalist, interface->attribs_stride, 16);
+ }
+
+ float *data = DRW_instance_data_next(interface->inst_data);
+
+ for (int i = 0; i < interface->attribs_count; ++i) {
+ memcpy(data, attr[i], sizeof(float) * interface->attribs_size[i]);
+ data = data + interface->attribs_size[i];
+ }
+ }
+
+ interface->instance_count += 1;
+}
+
+/* Used for instancing with no attributes */
+void DRW_shgroup_set_instance_count(DRWShadingGroup *shgroup, int count)
+{
+ DRWInterface *interface = &shgroup->interface;
+
+ BLI_assert(interface->instance_count == 0);
+ BLI_assert(interface->attribs_count == 0);
+
+#ifdef USE_GPU_SELECT
+ if (G.f & G_PICKSEL) {
+ interface->override_selectid = g_DRW_select_id;
+ }
+#endif
+
+ interface->instance_count = count;
+}
+
+/**
+ * State is added to #Pass.state while drawing.
+ * Use to temporarily enable draw options.
+ */
+void DRW_shgroup_state_enable(DRWShadingGroup *shgroup, DRWState state)
+{
+ shgroup->state_extra |= state;
+}
+
+void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state)
+{
+ shgroup->state_extra_disable &= ~state;
+}
+
+void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, unsigned int mask)
+{
+ BLI_assert(mask <= 255);
+ shgroup->stencil_mask = mask;
+}
+
+void DRW_shgroup_attrib_float(DRWShadingGroup *shgroup, const char *name, int size)
+{
+ drw_interface_attrib(shgroup, name, DRW_ATTRIB_FLOAT, size, false);
+}
+
+void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup, const char *name, const GPUTexture *tex)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_TEXTURE, tex, 0, 1);
+}
+
+void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, const char *name, const GPUUniformBuffer *ubo)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_BLOCK, ubo, 0, 1);
+}
+
+void DRW_shgroup_uniform_buffer(DRWShadingGroup *shgroup, const char *name, GPUTexture **tex)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_BUFFER, tex, 0, 1);
+}
+
+void DRW_shgroup_uniform_bool(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_BOOL, value, 1, arraysize);
+}
+
+void DRW_shgroup_uniform_float(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 1, arraysize);
+}
+
+void DRW_shgroup_uniform_vec2(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 2, arraysize);
+}
+
+void DRW_shgroup_uniform_vec3(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 3, arraysize);
+}
+
+void DRW_shgroup_uniform_vec4(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 4, arraysize);
+}
+
+void DRW_shgroup_uniform_short_to_int(DRWShadingGroup *shgroup, const char *name, const short *value, int arraysize)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_SHORT_TO_INT, value, 1, arraysize);
+}
+
+void DRW_shgroup_uniform_short_to_float(DRWShadingGroup *shgroup, const char *name, const short *value, int arraysize)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_SHORT_TO_FLOAT, value, 1, arraysize);
+}
+
+void DRW_shgroup_uniform_int(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_INT, value, 1, arraysize);
+}
+
+void DRW_shgroup_uniform_ivec2(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_INT, value, 2, arraysize);
+}
+
+void DRW_shgroup_uniform_ivec3(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_INT, value, 3, arraysize);
+}
+
+void DRW_shgroup_uniform_mat3(DRWShadingGroup *shgroup, const char *name, const float *value)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_MAT3, value, 9, 1);
+}
+
+void DRW_shgroup_uniform_mat4(DRWShadingGroup *shgroup, const char *name, const float *value)
+{
+ drw_interface_uniform(shgroup, name, DRW_UNIFORM_MAT4, value, 16, 1);
+}
+
+/* Creates a VBO containing OGL primitives for all DRWCallDynamic */
+static void shgroup_dynamic_batch(DRWShadingGroup *shgroup)
+{
+ DRWInterface *interface = &shgroup->interface;
+ int nbr = interface->instance_count;
+
+ Gwn_PrimType type = (shgroup->type == DRW_SHG_POINT_BATCH) ? GWN_PRIM_POINTS :
+ (shgroup->type == DRW_SHG_TRIANGLE_BATCH) ? GWN_PRIM_TRIS : GWN_PRIM_LINES;
+
+ if (nbr == 0)
+ return;
+
+ /* Upload Data */
+ Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&interface->vbo_format);
+ if (interface->inst_data) {
+ GWN_vertbuf_data_set(vbo, nbr, DRW_instance_data_get(interface->inst_data), false);
+ } else {
+ /* Use unitialized memory. This is for dummy vertex buffers. */
+ /* XXX TODO do not alloc at all. */
+ GWN_vertbuf_data_alloc(vbo, nbr);
+ }
+
+ /* TODO make the batch dynamic instead of freeing it every times */
+ if (shgroup->batch_geom)
+ GWN_batch_discard(shgroup->batch_geom);
+
+ shgroup->batch_geom = GWN_batch_create_ex(type, vbo, NULL, GWN_BATCH_OWNS_VBO);
+}
+
+static void shgroup_dynamic_instance(DRWShadingGroup *shgroup)
+{
+ DRWInterface *interface = &shgroup->interface;
+ int buffer_size = 0;
+ void *data = NULL;
+
+ if (interface->instance_batch != NULL) {
+ return;
+ }
+
+ /* TODO We still need this because gawain does not support Matrix attribs. */
+ if (interface->instance_count == 0) {
+ if (interface->instance_vbo) {
+ glDeleteBuffers(1, &interface->instance_vbo);
+ interface->instance_vbo = 0;
+ }
+ return;
+ }
+
+ /* Gather Data */
+ buffer_size = sizeof(float) * interface->attribs_stride * interface->instance_count;
+
+ /* TODO poke mike to add this to gawain */
+ if (interface->instance_vbo) {
+ glDeleteBuffers(1, &interface->instance_vbo);
+ interface->instance_vbo = 0;
+ }
+
+ if (interface->inst_data) {
+ data = DRW_instance_data_get(interface->inst_data);
+ }
+
+ glGenBuffers(1, &interface->instance_vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, interface->instance_vbo);
+ glBufferData(GL_ARRAY_BUFFER, buffer_size, data, GL_STATIC_DRAW);
+}
+
+static void shgroup_dynamic_batch_from_calls(DRWShadingGroup *shgroup)
+{
+ if ((shgroup->interface.instance_vbo || shgroup->batch_geom) &&
+ (G.debug_value == 667))
+ {
+ return;
+ }
+
+ if (shgroup->type == DRW_SHG_INSTANCE) {
+ shgroup_dynamic_instance(shgroup);
+ }
+ else {
+ shgroup_dynamic_batch(shgroup);
+ }
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Passes (DRW_pass)
+ * \{ */
+
+DRWPass *DRW_pass_create(const char *name, DRWState state)
+{
+ DRWPass *pass = BLI_mempool_alloc(DST.vmempool->passes);
+ pass->state = state;
+ BLI_strncpy(pass->name, name, MAX_PASS_NAME);
+
+ pass->shgroups = NULL;
+ pass->shgroups_last = NULL;
+
+ return pass;
+}
+
+void DRW_pass_state_set(DRWPass *pass, DRWState state)
+{
+ pass->state = state;
+}
+
+void DRW_pass_free(DRWPass *pass)
+{
+ for (DRWShadingGroup *shgroup = pass->shgroups; shgroup; shgroup = shgroup->next) {
+ DRW_shgroup_free(shgroup);
+ }
+
+ pass->shgroups = NULL;
+ pass->shgroups_last = NULL;
+}
+
+void DRW_pass_foreach_shgroup(DRWPass *pass, void (*callback)(void *userData, DRWShadingGroup *shgrp), void *userData)
+{
+ for (DRWShadingGroup *shgroup = pass->shgroups; shgroup; shgroup = shgroup->next) {
+ callback(userData, shgroup);
+ }
+}
+
+typedef struct ZSortData {
+ float *axis;
+ float *origin;
+} ZSortData;
+
+static int pass_shgroup_dist_sort(void *thunk, const void *a, const void *b)
+{
+ const ZSortData *zsortdata = (ZSortData *)thunk;
+ const DRWShadingGroup *shgrp_a = (const DRWShadingGroup *)a;
+ const DRWShadingGroup *shgrp_b = (const DRWShadingGroup *)b;
+
+ const DRWCall *call_a;
+ const DRWCall *call_b;
+
+ call_a = shgrp_a->calls_first;
+ call_b = shgrp_b->calls_first;
+
+ if (call_a == NULL) return -1;
+ if (call_b == NULL) return -1;
+
+ float tmp[3];
+ sub_v3_v3v3(tmp, zsortdata->origin, call_a->obmat[3]);
+ const float a_sq = dot_v3v3(zsortdata->axis, tmp);
+ sub_v3_v3v3(tmp, zsortdata->origin, call_b->obmat[3]);
+ const float b_sq = dot_v3v3(zsortdata->axis, tmp);
+
+ if (a_sq < b_sq) return 1;
+ else if (a_sq > b_sq) return -1;
+ else {
+ /* If there is a depth prepass put it before */
+ if ((shgrp_a->state_extra & DRW_STATE_WRITE_DEPTH) != 0) {
+ return -1;
+ }
+ else if ((shgrp_b->state_extra & DRW_STATE_WRITE_DEPTH) != 0) {
+ return 1;
+ }
+ else return 0;
+ }
+}
+
+/* ------------------ Shading group sorting --------------------- */
+
+#define SORT_IMPL_LINKTYPE DRWShadingGroup
+
+#define SORT_IMPL_USE_THUNK
+#define SORT_IMPL_FUNC shgroup_sort_fn_r
+#include "../../blenlib/intern/list_sort_impl.h"
+#undef SORT_IMPL_FUNC
+#undef SORT_IMPL_USE_THUNK
+
+#undef SORT_IMPL_LINKTYPE
+
+/**
+ * Sort Shading groups by decreasing Z of their first draw call.
+ * This is usefull for order dependant effect such as transparency.
+ **/
+void DRW_pass_sort_shgroup_z(DRWPass *pass)
+{
+ RegionView3D *rv3d = DST.draw_ctx.rv3d;
+
+ float (*viewinv)[4];
+ viewinv = (viewport_matrix_override.override[DRW_MAT_VIEWINV])
+ ? viewport_matrix_override.mat[DRW_MAT_VIEWINV] : rv3d->viewinv;
+
+ ZSortData zsortdata = {viewinv[2], viewinv[3]};
+
+ if (pass->shgroups && pass->shgroups->next) {
+ pass->shgroups = shgroup_sort_fn_r(pass->shgroups, pass_shgroup_dist_sort, &zsortdata);
+
+ /* Find the next last */
+ DRWShadingGroup *last = pass->shgroups;
+ while ((last = last->next)) {
+ /* Do nothing */
+ }
+ pass->shgroups_last = last;
+ }
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Draw (DRW_draw)
+ * \{ */
+
+static void drw_state_set(DRWState state)
+{
+ if (DST.state == state) {
+ return;
+ }
+
+
+#define CHANGED_TO(f) \
+ ((DST.state & (f)) ? \
+ ((state & (f)) ? 0 : -1) : \
+ ((state & (f)) ? 1 : 0))
+
+#define CHANGED_ANY(f) \
+ ((DST.state & (f)) != (state & (f)))
+
+#define CHANGED_ANY_STORE_VAR(f, enabled) \
+ ((DST.state & (f)) != (enabled = (state & (f))))
+
+ /* Depth Write */
+ {
+ int test;
+ if ((test = CHANGED_TO(DRW_STATE_WRITE_DEPTH))) {
+ if (test == 1) {
+ glDepthMask(GL_TRUE);
+ }
+ else {
+ glDepthMask(GL_FALSE);
+ }
+ }
+ }
+
+ /* Color Write */
+ {
+ int test;
+ if ((test = CHANGED_TO(DRW_STATE_WRITE_COLOR))) {
+ if (test == 1) {
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ }
+ else {
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ }
+ }
+ }
+
+ /* Cull */
+ {
+ DRWState test;
+ if (CHANGED_ANY_STORE_VAR(
+ DRW_STATE_CULL_BACK | DRW_STATE_CULL_FRONT,
+ test))
+ {
+ if (test) {
+ glEnable(GL_CULL_FACE);
+
+ if ((state & DRW_STATE_CULL_BACK) != 0) {
+ glCullFace(GL_BACK);
+ }
+ else if ((state & DRW_STATE_CULL_FRONT) != 0) {
+ glCullFace(GL_FRONT);
+ }
+ else {
+ BLI_assert(0);
+ }
+ }
+ else {
+ glDisable(GL_CULL_FACE);
+ }
+ }
+ }
+
+ /* Depth Test */
+ {
+ DRWState test;
+ if (CHANGED_ANY_STORE_VAR(
+ DRW_STATE_DEPTH_LESS | DRW_STATE_DEPTH_EQUAL | DRW_STATE_DEPTH_GREATER | DRW_STATE_DEPTH_ALWAYS,
+ test))
+ {
+ if (test) {
+ glEnable(GL_DEPTH_TEST);
+
+ if (state & DRW_STATE_DEPTH_LESS) {
+ glDepthFunc(GL_LEQUAL);
+ }
+ else if (state & DRW_STATE_DEPTH_EQUAL) {
+ glDepthFunc(GL_EQUAL);
+ }
+ else if (state & DRW_STATE_DEPTH_GREATER) {
+ glDepthFunc(GL_GREATER);
+ }
+ else if (state & DRW_STATE_DEPTH_ALWAYS) {
+ glDepthFunc(GL_ALWAYS);
+ }
+ else {
+ BLI_assert(0);
+ }
+ }
+ else {
+ glDisable(GL_DEPTH_TEST);
+ }
+ }
+ }
+
+ /* Wire Width */
+ {
+ if (CHANGED_ANY(DRW_STATE_WIRE | DRW_STATE_WIRE_LARGE)) {
+ if ((state & DRW_STATE_WIRE) != 0) {
+ glLineWidth(1.0f);
+ }
+ else if ((state & DRW_STATE_WIRE_LARGE) != 0) {
+ glLineWidth(UI_GetThemeValuef(TH_OUTLINE_WIDTH) * 2.0f);
+ }
+ else {
+ /* do nothing */
+ }
+ }
+ }
+
+ /* Points Size */
+ {
+ int test;
+ if ((test = CHANGED_TO(DRW_STATE_POINT))) {
+ if (test == 1) {
+ GPU_enable_program_point_size();
+ glPointSize(5.0f);
+ }
+ else {
+ GPU_disable_program_point_size();
+ }
+ }
+ }
+
+ /* Blending (all buffer) */
+ {
+ int test;
+ if (CHANGED_ANY_STORE_VAR(
+ DRW_STATE_BLEND | DRW_STATE_ADDITIVE | DRW_STATE_MULTIPLY | DRW_STATE_TRANSMISSION |
+ DRW_STATE_ADDITIVE_FULL,
+ test))
+ {
+ if (test) {
+ glEnable(GL_BLEND);
+
+ if ((state & DRW_STATE_BLEND) != 0) {
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, /* RGB */
+ GL_ONE, GL_ONE_MINUS_SRC_ALPHA); /* Alpha */
+ }
+ else if ((state & DRW_STATE_MULTIPLY) != 0) {
+ glBlendFunc(GL_DST_COLOR, GL_ZERO);
+ }
+ else if ((state & DRW_STATE_TRANSMISSION) != 0) {
+ glBlendFunc(GL_ONE, GL_SRC_ALPHA);
+ }
+ else if ((state & DRW_STATE_ADDITIVE) != 0) {
+ /* Do not let alpha accumulate but premult the source RGB by it. */
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, /* RGB */
+ GL_ZERO, GL_ONE); /* Alpha */
+ }
+ else if ((state & DRW_STATE_ADDITIVE_FULL) != 0) {
+ /* Let alpha accumulate. */
+ glBlendFunc(GL_ONE, GL_ONE);
+ }
+ else {
+ BLI_assert(0);
+ }
+ }
+ else {
+ glDisable(GL_BLEND);
+ }
+ }
+ }
+
+ /* Clip Planes */
+ {
+ int test;
+ if ((test = CHANGED_TO(DRW_STATE_CLIP_PLANES))) {
+ if (test == 1) {
+ for (int i = 0; i < DST.num_clip_planes; ++i) {
+ glEnable(GL_CLIP_DISTANCE0 + i);
+ }
+ }
+ else {
+ for (int i = 0; i < MAX_CLIP_PLANES; ++i) {
+ glDisable(GL_CLIP_DISTANCE0 + i);
+ }
+ }
+ }
+ }
+
+ /* Line Stipple */
+ {
+ int test;
+ if (CHANGED_ANY_STORE_VAR(
+ DRW_STATE_STIPPLE_2 | DRW_STATE_STIPPLE_3 | DRW_STATE_STIPPLE_4,
+ test))
+ {
+ if (test) {
+ if ((state & DRW_STATE_STIPPLE_2) != 0) {
+ setlinestyle(2);
+ }
+ else if ((state & DRW_STATE_STIPPLE_3) != 0) {
+ setlinestyle(3);
+ }
+ else if ((state & DRW_STATE_STIPPLE_4) != 0) {
+ setlinestyle(4);
+ }
+ else {
+ BLI_assert(0);
+ }
+ }
+ else {
+ setlinestyle(0);
+ }
+ }
+ }
+
+ /* Stencil */
+ {
+ DRWState test;
+ if (CHANGED_ANY_STORE_VAR(
+ DRW_STATE_WRITE_STENCIL |
+ DRW_STATE_STENCIL_EQUAL,
+ test))
+ {
+ if (test) {
+ glEnable(GL_STENCIL_TEST);
+
+ /* Stencil Write */
+ if ((state & DRW_STATE_WRITE_STENCIL) != 0) {
+ glStencilMask(0xFF);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+ }
+ /* Stencil Test */
+ else if ((state & DRW_STATE_STENCIL_EQUAL) != 0) {
+ glStencilMask(0x00); /* disable write */
+ DST.stencil_mask = 0;
+ }
+ else {
+ BLI_assert(0);
+ }
+ }
+ else {
+ /* disable write & test */
+ DST.stencil_mask = 0;
+ glStencilMask(0x00);
+ glStencilFunc(GL_ALWAYS, 1, 0xFF);
+ glDisable(GL_STENCIL_TEST);
+ }
+ }
+ }
+
+#undef CHANGED_TO
+#undef CHANGED_ANY
+#undef CHANGED_ANY_STORE_VAR
+
+ DST.state = state;
+}
+
+static void drw_stencil_set(unsigned int mask)
+{
+ if (DST.stencil_mask != mask) {
+ /* Stencil Write */
+ if ((DST.state & DRW_STATE_WRITE_STENCIL) != 0) {
+ glStencilFunc(GL_ALWAYS, mask, 0xFF);
+ DST.stencil_mask = mask;
+ }
+ /* Stencil Test */
+ else if ((DST.state & DRW_STATE_STENCIL_EQUAL) != 0) {
+ glStencilFunc(GL_EQUAL, mask, 0xFF);
+ DST.stencil_mask = mask;
+ }
+ }
+}
+
+typedef struct DRWBoundTexture {
+ struct DRWBoundTexture *next, *prev;
+ GPUTexture *tex;
+} DRWBoundTexture;
+
+static void draw_geometry_prepare(
+ DRWShadingGroup *shgroup, const float (*obmat)[4], const float *texcoloc, const float *texcosize)
+{
+ RegionView3D *rv3d = DST.draw_ctx.rv3d;
+ DRWInterface *interface = &shgroup->interface;
+
+ float mvp[4][4], mv[4][4], mi[4][4], mvi[4][4], pi[4][4], n[3][3], wn[3][3];
+ float orcofacs[2][3] = {{0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}};
+ float eye[3] = { 0.0f, 0.0f, 1.0f }; /* looking into the screen */
+ float viewcamtexcofac[4] = { 1.0f, 1.0f, 0.0f, 0.0f };
+
+ if (rv3d != NULL) {
+ copy_v4_v4(viewcamtexcofac, rv3d->viewcamtexcofac);
+ }
+
+ bool do_pi = (interface->projectioninverse != -1);
+ bool do_mvp = (interface->modelviewprojection != -1);
+ bool do_mi = (interface->modelinverse != -1);
+ bool do_mv = (interface->modelview != -1);
+ bool do_mvi = (interface->modelviewinverse != -1);
+ bool do_n = (interface->normal != -1);
+ bool do_wn = (interface->worldnormal != -1);
+ bool do_eye = (interface->eye != -1);
+ bool do_orco = (interface->orcotexfac != -1) && (texcoloc != NULL) && (texcosize != NULL);
+
+ /* Matrix override */
+ float (*persmat)[4];
+ float (*persinv)[4];
+ float (*viewmat)[4];
+ float (*viewinv)[4];
+ float (*winmat)[4];
+ float (*wininv)[4];
+
+ persmat = (viewport_matrix_override.override[DRW_MAT_PERS])
+ ? viewport_matrix_override.mat[DRW_MAT_PERS] : rv3d->persmat;
+ persinv = (viewport_matrix_override.override[DRW_MAT_PERSINV])
+ ? viewport_matrix_override.mat[DRW_MAT_PERSINV] : rv3d->persinv;
+ viewmat = (viewport_matrix_override.override[DRW_MAT_VIEW])
+ ? viewport_matrix_override.mat[DRW_MAT_VIEW] : rv3d->viewmat;
+ viewinv = (viewport_matrix_override.override[DRW_MAT_VIEWINV])
+ ? viewport_matrix_override.mat[DRW_MAT_VIEWINV] : rv3d->viewinv;
+ winmat = (viewport_matrix_override.override[DRW_MAT_WIN])
+ ? viewport_matrix_override.mat[DRW_MAT_WIN] : rv3d->winmat;
+ wininv = viewport_matrix_override.mat[DRW_MAT_WININV];
+
+ if (do_pi) {
+ if (!viewport_matrix_override.override[DRW_MAT_WININV]) {
+ invert_m4_m4(pi, winmat);
+ wininv = pi;
+ }
+ }
+ if (do_mi) {
+ invert_m4_m4(mi, obmat);
+ }
+ if (do_mvp) {
+ mul_m4_m4m4(mvp, persmat, obmat);
+ }
+ if (do_mv || do_mvi || do_n || do_eye) {
+ mul_m4_m4m4(mv, viewmat, obmat);
+ }
+ if (do_mvi) {
+ invert_m4_m4(mvi, mv);
+ }
+ if (do_n || do_eye) {
+ copy_m3_m4(n, mv);
+ invert_m3(n);
+ transpose_m3(n);
+ }
+ if (do_wn) {
+ copy_m3_m4(wn, obmat);
+ invert_m3(wn);
+ transpose_m3(wn);
+ }
+ if (do_eye) {
+ /* Used by orthographic wires */
+ float tmp[3][3];
+ invert_m3_m3(tmp, n);
+ /* set eye vector, transformed to object coords */
+ mul_m3_v3(tmp, eye);
+ }
+ if (do_orco) {
+ mul_v3_v3fl(orcofacs[1], texcosize, 2.0f);
+ invert_v3(orcofacs[1]);
+ sub_v3_v3v3(orcofacs[0], texcoloc, texcosize);
+ negate_v3(orcofacs[0]);
+ mul_v3_v3(orcofacs[0], orcofacs[1]); /* result in a nice MADD in the shader */
+ }
+
+ /* Should be really simple */
+ /* step 1 : bind object dependent matrices */
+ /* TODO : Some of these are not object dependant.
+ * They should be grouped inside a UBO updated once per redraw.
+ * The rest can also go into a UBO to reduce API calls. */
+ GPU_shader_uniform_vector(shgroup->shader, interface->model, 16, 1, (float *)obmat);
+ GPU_shader_uniform_vector(shgroup->shader, interface->modelinverse, 16, 1, (float *)mi);
+ GPU_shader_uniform_vector(shgroup->shader, interface->modelviewprojection, 16, 1, (float *)mvp);
+ GPU_shader_uniform_vector(shgroup->shader, interface->viewinverse, 16, 1, (float *)viewinv);
+ GPU_shader_uniform_vector(shgroup->shader, interface->viewprojection, 16, 1, (float *)persmat);
+ GPU_shader_uniform_vector(shgroup->shader, interface->viewprojectioninverse, 16, 1, (float *)persinv);
+ GPU_shader_uniform_vector(shgroup->shader, interface->projection, 16, 1, (float *)winmat);
+ GPU_shader_uniform_vector(shgroup->shader, interface->projectioninverse, 16, 1, (float *)wininv);
+ GPU_shader_uniform_vector(shgroup->shader, interface->view, 16, 1, (float *)viewmat);
+ GPU_shader_uniform_vector(shgroup->shader, interface->modelview, 16, 1, (float *)mv);
+ GPU_shader_uniform_vector(shgroup->shader, interface->modelviewinverse, 16, 1, (float *)mvi);
+ GPU_shader_uniform_vector(shgroup->shader, interface->normal, 9, 1, (float *)n);
+ GPU_shader_uniform_vector(shgroup->shader, interface->worldnormal, 9, 1, (float *)wn);
+ GPU_shader_uniform_vector(shgroup->shader, interface->camtexfac, 4, 1, (float *)viewcamtexcofac);
+ GPU_shader_uniform_vector(shgroup->shader, interface->orcotexfac, 3, 2, (float *)orcofacs);
+ GPU_shader_uniform_vector(shgroup->shader, interface->eye, 3, 1, (float *)eye);
+ GPU_shader_uniform_vector(shgroup->shader, interface->clipplanes, 4, DST.num_clip_planes, (float *)DST.clip_planes_eq);
+}
+
+static void draw_geometry_execute_ex(
+ DRWShadingGroup *shgroup, Gwn_Batch *geom, unsigned int start, unsigned int count)
+{
+ DRWInterface *interface = &shgroup->interface;
+ /* step 2 : bind vertex array & draw */
+ GWN_batch_program_set(geom, GPU_shader_get_program(shgroup->shader), GPU_shader_get_interface(shgroup->shader));
+ if (interface->instance_batch) {
+ /* Used for Particles. Cannot do partial drawing. */
+ GWN_batch_draw_stupid_instanced_with_batch(geom, interface->instance_batch);
+ }
+ else if (interface->instance_vbo) {
+ GWN_batch_draw_stupid_instanced(
+ geom, interface->instance_vbo, start, count, interface->attribs_count,
+ interface->attribs_stride, interface->attribs_size, interface->attribs_loc);
+ }
+ else {
+ GWN_batch_draw_stupid(geom, start, count);
+ }
+ /* XXX this just tells gawain we are done with the shader.
+ * This does not unbind the shader. */
+ GWN_batch_program_unset(geom);
+}
+
+static void draw_geometry_execute(DRWShadingGroup *shgroup, Gwn_Batch *geom)
+{
+ draw_geometry_execute_ex(shgroup, geom, 0, 0);
+}
+
+static void draw_geometry(
+ DRWShadingGroup *shgroup, Gwn_Batch *geom, const float (*obmat)[4], ID *ob_data,
+ unsigned int start, unsigned int count)
+{
+ float *texcoloc = NULL;
+ float *texcosize = NULL;
+
+ if (ob_data != NULL) {
+ switch (GS(ob_data->name)) {
+ case ID_ME:
+ BKE_mesh_texspace_get_reference((Mesh *)ob_data, NULL, &texcoloc, NULL, &texcosize);
+ break;
+ case ID_CU:
+ {
+ Curve *cu = (Curve *)ob_data;
+ if (cu->bb == NULL || (cu->bb->flag & BOUNDBOX_DIRTY)) {
+ BKE_curve_texspace_calc(cu);
+ }
+ texcoloc = cu->loc;
+ texcosize = cu->size;
+ break;
+ }
+ case ID_MB:
+ {
+ MetaBall *mb = (MetaBall *)ob_data;
+ texcoloc = mb->loc;
+ texcosize = mb->size;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ draw_geometry_prepare(shgroup, obmat, texcoloc, texcosize);
+
+ draw_geometry_execute_ex(shgroup, geom, start, count);
+}
+
+static void bind_texture(GPUTexture *tex)
+{
+ int bind_num = GPU_texture_bound_number(tex);
+ if (bind_num == -1) {
+ for (int i = 0; i < GPU_max_textures(); ++i) {
+ RST.bind_tex_inc = (RST.bind_tex_inc + 1) % GPU_max_textures();
+ if (RST.bound_tex_slots[RST.bind_tex_inc] == false) {
+ if (RST.bound_texs[RST.bind_tex_inc] != NULL) {
+ GPU_texture_unbind(RST.bound_texs[RST.bind_tex_inc]);
+ }
+ GPU_texture_bind(tex, RST.bind_tex_inc);
+ RST.bound_texs[RST.bind_tex_inc] = tex;
+ RST.bound_tex_slots[RST.bind_tex_inc] = true;
+ return;
+ }
+ }
+
+ printf("Not enough texture slots! Reduce number of textures used by your shader.\n");
+ }
+ RST.bound_tex_slots[bind_num] = true;
+}
+
+static void bind_ubo(GPUUniformBuffer *ubo)
+{
+ if (RST.bind_ubo_inc < GPU_max_ubo_binds()) {
+ GPU_uniformbuffer_bind(ubo, RST.bind_ubo_inc);
+ RST.bind_ubo_inc++;
+ }
+ else {
+ /* This is not depending on user input.
+ * It is our responsability to make sure there enough slots. */
+ BLI_assert(0 && "Not enough ubo slots! This should not happen!\n");
+
+ /* printf so user can report bad behaviour */
+ printf("Not enough ubo slots! This should not happen!\n");
+ }
+}
+
+static void release_texture_slots(void)
+{
+ memset(RST.bound_tex_slots, 0x0, sizeof(bool) * GPU_max_textures());
+}
+
+static void release_ubo_slots(void)
+{
+ RST.bind_ubo_inc = 0;
+}
+
+static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
+{
+ BLI_assert(shgroup->shader);
+
+ DRWInterface *interface = &shgroup->interface;
+ GPUTexture *tex;
+ GPUUniformBuffer *ubo;
+ int val;
+ float fval;
+
+ if (DST.shader != shgroup->shader) {
+ if (DST.shader) GPU_shader_unbind();
+ GPU_shader_bind(shgroup->shader);
+ DST.shader = shgroup->shader;
+ }
+
+ const bool is_normal = ELEM(shgroup->type, DRW_SHG_NORMAL);
+
+ if (!is_normal) {
+ shgroup_dynamic_batch_from_calls(shgroup);
+ }
+
+ release_texture_slots();
+ release_ubo_slots();
+
+ drw_state_set((pass_state & shgroup->state_extra_disable) | shgroup->state_extra);
+ drw_stencil_set(shgroup->stencil_mask);
+
+ /* Binding Uniform */
+ /* Don't check anything, Interface should already contain the least uniform as possible */
+ for (DRWUniform *uni = interface->uniforms; uni; uni = uni->next) {
+ switch (uni->type) {
+ case DRW_UNIFORM_SHORT_TO_INT:
+ val = (int)*((short *)uni->value);
+ GPU_shader_uniform_vector_int(
+ shgroup->shader, uni->location, uni->length, uni->arraysize, (int *)&val);
+ break;
+ case DRW_UNIFORM_SHORT_TO_FLOAT:
+ fval = (float)*((short *)uni->value);
+ GPU_shader_uniform_vector(
+ shgroup->shader, uni->location, uni->length, uni->arraysize, (float *)&fval);
+ break;
+ case DRW_UNIFORM_BOOL:
+ case DRW_UNIFORM_INT:
+ GPU_shader_uniform_vector_int(
+ shgroup->shader, uni->location, uni->length, uni->arraysize, (int *)uni->value);
+ break;
+ case DRW_UNIFORM_FLOAT:
+ case DRW_UNIFORM_MAT3:
+ case DRW_UNIFORM_MAT4:
+ GPU_shader_uniform_vector(
+ shgroup->shader, uni->location, uni->length, uni->arraysize, (float *)uni->value);
+ break;
+ case DRW_UNIFORM_TEXTURE:
+ tex = (GPUTexture *)uni->value;
+ BLI_assert(tex);
+ bind_texture(tex);
+ GPU_shader_uniform_texture(shgroup->shader, uni->location, tex);
+ break;
+ case DRW_UNIFORM_BUFFER:
+ if (!DRW_state_is_fbo()) {
+ break;
+ }
+ tex = *((GPUTexture **)uni->value);
+ BLI_assert(tex);
+ bind_texture(tex);
+ GPU_shader_uniform_texture(shgroup->shader, uni->location, tex);
+ break;
+ case DRW_UNIFORM_BLOCK:
+ ubo = (GPUUniformBuffer *)uni->value;
+ bind_ubo(ubo);
+ GPU_shader_uniform_buffer(shgroup->shader, uni->location, ubo);
+ break;
+ }
+ }
+
+#ifdef USE_GPU_SELECT
+ /* use the first item because of selection we only ever add one */
+# define GPU_SELECT_LOAD_IF_PICKSEL(_call) \
+ if ((G.f & G_PICKSEL) && (_call)) { \
+ GPU_select_load_id((_call)->head.select_id); \
+ } ((void)0)
+
+# define GPU_SELECT_LOAD_IF_PICKSEL_LIST(_shgroup, _start, _count) \
+ _start = 0; \
+ _count = _shgroup->interface.instance_count; \
+ int *select_id = NULL; \
+ if (G.f & G_PICKSEL) { \
+ if (_shgroup->interface.override_selectid == -1) { \
+ select_id = DRW_instance_data_get(_shgroup->interface.inst_selectid); \
+ switch (_shgroup->type) { \
+ case DRW_SHG_TRIANGLE_BATCH: _count = 3; break; \
+ case DRW_SHG_LINE_BATCH: _count = 2; break; \
+ default: _count = 1; break; \
+ } \
+ } \
+ else { \
+ GPU_select_load_id(_shgroup->interface.override_selectid); \
+ } \
+ } \
+ while (_start < _shgroup->interface.instance_count) { \
+ if (select_id) { \
+ GPU_select_load_id(select_id[_start]); \
+ }
+
+# define GPU_SELECT_LOAD_IF_PICKSEL_LIST_END(_start, _count) \
+ _start += _count; \
+ }
+
+#else
+# define GPU_SELECT_LOAD_IF_PICKSEL(call)
+# define GPU_SELECT_LOAD_IF_PICKSEL_LIST_END(start, count)
+# define GPU_SELECT_LOAD_IF_PICKSEL_LIST(_shgroup, _start, _count) \
+ _start = 0; \
+ _count = _shgroup->interface.instance_count;
+
+#endif
+
+ /* Rendering Calls */
+ if (!is_normal) {
+ /* Replacing multiple calls with only one */
+ float obmat[4][4];
+ unit_m4(obmat);
+
+ if (shgroup->type == DRW_SHG_INSTANCE &&
+ (interface->instance_count > 0 || interface->instance_batch != NULL))
+ {
+ if (interface->instance_batch != NULL) {
+ GPU_SELECT_LOAD_IF_PICKSEL((DRWCall *)shgroup->calls_first);
+ draw_geometry(shgroup, shgroup->instance_geom, obmat, shgroup->instance_data, 0, 0);
+ }
+ else {
+ unsigned int count, start;
+ GPU_SELECT_LOAD_IF_PICKSEL_LIST(shgroup, start, count)
+ {
+ draw_geometry(shgroup, shgroup->instance_geom, obmat, shgroup->instance_data, start, count);
+ }
+ GPU_SELECT_LOAD_IF_PICKSEL_LIST_END(start, count)
+ }
+ }
+ else {
+ /* Some dynamic batch can have no geom (no call to aggregate) */
+ if (shgroup->batch_geom) {
+ unsigned int count, start;
+ GPU_SELECT_LOAD_IF_PICKSEL_LIST(shgroup, start, count)
+ {
+ draw_geometry(shgroup, shgroup->batch_geom, obmat, NULL, start, count);
+ }
+ GPU_SELECT_LOAD_IF_PICKSEL_LIST_END(start, count)
+ }
+ }
+ }
+ else {
+ for (DRWCall *call = shgroup->calls_first; call; call = call->head.prev) {
+ bool neg_scale = is_negative_m4(call->obmat);
+
+ /* Negative scale objects */
+ if (neg_scale) {
+ glFrontFace(DST.backface);
+ }
+
+ GPU_SELECT_LOAD_IF_PICKSEL(call);
+
+ if (call->head.type == DRW_CALL_SINGLE) {
+ draw_geometry(shgroup, call->geometry, call->obmat, call->ob_data, 0, 0);
+ }
+ else {
+ BLI_assert(call->head.type == DRW_CALL_GENERATE);
+ DRWCallGenerate *callgen = ((DRWCallGenerate *)call);
+ draw_geometry_prepare(shgroup, callgen->obmat, NULL, NULL);
+ callgen->geometry_fn(shgroup, draw_geometry_execute, callgen->user_data);
+ }
+
+ /* Reset state */
+ if (neg_scale) {
+ glFrontFace(DST.frontface);
+ }
+ }
+ }
+
+ /* TODO: remove, (currently causes alpha issue with sculpt, need to investigate) */
+ DRW_state_reset();
+}
+
+static void drw_draw_pass_ex(DRWPass *pass, DRWShadingGroup *start_group, DRWShadingGroup *end_group)
+{
+ /* Start fresh */
+ DST.shader = NULL;
+
+ drw_state_set(pass->state);
+
+ DRW_stats_query_start(pass->name);
+
+ for (DRWShadingGroup *shgroup = start_group; shgroup; shgroup = shgroup->next) {
+ draw_shgroup(shgroup, pass->state);
+ /* break if upper limit */
+ if (shgroup == end_group) {
+ break;
+ }
+ }
+
+ /* Clear Bound textures */
+ for (int i = 0; i < GPU_max_textures(); i++) {
+ if (RST.bound_texs[i] != NULL) {
+ GPU_texture_unbind(RST.bound_texs[i]);
+ RST.bound_texs[i] = NULL;
+ }
+ }
+
+ if (DST.shader) {
+ GPU_shader_unbind();
+ DST.shader = NULL;
+ }
+
+ DRW_stats_query_end();
+}
+
+void DRW_draw_pass(DRWPass *pass)
+{
+ drw_draw_pass_ex(pass, pass->shgroups, pass->shgroups_last);
+}
+
+/* Draw only a subset of shgroups. Used in special situations as grease pencil strokes */
+void DRW_draw_pass_subset(DRWPass *pass, DRWShadingGroup *start_group, DRWShadingGroup *end_group)
+{
+ drw_draw_pass_ex(pass, start_group, end_group);
+}
+
+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);
+}
+
+/* Reset state to not interfer with other UI drawcall */
+void DRW_state_reset_ex(DRWState state)
+{
+ DST.state = ~state;
+ drw_state_set(state);
+}
+
+void DRW_state_reset(void)
+{
+ /* Reset blending function */
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ DRW_state_reset_ex(DRW_STATE_DEFAULT);
+}
+
+/* NOTE : Make sure to reset after use! */
+void DRW_state_invert_facing(void)
+{
+ SWAP(GLenum, DST.backface, DST.frontface);
+ glFrontFace(DST.frontface);
+}
+
+/**
+ * This only works if DRWPasses have been tagged with DRW_STATE_CLIP_PLANES,
+ * and if the shaders have support for it (see usage of gl_ClipDistance).
+ * Be sure to call DRW_state_clip_planes_reset() after you finish drawing.
+ **/
+void DRW_state_clip_planes_add(float plane_eq[4])
+{
+ copy_v4_v4(DST.clip_planes_eq[DST.num_clip_planes++], plane_eq);
+}
+
+void DRW_state_clip_planes_reset(void)
+{
+ DST.num_clip_planes = 0;
+}
+
+/** \} */
+
+
+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)
+{
+ Scene *scene = DST.draw_ctx.scene;
+ Object *obedit = scene->obedit;
+
+ BLI_assert(BKE_object_is_visible(ob, OB_VISIBILITY_CHECK_UNKNOWN_RENDER_MODE));
+
+ if (ob->type == OB_MESH) {
+ if (ob == obedit) {
+ IDProperty *props = BKE_layer_collection_engine_evaluated_get(ob, COLLECTION_MODE_EDIT, "");
+ bool do_show_occlude_wire = BKE_collection_engine_property_value_get_bool(props, "show_occlude_wire");
+ if (do_show_occlude_wire) {
+ return false;
+ }
+ bool do_show_weight = BKE_collection_engine_property_value_get_bool(props, "show_weight");
+ if (do_show_weight) {
+ 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;
+}
+
+/**
+ * Return true if the object has its own draw mode.
+ * Caller must check this is active */
+int DRW_object_is_mode_shade(const Object *ob)
+{
+ BLI_assert(ob == DST.draw_ctx.obact);
+ if ((ob->mode & OB_MODE_EDIT) == 0) {
+ if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) {
+ if ((DST.draw_ctx.v3d->flag2 & V3D_SHOW_MODE_SHADE_OVERRIDE) == 0) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ }
+ return -1;
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Framebuffers (DRW_framebuffer)
+ * \{ */
+
+static GPUTextureFormat convert_tex_format(
+ int fbo_format,
+ int *r_channels, bool *r_is_depth)
+{
+ *r_is_depth = ELEM(fbo_format, DRW_TEX_DEPTH_16, DRW_TEX_DEPTH_24, DRW_TEX_DEPTH_24_STENCIL_8);
+
+ switch (fbo_format) {
+ case DRW_TEX_R_16: *r_channels = 1; return GPU_R16F;
+ case DRW_TEX_R_32: *r_channels = 1; return GPU_R32F;
+ case DRW_TEX_RG_8: *r_channels = 2; return GPU_RG8;
+ case DRW_TEX_RG_16: *r_channels = 2; return GPU_RG16F;
+ case DRW_TEX_RG_16I: *r_channels = 2; return GPU_RG16I;
+ case DRW_TEX_RG_32: *r_channels = 2; return GPU_RG32F;
+ case DRW_TEX_RGBA_8: *r_channels = 4; return GPU_RGBA8;
+ case DRW_TEX_RGBA_16: *r_channels = 4; return GPU_RGBA16F;
+ case DRW_TEX_RGBA_32: *r_channels = 4; return GPU_RGBA32F;
+ case DRW_TEX_DEPTH_16: *r_channels = 1; return GPU_DEPTH_COMPONENT16;
+ case DRW_TEX_DEPTH_24: *r_channels = 1; return GPU_DEPTH_COMPONENT24;
+ case DRW_TEX_DEPTH_24_STENCIL_8: *r_channels = 1; return GPU_DEPTH24_STENCIL8;
+ case DRW_TEX_DEPTH_32: *r_channels = 1; return GPU_DEPTH_COMPONENT32F;
+ case DRW_TEX_RGB_11_11_10: *r_channels = 3; return GPU_R11F_G11F_B10F;
+ default:
+ BLI_assert(false && "Texture format unsupported as render target!");
+ *r_channels = 4; return GPU_RGBA8;
+ }
+}
+
+struct GPUFrameBuffer *DRW_framebuffer_create(void)
+{
+ return GPU_framebuffer_create();
+}
+
+void DRW_framebuffer_init(
+ struct GPUFrameBuffer **fb, void *engine_type, int width, int height,
+ DRWFboTexture textures[MAX_FBO_TEX], int textures_len)
+{
+ BLI_assert(textures_len <= MAX_FBO_TEX);
+ BLI_assert(width > 0 && height > 0);
+
+ bool create_fb = false;
+ int color_attachment = -1;
+
+ if (!*fb) {
+ *fb = GPU_framebuffer_create();
+ create_fb = true;
+ }
+
+ for (int i = 0; i < textures_len; ++i) {
+ int channels;
+ bool is_depth;
+ bool create_tex = false;
+
+ DRWFboTexture fbotex = textures[i];
+ bool is_temp = (fbotex.flag & DRW_TEX_TEMP) != 0;
+
+ GPUTextureFormat gpu_format = convert_tex_format(fbotex.format, &channels, &is_depth);
+
+ if (!*fbotex.tex || is_temp) {
+ /* Temp textures need to be queried each frame, others not. */
+ if (is_temp) {
+ *fbotex.tex = GPU_viewport_texture_pool_query(
+ DST.viewport, engine_type, width, height, channels, gpu_format);
+ }
+ else {
+ *fbotex.tex = GPU_texture_create_2D_custom(
+ width, height, channels, gpu_format, NULL, NULL);
+ create_tex = true;
+ }
+ }
+
+ if (!is_depth) {
+ ++color_attachment;
+ }
+
+ if (create_fb || create_tex) {
+ drw_texture_set_parameters(*fbotex.tex, fbotex.flag);
+ GPU_framebuffer_texture_attach(*fb, *fbotex.tex, color_attachment, 0);
+ }
+ }
+
+ if (create_fb && (textures_len > 0)) {
+ if (!GPU_framebuffer_check_valid(*fb, NULL)) {
+ printf("Error invalid framebuffer\n");
+ }
+
+ /* Detach temp textures */
+ for (int i = 0; i < textures_len; ++i) {
+ DRWFboTexture fbotex = textures[i];
+
+ if ((fbotex.flag & DRW_TEX_TEMP) != 0) {
+ GPU_framebuffer_texture_detach(*fbotex.tex);
+ }
+ }
+
+ if (DST.default_framebuffer != NULL) {
+ GPU_framebuffer_bind(DST.default_framebuffer);
+ }
+ }
+}
+
+void DRW_framebuffer_free(struct GPUFrameBuffer *fb)
+{
+ GPU_framebuffer_free(fb);
+}
+
+void DRW_framebuffer_bind(struct GPUFrameBuffer *fb)
+{
+ GPU_framebuffer_bind(fb);
+}
+
+void DRW_framebuffer_clear(bool color, bool depth, bool stencil, float clear_col[4], float clear_depth)
+{
+ if (color) {
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glClearColor(clear_col[0], clear_col[1], clear_col[2], clear_col[3]);
+ }
+ if (depth) {
+ glDepthMask(GL_TRUE);
+ glClearDepth(clear_depth);
+ }
+ if (stencil) {
+ glStencilMask(0xFF);
+ }
+ glClear(((color) ? GL_COLOR_BUFFER_BIT : 0) |
+ ((depth) ? GL_DEPTH_BUFFER_BIT : 0) |
+ ((stencil) ? GL_STENCIL_BUFFER_BIT : 0));
+}
+
+void DRW_framebuffer_read_data(int x, int y, int w, int h, int channels, int slot, float *data)
+{
+ GLenum type;
+ switch (channels) {
+ case 1: type = GL_RED; break;
+ case 2: type = GL_RG; break;
+ case 3: type = GL_RGB; break;
+ case 4: type = GL_RGBA; break;
+ default:
+ BLI_assert(false && "wrong number of read channels");
+ return;
+ }
+ glReadBuffer(GL_COLOR_ATTACHMENT0 + slot);
+ glReadPixels(x, y, w, h, type, GL_FLOAT, data);
+}
+
+void DRW_framebuffer_read_depth(int x, int y, int w, int h, float *data)
+{
+ GLenum type = GL_DEPTH_COMPONENT;
+
+ glReadBuffer(GL_COLOR_ATTACHMENT0); /* This is OK! */
+ glReadPixels(x, y, w, h, type, GL_FLOAT, data);
+}
+
+void DRW_framebuffer_texture_attach(struct GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip)
+{
+ GPU_framebuffer_texture_attach(fb, tex, slot, mip);
+}
+
+void DRW_framebuffer_texture_layer_attach(struct GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int layer, int mip)
+{
+ GPU_framebuffer_texture_layer_attach(fb, tex, slot, layer, mip);
+}
+
+void DRW_framebuffer_cubeface_attach(struct GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip)
+{
+ GPU_framebuffer_texture_cubeface_attach(fb, tex, slot, face, mip);
+}
+
+void DRW_framebuffer_texture_detach(GPUTexture *tex)
+{
+ GPU_framebuffer_texture_detach(tex);
+}
+
+void DRW_framebuffer_blit(struct GPUFrameBuffer *fb_read, struct GPUFrameBuffer *fb_write, bool depth, bool stencil)
+{
+ GPU_framebuffer_blit(fb_read, 0, fb_write, 0, depth, stencil);
+}
+
+void DRW_framebuffer_recursive_downsample(
+ struct GPUFrameBuffer *fb, struct GPUTexture *tex, int num_iter,
+ void (*callback)(void *userData, int level), void *userData)
+{
+ GPU_framebuffer_recursive_downsample(fb, tex, num_iter, callback, userData);
+}
+
+void DRW_framebuffer_viewport_size(struct GPUFrameBuffer *UNUSED(fb_read), int x, int y, int w, int h)
+{
+ glViewport(x, y, w, h);
+}
+
+/* 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();
+ unsigned int pos = GWN_vertformat_attr_add(vert_format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+ unsigned int 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 Viewport (DRW_viewport)
+ * \{ */
+
+static 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[0];
+}
+
+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_count(DST.vmempool->calls));
+ BLI_mempool_clear_ex(DST.vmempool->calls_generate, BLI_mempool_count(DST.vmempool->calls_generate));
+ BLI_mempool_clear_ex(DST.vmempool->shgroups, BLI_mempool_count(DST.vmempool->shgroups));
+ BLI_mempool_clear_ex(DST.vmempool->uniforms, BLI_mempool_count(DST.vmempool->uniforms));
+ BLI_mempool_clear_ex(DST.vmempool->passes, BLI_mempool_count(DST.vmempool->passes));
+ }
+
+ DRW_instance_data_list_free_unused(DST.idatalist);
+ DRW_instance_data_list_resize(DST.idatalist);
+}
+
+/* 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];
+
+ 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->calls_generate == NULL) {
+ DST.vmempool->calls_generate = BLI_mempool_create(sizeof(DRWCallGenerate), 0, 512, 0);
+ }
+ 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.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;
+ }
+
+ /* Reset facing */
+ DST.frontface = GL_CCW;
+ DST.backface = GL_CW;
+ glFrontFace(DST.frontface);
+
+ if (DST.draw_ctx.scene->obedit) {
+ ED_view3d_init_mats_rv3d(DST.draw_ctx.scene->obedit, rv3d);
+ }
+
+ /* Alloc array of texture reference. */
+ if (RST.bound_texs == NULL) {
+ RST.bound_texs = MEM_callocN(sizeof(GPUTexture *) * GPU_max_textures(), "Bound GPUTexture refs");
+ }
+ if (RST.bound_tex_slots == NULL) {
+ RST.bound_tex_slots = MEM_callocN(sizeof(bool) * GPU_max_textures(), "Bound Texture Slots");
+ }
+
+ memset(viewport_matrix_override.override, 0x0, sizeof(viewport_matrix_override.override));
+}
+
+void DRW_viewport_matrix_get(float mat[4][4], DRWViewportMatrixType type)
+{
+ RegionView3D *rv3d = DST.draw_ctx.rv3d;
+ BLI_assert(type >= DRW_MAT_PERS && type <= DRW_MAT_WININV);
+
+ if (viewport_matrix_override.override[type]) {
+ copy_m4_m4(mat, viewport_matrix_override.mat[type]);
+ }
+ else {
+ BLI_assert(rv3d != NULL); /* Can't use this in render mode. */
+ switch (type) {
+ case DRW_MAT_PERS:
+ copy_m4_m4(mat, rv3d->persmat);
+ break;
+ case DRW_MAT_PERSINV:
+ copy_m4_m4(mat, rv3d->persinv);
+ break;
+ case DRW_MAT_VIEW:
+ copy_m4_m4(mat, rv3d->viewmat);
+ break;
+ case DRW_MAT_VIEWINV:
+ copy_m4_m4(mat, rv3d->viewinv);
+ break;
+ case DRW_MAT_WIN:
+ copy_m4_m4(mat, rv3d->winmat);
+ break;
+ case DRW_MAT_WININV:
+ invert_m4_m4(mat, rv3d->winmat);
+ break;
+ default:
+ BLI_assert(!"Matrix type invalid");
+ break;
+ }
+ }
+}
+
+void DRW_viewport_matrix_override_set(float mat[4][4], DRWViewportMatrixType type)
+{
+ copy_m4_m4(viewport_matrix_override.mat[type], mat);
+ viewport_matrix_override.override[type] = true;
+}
+
+void DRW_viewport_matrix_override_unset(DRWViewportMatrixType type)
+{
+ viewport_matrix_override.override[type] = false;
+}
+
+bool DRW_viewport_is_persp_get(void)
+{
+ RegionView3D *rv3d = DST.draw_ctx.rv3d;
+ if (rv3d) {
+ return rv3d->is_persp;
+ }
+ else {
+ if (viewport_matrix_override.override[DRW_MAT_WIN]) {
+ return viewport_matrix_override.mat[DRW_MAT_WIN][3][3] == 0.0f;
+ }
+ }
+ BLI_assert(0);
+ return false;
+}
+
+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(DrawEngineType *engine_type, void (*callback)(void *storage))
+{
+ ViewLayerEngineData *sled;
+
+ for (sled = DST.draw_ctx.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(&DST.draw_ctx.view_layer->drawdata, sled);
+
+ return &sled->storage;
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \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. */
+ 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;
+}
+
+/* XXX There is definitly some overlap between this and DRW_object_engine_data_ensure.
+ * We should get rid of one of the two. */
+LampEngineData *DRW_lamp_engine_data_ensure(Object *ob, RenderEngineType *engine_type)
+{
+ BLI_assert(ob->type == OB_LAMP);
+
+ Scene *scene = DST.draw_ctx.scene;
+
+ /* TODO Dupliobjects */
+ /* TODO Should be per scenelayer */
+ return GPU_lamp_engine_data_get(scene, ob, NULL, engine_type);
+}
+
+void DRW_lamp_engine_data_free(LampEngineData *led)
+{
+ GPU_lamp_engine_data_free(led);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \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)
+{
+ 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);
+ 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);
+}
+
+/* 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)
+{
+ /* 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();
+ }
+}
+
+static void drw_engines_enable_from_object_mode(void)
+{
+ use_drw_engine(&draw_engine_object_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;
+ }
+}
+
+/**
+ * Use for select and depth-drawing.
+ */
+static void drw_engines_enable_basic(void)
+{
+ use_drw_engine(DRW_engine_viewport_basic_type.draw_engine);
+}
+
+/**
+ * Use for external render engines.
+ */
+static void drw_engines_enable_external(void)
+{
+ use_drw_engine(DRW_engine_viewport_external_type.draw_engine);
+}
+
+static void drw_engines_enable(const Scene *scene, ViewLayer *view_layer, RenderEngineType *engine_type)
+{
+ Object *obact = OBACT(view_layer);
+ const int mode = CTX_data_mode_enum_ex(scene->obedit, obact);
+
+ drw_engines_enable_from_engine(engine_type);
+
+ if (DRW_state_draw_support()) {
+ drw_engines_enable_from_object_mode();
+ drw_engines_enable_from_mode(mode);
+ }
+}
+
+static void drw_engines_disable(void)
+{
+ BLI_freelistN(&DST.enabled_engines);
+}
+
+static unsigned int DRW_engines_get_hash(void)
+{
+ unsigned int 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;
+}
+
+static void draw_stat(rcti *rect, int u, int v, const char *txt, const int size)
+{
+ BLF_draw_default_ascii(rect->xmin + (1 + u * 5) * U.widget_unit,
+ rect->ymax - (3 + v) * U.widget_unit, 0.0f,
+ txt, size);
+}
+
+/* CPU stats */
+static void drw_debug_cpu_stats(void)
+{
+ int u, v;
+ double init_tot_time = 0.0, background_tot_time = 0.0, render_tot_time = 0.0, tot_time = 0.0;
+ /* local coordinate visible rect inside region, to accomodate overlapping ui */
+ rcti rect;
+ struct ARegion *ar = DST.draw_ctx.ar;
+ ED_region_visible_rect(ar, &rect);
+
+ UI_FontThemeColor(BLF_default(), TH_TEXT_HI);
+
+ /* row by row */
+ v = 0; u = 0;
+ /* Label row */
+ char col_label[32];
+ sprintf(col_label, "Engine");
+ draw_stat(&rect, u++, v, col_label, sizeof(col_label));
+ sprintf(col_label, "Init");
+ draw_stat(&rect, u++, v, col_label, sizeof(col_label));
+ sprintf(col_label, "Background");
+ draw_stat(&rect, u++, v, col_label, sizeof(col_label));
+ sprintf(col_label, "Render");
+ draw_stat(&rect, u++, v, col_label, sizeof(col_label));
+ sprintf(col_label, "Total (w/o cache)");
+ draw_stat(&rect, u++, v, col_label, sizeof(col_label));
+ v++;
+
+ /* Engines rows */
+ char time_to_txt[16];
+ for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
+ u = 0;
+ DrawEngineType *engine = link->data;
+ ViewportEngineData *data = DRW_viewport_engine_data_ensure(engine);
+
+ draw_stat(&rect, u++, v, engine->idname, sizeof(engine->idname));
+
+ init_tot_time += data->init_time;
+ sprintf(time_to_txt, "%.2fms", data->init_time);
+ draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
+
+ background_tot_time += data->background_time;
+ sprintf(time_to_txt, "%.2fms", data->background_time);
+ draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
+
+ render_tot_time += data->render_time;
+ sprintf(time_to_txt, "%.2fms", data->render_time);
+ draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
+
+ tot_time += data->init_time + data->background_time + data->render_time;
+ sprintf(time_to_txt, "%.2fms", data->init_time + data->background_time + data->render_time);
+ draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
+ v++;
+ }
+
+ /* Totals row */
+ u = 0;
+ sprintf(col_label, "Sub Total");
+ draw_stat(&rect, u++, v, col_label, sizeof(col_label));
+ sprintf(time_to_txt, "%.2fms", init_tot_time);
+ draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
+ sprintf(time_to_txt, "%.2fms", background_tot_time);
+ draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
+ sprintf(time_to_txt, "%.2fms", render_tot_time);
+ draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
+ sprintf(time_to_txt, "%.2fms", tot_time);
+ draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
+ v += 2;
+
+ u = 0;
+ sprintf(col_label, "Cache Time");
+ draw_stat(&rect, u++, v, col_label, sizeof(col_label));
+ sprintf(time_to_txt, "%.2fms", DST.cache_time);
+ draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
+}
+
+/* Display GPU time for each passes */
+static void drw_debug_gpu_stats(void)
+{
+ /* local coordinate visible rect inside region, to accomodate overlapping ui */
+ rcti rect;
+ struct ARegion *ar = DST.draw_ctx.ar;
+ ED_region_visible_rect(ar, &rect);
+
+ UI_FontThemeColor(BLF_default(), TH_TEXT_HI);
+
+ int v = BLI_listbase_count(&DST.enabled_engines) + 5;
+
+ char stat_string[32];
+
+ /* Memory Stats */
+ unsigned int tex_mem = GPU_texture_memory_usage_get();
+ unsigned int vbo_mem = GWN_vertbuf_get_memory_usage();
+
+ sprintf(stat_string, "GPU Memory");
+ draw_stat(&rect, 0, v, stat_string, sizeof(stat_string));
+ sprintf(stat_string, "%.2fMB", (double)(tex_mem + vbo_mem) / 1000000.0);
+ draw_stat(&rect, 1, v++, stat_string, sizeof(stat_string));
+ sprintf(stat_string, " |--> Textures");
+ draw_stat(&rect, 0, v, stat_string, sizeof(stat_string));
+ sprintf(stat_string, "%.2fMB", (double)tex_mem / 1000000.0);
+ draw_stat(&rect, 1, v++, stat_string, sizeof(stat_string));
+ sprintf(stat_string, " |--> Meshes");
+ draw_stat(&rect, 0, v, stat_string, sizeof(stat_string));
+ sprintf(stat_string, "%.2fMB", (double)vbo_mem / 1000000.0);
+ draw_stat(&rect, 1, v++, stat_string, sizeof(stat_string));
+
+ /* Pre offset for stats_draw */
+ rect.ymax -= (3 + ++v) * U.widget_unit;
+
+ /* Rendering Stats */
+ DRW_stats_draw(&rect);
+}
+
+/* -------------------------------------------------------------------- */
+
+/** \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;
+
+ if (rv3d->viewport == NULL) {
+ return;
+ }
+
+
+ /* Reset before using it. */
+ memset(&DST, 0x0, sizeof(DST));
+
+ DST.viewport = rv3d->viewport;
+ DST.draw_ctx = (DRWContextState){
+ ar, rv3d, v3d, scene, view_layer, OBACT(view_layer), engine_type, depsgraph,
+ NULL,
+ };
+
+ drw_engines_enable(scene, 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();
+}
+
+/** \} */
+
+/** \name ID Update
+ * \{ */
+
+/* TODO(sergey): This code is run for each changed ID (including the ones which
+ * are changed indirectly via update flush. Need to find a way to make this to
+ * run really fast, hopefully without any memory allocations on a heap
+ * Idea here could be to run every known engine's id_update() and make them
+ * do nothing if there is no engine-specific data yet.
+ */
+void DRW_notify_id_update(const DRWUpdateContext *update_ctx, ID *id)
+{
+ 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;
+ if (rv3d->viewport == NULL) {
+ return;
+ }
+ /* Reset before using it. */
+ memset(&DST, 0x0, sizeof(DST));
+ DST.viewport = rv3d->viewport;
+ DST.draw_ctx = (DRWContextState){
+ ar, rv3d, v3d, scene, view_layer, OBACT(view_layer), engine_type, depsgraph, NULL,
+ };
+ drw_engines_enable(scene, 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->id_update) {
+ draw_engine->id_update(data, id);
+ }
+ }
+ DST.viewport = NULL;
+ drw_engines_disable();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+
+/** \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)
+{
+ struct Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ RenderEngineType *engine_type = CTX_data_engine_type(C);
+ ARegion *ar = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+
+ /* Reset before using it. */
+ memset(&DST, 0x0, sizeof(DST));
+ DRW_draw_render_loop_ex(depsgraph, engine_type, ar, v3d, 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,
+ 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 = rv3d->viewport;
+ v3d->zbuf = true;
+
+ /* Setup viewport */
+ GPU_viewport_engines_data_validate(DST.viewport, DRW_engines_get_hash());
+
+ DST.draw_ctx = (DRWContextState){
+ ar, rv3d, v3d, scene, view_layer, OBACT(view_layer), engine_type, depsgraph,
+
+ /* reuse if caller sets */
+ DST.draw_ctx.evil_C,
+ };
+
+ drw_viewport_var_init();
+
+ /* Get list of enabled engines */
+ drw_engines_enable(scene, view_layer, engine_type);
+
+ /* Update ubos */
+ DRW_globals_update();
+
+ /* Init engines */
+ drw_engines_init();
+
+ /* Cache filling */
+ {
+ PROFILE_START(stime);
+ drw_engines_cache_init();
+
+ DEG_OBJECT_ITER_FOR_RENDER_ENGINE(depsgraph, ob, DRW_iterator_mode_get())
+ {
+ drw_engines_cache_populate(ob);
+ }
+ DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END
+
+ drw_engines_cache_finish();
+ PROFILE_END_ACCUM(DST.cache_time, stime);
+ }
+
+ DRW_stats_begin();
+
+ /* Start Drawing */
+ DRW_state_reset();
+
+ 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_engines_draw_text();
+
+ if (DST.draw_ctx.evil_C) {
+ /* needed so manipulator isn't obscured */
+ glDisable(GL_DEPTH_TEST);
+ DRW_draw_manipulator_3d();
+
+ DRW_draw_region_info();
+
+ /* 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) {
+ drw_debug_cpu_stats();
+ drw_debug_gpu_stats();
+ }
+
+ DRW_state_reset();
+ drw_engines_disable();
+
+ drw_viewport_cache_resize();
+
+#ifdef DEBUG
+ /* Avoid accidental reuse. */
+ memset(&DST, 0xFF, sizeof(DST));
+#endif
+}
+
+void DRW_draw_render_loop(
+ struct Depsgraph *depsgraph,
+ ARegion *ar, View3D *v3d)
+{
+ /* Reset before using it. */
+ memset(&DST, 0x0, sizeof(DST));
+
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ RenderEngineType *engine_type = RE_engines_find(scene->view_render.engine_id);
+
+ DRW_draw_render_loop_ex(depsgraph, engine_type, ar, v3d, 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)
+{
+ RegionView3D *rv3d = ar->regiondata;
+
+ /* backup */
+ void *backup_viewport = rv3d->viewport;
+ {
+ /* backup (_never_ use rv3d->viewport) */
+ if (viewport == NULL) {
+ rv3d->viewport = GPU_viewport_create_from_offscreen(ofs);
+ }
+ else {
+ rv3d->viewport = viewport;
+ }
+ }
+
+ /* Reset before using it. */
+ memset(&DST, 0x0, sizeof(DST));
+ DST.options.is_image_render = true;
+ DST.options.draw_background = draw_background;
+ DRW_draw_render_loop_ex(depsgraph, engine_type, ar, v3d, NULL);
+
+ /* restore */
+ {
+ if (viewport == NULL) {
+ /* don't free data owned by 'ofs' */
+ GPU_viewport_clear_from_offscreen(rv3d->viewport);
+ GPU_viewport_free(rv3d->viewport);
+ MEM_freeN(rv3d->viewport);
+ }
+
+ rv3d->viewport = backup_viewport;
+ }
+
+ /* we need to re-bind (annoying!) */
+ GPU_offscreen_bind(ofs, false);
+}
+
+void DRW_render_to_image(RenderEngine *re, struct Depsgraph *depsgraph)
+{
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
+ RenderEngineType *engine_type = re->type;
+ DrawEngineType *draw_engine_type = engine_type->draw_engine;
+ RenderData *r = &scene->r;
+ Render *render = re->re;
+
+ /* Reset before using it. */
+ memset(&DST, 0x0, sizeof(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){
+ NULL, NULL, NULL, scene, view_layer, OBACT(view_layer), engine_type, depsgraph, NULL
+ };
+
+ 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 */
+ gpuPushAttrib(GPU_ENABLE_BIT | GPU_VIEWPORT_BIT);
+ glDisable(GL_SCISSOR_TEST);
+ glViewport(0, 0, size[0], size[1]);
+
+ if ((r->scemode & R_MULTIVIEW) != 0) {
+ for (SceneRenderView *srv = r->views.first; srv; srv = srv->next) {
+ if (BKE_scene_multiview_is_render_view_active(r, srv) == false)
+ continue;
+
+ RE_SetActiveRenderView(render, srv->name);
+
+ engine_type->draw_engine->render_to_image(data, re, depsgraph);
+ }
+ }
+ else {
+ engine_type->draw_engine->render_to_image(data, re, depsgraph);
+ }
+
+ /* TODO grease pencil */
+
+ GPU_viewport_free(DST.viewport);
+ MEM_freeN(DST.viewport);
+
+ DRW_state_reset();
+ /* FIXME GL_DEPTH_TEST is enabled by default but it seems
+ * to trigger some bad behaviour / artifacts if it's turned
+ * on at this point. */
+ glDisable(GL_DEPTH_TEST);
+
+ /* Restore Drawing area. */
+ gpuPopAttrib();
+ glEnable(GL_SCISSOR_TEST);
+ GPU_framebuffer_restore();
+
+#ifdef DEBUG
+ /* Avoid accidental reuse. */
+ memset(&DST, 0xFF, sizeof(DST));
+#endif
+}
+
+void DRW_render_object_iter(
+ void *vedata, RenderEngine *engine, struct Depsgraph *depsgraph,
+ void (*callback)(void *vedata, Object *ob, RenderEngine *engine, struct Depsgraph *depsgraph))
+{
+ DEG_OBJECT_ITER_FOR_RENDER_ENGINE(depsgraph, ob, DRW_iterator_mode_get())
+ {
+ callback(vedata, ob, engine, depsgraph);
+ }
+ DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END
+}
+
+/**
+ * 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)
+{
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ RenderEngineType *engine_type = RE_engines_find(scene->view_render.engine_id);
+ ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
+#ifndef USE_GPU_SELECT
+ UNUSED_VARS(vc, scene, view_layer, v3d, ar, rect);
+#else
+ RegionView3D *rv3d = ar->regiondata;
+
+ /* Reset before using it. */
+ memset(&DST, 0x0, sizeof(DST));
+
+ /* backup (_never_ use rv3d->viewport) */
+ void *backup_viewport = rv3d->viewport;
+ rv3d->viewport = NULL;
+
+ bool use_obedit = false;
+ int obedit_mode = 0;
+ if (scene->obedit && scene->obedit->type == OB_MBALL) {
+ use_obedit = true;
+ obedit_mode = CTX_MODE_EDIT_METABALL;
+ }
+ else if ((scene->obedit && scene->obedit->type == OB_ARMATURE)) {
+ /* if not drawing sketch, draw bones */
+ // if (!BDR_drawSketchNames(vc))
+ {
+ use_obedit = true;
+ obedit_mode = CTX_MODE_EDIT_ARMATURE;
+ }
+ }
+
+ struct GPUViewport *viewport = GPU_viewport_create();
+ GPU_viewport_size_set(viewport, (const int[2]){BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)});
+
+ bool cache_is_dirty;
+ 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 */
+ cache_is_dirty = true;
+
+ /* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */
+ DST.draw_ctx = (DRWContextState){
+ ar, rv3d, v3d, scene, view_layer, OBACT(view_layer), engine_type, depsgraph, (bContext *)NULL,
+ };
+
+ drw_viewport_var_init();
+
+ /* Update ubos */
+ DRW_globals_update();
+
+ /* Init engines */
+ drw_engines_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();
+
+ if (use_obedit) {
+ drw_engines_cache_populate(scene->obedit);
+ }
+ else {
+ DEG_OBJECT_ITER(depsgraph, ob, DRW_iterator_mode_get(),
+ DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY |
+ DEG_ITER_OBJECT_FLAG_VISIBLE |
+ DEG_ITER_OBJECT_FLAG_DUPLI)
+ {
+ if ((ob->base_flag & BASE_SELECTABLED) != 0) {
+ DRW_select_load_id(ob->select_color);
+ drw_engines_cache_populate(ob);
+ }
+ }
+ DEG_OBJECT_ITER_END
+ }
+
+ drw_engines_cache_finish();
+ }
+
+ /* Start Drawing */
+ DRW_state_reset();
+ 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. */
+ memset(&DST, 0xFF, sizeof(DST));
+#endif
+
+ /* Cleanup for selection state */
+ GPU_viewport_free(viewport);
+ MEM_freeN(viewport);
+
+ /* restore */
+ rv3d->viewport = backup_viewport;
+#endif /* USE_GPU_SELECT */
+}
+
+/**
+ * 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 = RE_engines_find(scene->view_render.engine_id);
+ ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
+ RegionView3D *rv3d = ar->regiondata;
+
+ /* backup (_never_ use rv3d->viewport) */
+ void *backup_viewport = rv3d->viewport;
+ rv3d->viewport = NULL;
+
+ /* Reset before using it. */
+ memset(&DST, 0x0, sizeof(DST));
+
+ struct GPUViewport *viewport = GPU_viewport_create();
+ GPU_viewport_size_set(viewport, (const int[2]){ar->winx, ar->winy});
+
+ 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, rv3d, v3d, scene, view_layer, OBACT(view_layer), engine_type, depsgraph, (bContext *)NULL,
+ };
+
+ drw_viewport_var_init();
+
+ /* Update ubos */
+ DRW_globals_update();
+
+ /* Init engines */
+ drw_engines_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(depsgraph, ob, DRW_iterator_mode_get())
+ {
+ drw_engines_cache_populate(ob);
+ }
+ DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END
+
+ drw_engines_cache_finish();
+ }
+
+ /* Start Drawing */
+ DRW_state_reset();
+ 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. */
+ memset(&DST, 0xFF, sizeof(DST));
+#endif
+
+ /* Cleanup for selection state */
+ GPU_viewport_free(viewport);
+ MEM_freeN(viewport);
+
+ /* restore */
+ rv3d->viewport = backup_viewport;
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \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;
+}
+
+/**
+ * Gives you the iterator mode to use for depsgraph.
+ */
+eDepsObjectIteratorMode DRW_iterator_mode_get(void)
+{
+ return DRW_state_is_scene_render() ? DEG_ITER_OBJECT_MODE_RENDER :
+ DEG_ITER_OBJECT_MODE_VIEWPORT;
+}
+
+/**
+ * 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;
+}
+
+/**
+ * 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)
+{
+#ifdef WITH_CLAY_ENGINE
+ RE_engines_register(NULL, &DRW_engine_viewport_clay_type);
+#endif
+ RE_engines_register(NULL, &DRW_engine_viewport_eevee_type);
+
+ 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_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 GPUUniformBuffer *globals_ubo; /* draw_common.c */
+extern struct GPUTexture *globals_ramp; /* draw_common.c */
+void DRW_engines_free(void)
+{
+ DRW_shape_cache_free();
+ DRW_stats_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();
+ }
+ }
+
+ if (globals_ubo)
+ GPU_uniformbuffer_free(globals_ubo);
+
+ if (globals_ramp)
+ GPU_texture_free(globals_ramp);
+
+ MEM_SAFE_FREE(RST.bound_texs);
+ MEM_SAFE_FREE(RST.bound_tex_slots);
+
+#ifdef WITH_CLAY_ENGINE
+ BLI_remlink(&R_engines, &DRW_engine_viewport_clay_type);
+#endif
+}
+
+/** \} */