DRW: Add DRWView to improve different view handling
authorClément Foucault <foucault.clem@gmail.com>
Mon, 20 May 2019 16:01:42 +0000 (18:01 +0200)
committerClément Foucault <foucault.clem@gmail.com>
Wed, 22 May 2019 11:29:04 +0000 (13:29 +0200)
This will have multiple benefit.

TODO detail benefits (culling, more explicit, handling of clipping planes)

For now the view usage is wrapped to make changes needed more progressive.

source/blender/draw/engines/eevee/eevee_materials.c
source/blender/draw/intern/DRW_render.h
source/blender/draw/intern/draw_manager.c
source/blender/draw/intern/draw_manager.h
source/blender/draw/intern/draw_manager_data.c
source/blender/draw/intern/draw_manager_exec.c
source/blender/draw/modes/edit_mesh_mode.c
source/blender/draw/modes/shaders/common_view_lib.glsl
source/blender/gpu/GPU_viewport.h
source/blender/gpu/intern/gpu_viewport.c

index 44e9b98..6e0bd7f 100644 (file)
@@ -1130,11 +1130,10 @@ void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 #define ADD_SHGROUP_CALL(shgrp, ob, geom, oedata) \
   do { \
     if (oedata) { \
-      DRW_shgroup_call_object_with_callback( \
-          shgrp, geom, ob, EEVEE_lightprobes_obj_visibility_cb, oedata); \
+      DRW_shgroup_call_object_with_callback(shgrp, geom, ob, oedata); \
     } \
     else { \
-      DRW_shgroup_call_object_ex(shgrp, geom, ob, false); \
+      DRW_shgroup_call_object(shgrp, geom, ob); \
     } \
   } while (0)
 
index 6d290f9..391d041 100644 (file)
@@ -81,6 +81,7 @@ struct rcti;
 
 typedef struct DRWInterface DRWInterface;
 typedef struct DRWPass DRWPass;
+typedef struct DRWView DRWView;
 typedef struct DRWShadingGroup DRWShadingGroup;
 typedef struct DRWUniform DRWUniform;
 
@@ -411,10 +412,10 @@ void DRW_shgroup_call_object_ex(DRWShadingGroup *shgroup,
   DRW_shgroup_call_object_ex(shgroup, geom, ob, true)
 
 /* TODO(fclem) remove this when we have DRWView */
+/* user_data is used by DRWCallVisibilityFn defined in DRWView. */
 void DRW_shgroup_call_object_with_callback(DRWShadingGroup *shgroup,
                                            struct GPUBatch *geom,
                                            struct Object *ob,
-                                           DRWCallVisibilityFn *callback,
                                            void *user_data);
 
 void DRW_shgroup_call_instances(DRWShadingGroup *shgroup,
@@ -520,6 +521,7 @@ bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup);
 
 /* Passes */
 DRWPass *DRW_pass_create(const char *name, DRWState state);
+/* TODO Replace with passes inheritance. */
 void DRW_pass_state_set(DRWPass *pass, DRWState state);
 void DRW_pass_state_add(DRWPass *pass, DRWState state);
 void DRW_pass_state_remove(DRWPass *pass, DRWState state);
@@ -532,6 +534,50 @@ bool DRW_pass_is_empty(DRWPass *pass);
 
 #define DRW_PASS_CREATE(pass, state) (pass = DRW_pass_create(#pass, state))
 
+/* Views */
+DRWView *DRW_view_create(const float viewmat[4][4],
+                         const float winmat[4][4],
+                         const float (*culling_viewmat)[4],
+                         const float (*culling_winmat)[4],
+                         DRWCallVisibilityFn *visibility_fn);
+DRWView *DRW_view_create_sub(const DRWView *parent_view,
+                             const float viewmat[4][4],
+                             const float winmat[4][4]);
+
+void DRW_view_update(DRWView *view,
+                     const float viewmat[4][4],
+                     const float winmat[4][4],
+                     const float (*culling_viewmat)[4],
+                     const float (*culling_winmat)[4]);
+void DRW_view_update_sub(DRWView *view, const float viewmat[4][4], const float winmat[4][4]);
+
+const DRWView *DRW_view_default_get(void);
+void DRW_view_default_set(DRWView *view);
+
+void DRW_view_set_active(DRWView *view);
+
+void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len);
+
+/* For all getters, if view is NULL, default view is assumed. */
+void DRW_view_winmat_get(const DRWView *view, float mat[4][4], bool inverse);
+void DRW_view_viewmat_get(const DRWView *view, float mat[4][4], bool inverse);
+void DRW_view_persmat_get(const DRWView *view, float mat[4][4], bool inverse);
+
+void DRW_view_frustum_corners_get(const DRWView *view, BoundBox *corners);
+void DRW_view_frustum_planes_get(const DRWView *view, float planes[6][4]);
+
+/* These are in view-space, so negative if in perspective.
+ * Extract near and far clip distance from the projection matrix. */
+float DRW_view_near_distance_get(const DRWView *view);
+float DRW_view_far_distance_get(const DRWView *view);
+bool DRW_view_is_persp_get(const DRWView *view);
+
+/* Culling, return true if object is inside view frustum. */
+/* TODO */
+// bool DRW_culling_sphere_test(DRWView *view, BoundSphere *bsphere);
+// bool DRW_culling_box_test(DRWView *view, BoundBox *bbox);
+// bool DRW_culling_plane_test(DRWView *view, float plane[4]);
+
 /* Viewport */
 typedef enum {
   /* keep in sync with the union struct DRWMatrixState. */
@@ -645,9 +691,9 @@ void DRW_state_clip_planes_reset(void);
 void DRW_state_clip_planes_set_from_rv3d(struct RegionView3D *rv3d);
 
 /* Culling, return true if object is inside view frustum. */
-bool DRW_culling_sphere_test(BoundSphere *bsphere);
-bool DRW_culling_box_test(BoundBox *bbox);
-bool DRW_culling_plane_test(float plane[4]);
+bool DRW_culling_sphere_test(const BoundSphere *bsphere);
+bool DRW_culling_box_test(const BoundBox *bbox);
+bool DRW_culling_plane_test(const float plane[4]);
 
 void DRW_culling_frustum_corners_get(BoundBox *corners);
 void DRW_culling_frustum_planes_get(float planes[6][4]);
index 8ded73a..90e1ff7 100644 (file)
@@ -538,9 +538,11 @@ static void drw_viewport_cache_resize(void)
 
     BLI_memblock_clear(DST.vmempool->calls, NULL);
     BLI_memblock_clear(DST.vmempool->states, NULL);
+    BLI_memblock_clear(DST.vmempool->cullstates, NULL);
     BLI_memblock_clear(DST.vmempool->shgroups, NULL);
     BLI_memblock_clear(DST.vmempool->uniforms, NULL);
     BLI_memblock_clear(DST.vmempool->passes, NULL);
+    BLI_memblock_clear(DST.vmempool->views, NULL);
     BLI_memblock_clear(DST.vmempool->images, NULL);
   }
 
@@ -611,12 +613,18 @@ static void drw_viewport_var_init(void)
     if (DST.vmempool->states == NULL) {
       DST.vmempool->states = BLI_memblock_create(sizeof(DRWCallState), false);
     }
+    if (DST.vmempool->cullstates == NULL) {
+      DST.vmempool->cullstates = BLI_memblock_create(sizeof(DRWCullingState), false);
+    }
     if (DST.vmempool->shgroups == NULL) {
       DST.vmempool->shgroups = BLI_memblock_create(sizeof(DRWShadingGroup), false);
     }
     if (DST.vmempool->uniforms == NULL) {
       DST.vmempool->uniforms = BLI_memblock_create(sizeof(DRWUniform), false);
     }
+    if (DST.vmempool->views == NULL) {
+      DST.vmempool->views = BLI_memblock_create(sizeof(DRWView), false);
+    }
     if (DST.vmempool->passes == NULL) {
       DST.vmempool->passes = BLI_memblock_create(sizeof(DRWPass), false);
     }
@@ -638,31 +646,38 @@ static void drw_viewport_var_init(void)
     DST.vmempool = NULL;
   }
 
+  DST.primary_view_ct = 0;
+
   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]);
+    normalize_v3_v3(DST.screenvecs[0], rv3d->viewinv[0]);
+    normalize_v3_v3(DST.screenvecs[1], rv3d->viewinv[1]);
 
-    /* Refresh DST.pixelsize */
     DST.pixsize = rv3d->pixsize;
+    DST.view_default = DRW_view_create(rv3d->viewmat, rv3d->winmat, NULL, NULL, NULL);
+    copy_v4_v4(DST.view_default->storage.viewcamtexcofac, rv3d->viewcamtexcofac);
 
-    copy_m4_m4(DST.original_mat.mat[DRW_MAT_PERS], rv3d->persmat);
-    copy_m4_m4(DST.original_mat.mat[DRW_MAT_PERSINV], rv3d->persinv);
-    copy_m4_m4(DST.original_mat.mat[DRW_MAT_VIEW], rv3d->viewmat);
-    copy_m4_m4(DST.original_mat.mat[DRW_MAT_VIEWINV], rv3d->viewinv);
-    copy_m4_m4(DST.original_mat.mat[DRW_MAT_WIN], rv3d->winmat);
-    invert_m4_m4(DST.original_mat.mat[DRW_MAT_WININV], rv3d->winmat);
-
-    memcpy(DST.view_data.matstate.mat, DST.original_mat.mat, sizeof(DST.original_mat.mat));
+    if (DST.draw_ctx.sh_cfg == GPU_SHADER_CFG_CLIPPED) {
+      int plane_len = (rv3d->viewlock & RV3D_BOXCLIP) ? 4 : 6;
+      DRW_view_clip_planes_set(DST.view_default, rv3d->clip, plane_len);
+    }
 
-    copy_v4_v4(DST.view_data.viewcamtexcofac, rv3d->viewcamtexcofac);
+    /* TODO should be set to NULL. */
+    DST.view_active = DRW_view_create(rv3d->viewmat, rv3d->winmat, NULL, NULL, NULL);
   }
   else {
-    copy_v4_fl4(DST.view_data.viewcamtexcofac, 1.0f, 1.0f, 0.0f, 0.0f);
+    zero_v3(DST.screenvecs[0]);
+    zero_v3(DST.screenvecs[1]);
+
+    DST.pixsize = 1.0f;
+    DST.view_default = NULL;
+
+    /* TODO should be set to NULL. */
+    float mat[4][4];
+    unit_m4(mat);
+    DST.view_active = DRW_view_create(mat, mat, NULL, NULL, NULL);
   }
 
+  /* fclem: Is this still needed ? */
   if (DST.draw_ctx.object_edit) {
     ED_view3d_init_mats_rv3d(DST.draw_ctx.object_edit, rv3d);
   }
@@ -674,61 +689,49 @@ static void drw_viewport_var_init(void)
     G_draw.view_ubo = DRW_uniformbuffer_create(sizeof(ViewUboStorage), NULL);
   }
 
-  DST.override_mat = 0;
-  DST.dirty_mat = true;
-  DST.state_cache_id = 1;
-
-  DST.clipping.updated = false;
-
   memset(DST.object_instance_data, 0x0, sizeof(DST.object_instance_data));
 }
 
+/* TODO remove all of the DRW_viewport_matrix_* functions. */
+
 void DRW_viewport_matrix_get(float mat[4][4], DRWViewportMatrixType type)
 {
   BLI_assert(type >= 0 && type < DRW_MAT_COUNT);
   /* Can't use this in render mode. */
-  BLI_assert(((DST.override_mat & (1 << type)) != 0) || DST.draw_ctx.rv3d != NULL);
+  // BLI_assert(((DST.override_mat & (1 << type)) != 0) || DST.draw_ctx.rv3d != NULL);
 
-  copy_m4_m4(mat, DST.view_data.matstate.mat[type]);
+  copy_m4_m4(mat, DST.view_active->storage.matstate.mat[type]);
 }
 
 void DRW_viewport_matrix_get_all(DRWMatrixState *state)
 {
-  memcpy(state, DST.view_data.matstate.mat, sizeof(DRWMatrixState));
+  memcpy(state, DST.view_active->storage.matstate.mat, sizeof(DRWMatrixState));
 }
 
 void DRW_viewport_matrix_override_set(const float mat[4][4], DRWViewportMatrixType type)
 {
   BLI_assert(type < DRW_MAT_COUNT);
-  copy_m4_m4(DST.view_data.matstate.mat[type], mat);
-  DST.override_mat |= (1 << type);
-  DST.dirty_mat = true;
-  DST.clipping.updated = false;
+  copy_m4_m4(DST.view_active->storage.matstate.mat[type], mat);
+  DST.view_active->is_dirty = true;
 }
 
 void DRW_viewport_matrix_override_unset(DRWViewportMatrixType type)
 {
   BLI_assert(type < DRW_MAT_COUNT);
-  copy_m4_m4(DST.view_data.matstate.mat[type], DST.original_mat.mat[type]);
-  DST.override_mat &= ~(1 << type);
-  DST.dirty_mat = true;
-  DST.clipping.updated = false;
+  copy_m4_m4(DST.view_active->storage.matstate.mat[type],
+             DST.view_default->storage.matstate.mat[type]);
+  DST.view_active->is_dirty = true;
 }
 
 void DRW_viewport_matrix_override_set_all(DRWMatrixState *state)
 {
-  memcpy(DST.view_data.matstate.mat, state, sizeof(DRWMatrixState));
-  DST.override_mat = 0xFFFFFF;
-  DST.dirty_mat = true;
-  DST.clipping.updated = false;
+  memcpy(DST.view_active->storage.matstate.mat, state, sizeof(DRWMatrixState));
+  DST.view_active->is_dirty = true;
 }
 
 void DRW_viewport_matrix_override_unset_all(void)
 {
-  memcpy(DST.view_data.matstate.mat, DST.original_mat.mat, sizeof(DRWMatrixState));
-  DST.override_mat = 0;
-  DST.dirty_mat = true;
-  DST.clipping.updated = false;
+  DRW_viewport_matrix_override_set_all(&DST.view_default->storage.matstate);
 }
 
 bool DRW_viewport_is_persp_get(void)
@@ -738,7 +741,7 @@ bool DRW_viewport_is_persp_get(void)
     return rv3d->is_persp;
   }
   else {
-    return DST.view_data.matstate.mat[DRW_MAT_WIN][3][3] == 0.0f;
+    return DST.view_active->storage.matstate.winmat[3][3] == 0.0f;
   }
 }
 
index 96dcfb9..0ccffce 100644 (file)
@@ -43,6 +43,7 @@
 /* Use draw manager to call GPU_select, see: DRW_draw_select_loop */
 #define USE_GPU_SELECT
 
+#define DRW_DEBUG_CULLING
 #define DRW_DEBUG_USE_UNIFORM_NAME 0
 #define DRW_UNIFORM_BUFFER_NAME 64
 
@@ -91,9 +92,7 @@
 
 /* Used by DRWCallState.flag */
 enum {
-  DRW_CALL_CULLED = (1 << 0),
   DRW_CALL_NEGSCALE = (1 << 1),
-  DRW_CALL_BYPASS_CULLING = (1 << 2),
 };
 
 /* Used by DRWCallState.matflag */
@@ -104,22 +103,24 @@ enum {
   DRW_CALL_OBJECTINFO = (1 << 3),
 };
 
-typedef struct DRWCallState {
-  DRWCallVisibilityFn *visibility_cb;
+typedef struct DRWCullingState {
+  uint32_t mask;
+  /* Culling: Using Bounding Sphere for now for faster culling.
+   * Not ideal for planes. Could be extended. */
+  BoundSphere bsphere;
+  /* Grrr only used by EEVEE. */
   void *user_data;
+} DRWCullingState;
 
+typedef struct DRWCallState {
+  DRWCullingState *culling;
   uchar flag;
-  uchar cache_id; /* Compared with DST.state_cache_id to see if matrices are still valid. */
-  uchar matflag;  /* Which matrices to compute. */
+  uchar matflag; /* Which matrices to compute. */
   short ob_index;
-  /* Culling: Using Bounding Sphere for now for faster culling.
-   * Not ideal for planes. */
-  BoundSphere bsphere;
   /* Matrices */
   float model[4][4];
   float modelinverse[4][4];
-  float modelviewprojection[4][4];
-  float orcotexfac[2][3]; /* Not view dependent */
+  float orcotexfac[2][3];
   float ob_random;
 } DRWCallState;
 
@@ -213,24 +214,43 @@ struct DRWPass {
   char name[MAX_PASS_NAME];
 };
 
+/* keep in sync with viewBlock */
+typedef struct ViewUboStorage {
+  DRWMatrixState matstate;
+  float clipplanes[6][4];
+  /* Should not be here. Not view dependant (only main view). */
+  float viewcamtexcofac[4];
+} ViewUboStorage;
+
+#define MAX_CULLED_VIEWS 32
+
+struct DRWView {
+  /** Parent view if this is a sub view. NULL otherwise. */
+  struct DRWView *parent;
+
+  ViewUboStorage storage;
+  /** Number of active clipplanes. */
+  int clip_planes_len;
+  /** Does culling result needs to be updated. */
+  bool is_dirty;
+  /** Culling */
+  uint32_t culling_mask;
+  BoundBox frustum_corners;
+  BoundSphere frustum_bsphere;
+  float frustum_planes[6][4];
+  /** Custom visibility function. */
+  DRWCallVisibilityFn *visibility_fn;
+  void *user_data;
+};
+
 /* TODO(fclem): Future awaits */
 #if 0
-typedef struct DRWView {
-  /* Culling function, culling result etc...*/
-} DRWView;
-
 typedef struct ModelUboStorage {
   float model[4][4];
   float modelinverse[4][4];
 } ModelUboStorage;
 #endif
 
-typedef struct ViewUboStorage {
-  DRWMatrixState matstate;
-  float viewcamtexcofac[4];
-  float clipplanes[2][4];
-} ViewUboStorage;
-
 /* ------------- DRAW DEBUG ------------ */
 
 typedef struct DRWDebugLine {
@@ -258,7 +278,6 @@ typedef struct DRWManager {
   DRWInstanceData *object_instance_data[MAX_INSTANCE_DATA_SIZE];
   /* State of the object being evaluated if already allocated. */
   DRWCallState *ob_state;
-  uchar state_cache_id; /* Could be larger but 254 view changes is already a lot! */
   struct DupliObject *dupli_source;
   struct Object *dupli_parent;
   struct Object *dupli_origin;
@@ -302,21 +321,12 @@ typedef struct DRWManager {
 
   bool buffer_finish_called; /* Avoid bad usage of DRW_render_instance_buffer_finish */
 
-  /* View dependent uniforms. */
-  DRWMatrixState original_mat; /* Original rv3d matrices. */
-  int override_mat;            /* Bitflag of which matrices are overridden. */
-  int clip_planes_len;         /* Number of active clipplanes. */
-  bool dirty_mat;
-
-  /* keep in sync with viewBlock */
-  ViewUboStorage view_data;
-
-  struct {
-    float frustum_planes[6][4];
-    BoundBox frustum_corners;
-    BoundSphere frustum_bsphere;
-    bool updated;
-  } clipping;
+  DRWView *view_default;
+  DRWView *view_active;
+  uint primary_view_ct;
+  /** TODO(fclem) Remove this. Only here to support
+   * shaders without common_view_lib.glsl */
+  ViewUboStorage view_storage_cpy;
 
 #ifdef USE_GPU_SELECT
   uint select_id;
index 837b68c..12f60b5 100644 (file)
 #include "BLI_mempool.h"
 #include "BLI_memblock.h"
 
+#ifdef DRW_DEBUG_CULLING
+#  include "BLI_math_bits.h"
+#endif
+
 #include "GPU_buffers.h"
 
 #include "intern/gpu_codegen.h"
@@ -389,8 +393,6 @@ static DRWCallState *drw_call_state_create(DRWShadingGroup *shgroup, float (*obm
 {
   DRWCallState *state = BLI_memblock_alloc(DST.vmempool->states);
   state->flag = 0;
-  state->cache_id = 0;
-  state->visibility_cb = NULL;
   state->matflag = 0;
 
   /* Matrices */
@@ -407,18 +409,23 @@ static DRWCallState *drw_call_state_create(DRWShadingGroup *shgroup, float (*obm
 
   drw_call_state_update_matflag(state, shgroup, ob);
 
+  DRWCullingState *cull = BLI_memblock_alloc(DST.vmempool->cullstates);
+  state->culling = cull;
+
   if (ob != NULL) {
     float corner[3];
     BoundBox *bbox = BKE_object_boundbox_get(ob);
     /* Get BoundSphere center and radius from the BoundBox. */
-    mid_v3_v3v3(state->bsphere.center, bbox->vec[0], bbox->vec[6]);
+    mid_v3_v3v3(cull->bsphere.center, bbox->vec[0], bbox->vec[6]);
     mul_v3_m4v3(corner, obmat, bbox->vec[0]);
-    mul_m4_v3(obmat, state->bsphere.center);
-    state->bsphere.radius = len_v3v3(state->bsphere.center, corner);
+    mul_m4_v3(obmat, cull->bsphere.center);
+    cull->bsphere.radius = len_v3v3(cull->bsphere.center, corner);
   }
   else {
+    /* TODO(fclem) Bypass alloc if we can (see if eevee's
+     * probe visibility collection still works). */
     /* Bypass test. */
-    state->bsphere.radius = -1.0f;
+    cull->bsphere.radius = -1.0f;
   }
 
   return state;
@@ -531,8 +538,6 @@ void DRW_shgroup_call_object_ex(DRWShadingGroup *shgroup,
   BLI_LINKS_APPEND(&shgroup->calls, call);
 
   call->state = drw_call_state_object(shgroup, ob->obmat, ob);
-  /* NOTE this will disable culling for the whole object. */
-  call->state->flag |= (bypass_culling) ? DRW_CALL_BYPASS_CULLING : 0;
   call->batch = geom;
   call->vert_first = 0;
   call->vert_count = 0; /* Auto from batch. */
@@ -541,12 +546,15 @@ void DRW_shgroup_call_object_ex(DRWShadingGroup *shgroup,
   call->select_id = DST.select_id;
   call->inst_selectid = NULL;
 #endif
+  if (bypass_culling) {
+    /* NOTE this will disable culling for the whole object. */
+    call->state->culling->bsphere.radius = -1.0f;
+  }
 }
 
 void DRW_shgroup_call_object_with_callback(DRWShadingGroup *shgroup,
                                            GPUBatch *geom,
                                            Object *ob,
-                                           DRWCallVisibilityFn *callback,
                                            void *user_data)
 {
   BLI_assert(geom != NULL);
@@ -555,8 +563,7 @@ void DRW_shgroup_call_object_with_callback(DRWShadingGroup *shgroup,
   BLI_LINKS_APPEND(&shgroup->calls, call);
 
   call->state = drw_call_state_object(shgroup, ob->obmat, ob);
-  call->state->visibility_cb = callback;
-  call->state->user_data = user_data;
+  call->state->culling->user_data = user_data;
   call->batch = geom;
   call->vert_first = 0;
   call->vert_count = 0; /* Auto from batch. */
@@ -859,23 +866,16 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader)
   }
   else {
     /* Only here to support builtin shaders. This should not be used by engines. */
+    /* TODO remove. */
+    DRWMatrixState *matstate = &DST.view_storage_cpy.matstate;
+    drw_shgroup_builtin_uniform(shgroup, GPU_UNIFORM_VIEW, matstate->viewmat, 16, 1);
+    drw_shgroup_builtin_uniform(shgroup, GPU_UNIFORM_VIEW_INV, matstate->viewinv, 16, 1);
+    drw_shgroup_builtin_uniform(shgroup, GPU_UNIFORM_VIEWPROJECTION, matstate->persmat, 16, 1);
+    drw_shgroup_builtin_uniform(shgroup, GPU_UNIFORM_VIEWPROJECTION_INV, matstate->persinv, 16, 1);
+    drw_shgroup_builtin_uniform(shgroup, GPU_UNIFORM_PROJECTION, matstate->winmat, 16, 1);
+    drw_shgroup_builtin_uniform(shgroup, GPU_UNIFORM_PROJECTION_INV, matstate->wininv, 16, 1);
     drw_shgroup_builtin_uniform(
-        shgroup, GPU_UNIFORM_VIEW, DST.view_data.matstate.mat[DRW_MAT_VIEW], 16, 1);
-    drw_shgroup_builtin_uniform(
-        shgroup, GPU_UNIFORM_VIEW_INV, DST.view_data.matstate.mat[DRW_MAT_VIEWINV], 16, 1);
-    drw_shgroup_builtin_uniform(
-        shgroup, GPU_UNIFORM_VIEWPROJECTION, DST.view_data.matstate.mat[DRW_MAT_PERS], 16, 1);
-    drw_shgroup_builtin_uniform(shgroup,
-                                GPU_UNIFORM_VIEWPROJECTION_INV,
-                                DST.view_data.matstate.mat[DRW_MAT_PERSINV],
-                                16,
-                                1);
-    drw_shgroup_builtin_uniform(
-        shgroup, GPU_UNIFORM_PROJECTION, DST.view_data.matstate.mat[DRW_MAT_WIN], 16, 1);
-    drw_shgroup_builtin_uniform(
-        shgroup, GPU_UNIFORM_PROJECTION_INV, DST.view_data.matstate.mat[DRW_MAT_WININV], 16, 1);
-    drw_shgroup_builtin_uniform(
-        shgroup, GPU_UNIFORM_CAMERATEXCO, DST.view_data.viewcamtexcofac, 3, 2);
+        shgroup, GPU_UNIFORM_CAMERATEXCO, DST.view_storage_cpy.viewcamtexcofac, 4, 1);
   }
 
   /* Not supported. */
@@ -1065,6 +1065,480 @@ DRWShadingGroup *DRW_shgroup_create_sub(DRWShadingGroup *shgroup)
 
 /** \} */
 
+/* -------------------------------------------------------------------- */
+/** \name View (DRW_view)
+ * \{ */
+
+/* Extract the 8 corners from a Projection Matrix.
+ * Although less accurate, this solution can be simplified as follows:
+ * BKE_boundbox_init_from_minmax(&bbox, (const float[3]){-1.0f, -1.0f, -1.0f}, (const
+ * float[3]){1.0f, 1.0f, 1.0f}); for (int i = 0; i < 8; i++) {mul_project_m4_v3(projinv,
+ * bbox.vec[i]);}
+ */
+static void draw_frustum_boundbox_calc(const float (*viewinv)[4],
+                                       const float (*projmat)[4],
+                                       BoundBox *r_bbox)
+{
+  float left, right, bottom, top, near, far;
+  bool is_persp = projmat[3][3] == 0.0f;
+
+#if 0 /* Equivalent to this but it has accuracy problems. */
+  BKE_boundbox_init_from_minmax(
+      &bbox, (const float[3]){-1.0f, -1.0f, -1.0f}, (const float[3]){1.0f, 1.0f, 1.0f});
+  for (int i = 0; i < 8; i++) {
+    mul_project_m4_v3(projinv, bbox.vec[i]);
+  }
+#endif
+
+  projmat_dimensions(projmat, &left, &right, &bottom, &top, &near, &far);
+
+  if (is_persp) {
+    left *= near;
+    right *= near;
+    bottom *= near;
+    top *= near;
+  }
+
+  r_bbox->vec[0][2] = r_bbox->vec[3][2] = r_bbox->vec[7][2] = r_bbox->vec[4][2] = -near;
+  r_bbox->vec[0][0] = r_bbox->vec[3][0] = left;
+  r_bbox->vec[4][0] = r_bbox->vec[7][0] = right;
+  r_bbox->vec[0][1] = r_bbox->vec[4][1] = bottom;
+  r_bbox->vec[7][1] = r_bbox->vec[3][1] = top;
+
+  /* Get the coordinates of the far plane. */
+  if (is_persp) {
+    float sca_far = far / near;
+    left *= sca_far;
+    right *= sca_far;
+    bottom *= sca_far;
+    top *= sca_far;
+  }
+
+  r_bbox->vec[1][2] = r_bbox->vec[2][2] = r_bbox->vec[6][2] = r_bbox->vec[5][2] = -far;
+  r_bbox->vec[1][0] = r_bbox->vec[2][0] = left;
+  r_bbox->vec[6][0] = r_bbox->vec[5][0] = right;
+  r_bbox->vec[1][1] = r_bbox->vec[5][1] = bottom;
+  r_bbox->vec[2][1] = r_bbox->vec[6][1] = top;
+
+  /* Transform into world space. */
+  for (int i = 0; i < 8; i++) {
+    mul_m4_v3(viewinv, r_bbox->vec[i]);
+  }
+}
+
+static void draw_frustum_culling_planes_calc(const BoundBox *bbox, float (*frustum_planes)[4])
+{
+  /* TODO See if planes_from_projmat cannot do the job. */
+
+  /* Compute clip planes using the world space frustum corners. */
+  for (int p = 0; p < 6; p++) {
+    int q, r, s;
+    switch (p) {
+      case 0:
+        q = 1;
+        r = 2;
+        s = 3;
+        break; /* -X */
+      case 1:
+        q = 0;
+        r = 4;
+        s = 5;
+        break; /* -Y */
+      case 2:
+        q = 1;
+        r = 5;
+        s = 6;
+        break; /* +Z (far) */
+      case 3:
+        q = 2;
+        r = 6;
+        s = 7;
+        break; /* +Y */
+      case 4:
+        q = 0;
+        r = 3;
+        s = 7;
+        break; /* -Z (near) */
+      default:
+        q = 4;
+        r = 7;
+        s = 6;
+        break; /* +X */
+    }
+
+    normal_quad_v3(frustum_planes[p], bbox->vec[p], bbox->vec[q], bbox->vec[r], bbox->vec[s]);
+    /* Increase precision and use the mean of all 4 corners. */
+    frustum_planes[p][3] = -dot_v3v3(frustum_planes[p], bbox->vec[p]);
+    frustum_planes[p][3] += -dot_v3v3(frustum_planes[p], bbox->vec[q]);
+    frustum_planes[p][3] += -dot_v3v3(frustum_planes[p], bbox->vec[r]);
+    frustum_planes[p][3] += -dot_v3v3(frustum_planes[p], bbox->vec[s]);
+    frustum_planes[p][3] *= 0.25f;
+  }
+}
+
+static void draw_frustum_bound_sphere_calc(const BoundBox *bbox,
+                                           const float (*viewinv)[4],
+                                           const float (*projmat)[4],
+                                           const float (*projinv)[4],
+                                           BoundSphere *bsphere)
+{
+  /* Extract Bounding Sphere */
+  if (projmat[3][3] != 0.0f) {
+    /* Orthographic */
+    /* The most extreme points on the near and far plane. (normalized device coords). */
+    const float *nearpoint = bbox->vec[0];
+    const float *farpoint = bbox->vec[6];
+
+    /* just use median point */
+    mid_v3_v3v3(bsphere->center, farpoint, nearpoint);
+    bsphere->radius = len_v3v3(bsphere->center, farpoint);
+  }
+  else if (projmat[2][0] == 0.0f && projmat[2][1] == 0.0f) {
+    /* Perspective with symmetrical frustum. */
+
+    /* We obtain the center and radius of the circumscribed circle of the
+     * isosceles trapezoid composed by the diagonals of the near and far clipping plane */
+
+    /* center of each clipping plane */
+    float mid_min[3], mid_max[3];
+    mid_v3_v3v3(mid_min, bbox->vec[3], bbox->vec[4]);
+    mid_v3_v3v3(mid_max, bbox->vec[2], bbox->vec[5]);
+
+    /* square length of the diagonals of each clipping plane */
+    float a_sq = len_squared_v3v3(bbox->vec[3], bbox->vec[4]);
+    float b_sq = len_squared_v3v3(bbox->vec[2], bbox->vec[5]);
+
+    /* distance squared between clipping planes */
+    float h_sq = len_squared_v3v3(mid_min, mid_max);
+
+    float fac = (4 * h_sq + b_sq - a_sq) / (8 * h_sq);
+
+    /* The goal is to get the smallest sphere,
+     * not the sphere that passes through each corner */
+    CLAMP(fac, 0.0f, 1.0f);
+
+    interp_v3_v3v3(bsphere->center, mid_min, mid_max, fac);
+
+    /* distance from the center to one of the points of the far plane (1, 2, 5, 6) */
+    bsphere->radius = len_v3v3(bsphere->center, bbox->vec[1]);
+  }
+  else {
+    /* Perspective with asymmetrical frustum. */
+
+    /* We put the sphere center on the line that goes from origin
+     * to the center of the far clipping plane. */
+
+    /* Detect which of the corner of the far clipping plane is the farthest to the origin */
+    float nfar[4];               /* most extreme far point in NDC space */
+    float farxy[2];              /* farpoint projection onto the near plane */
+    float farpoint[3] = {0.0f};  /* most extreme far point in camera coordinate */
+    float nearpoint[3];          /* most extreme near point in camera coordinate */
+    float farcenter[3] = {0.0f}; /* center of far cliping plane in camera coordinate */
+    float F = -1.0f, N;          /* square distance of far and near point to origin */
+    float f, n; /* distance of far and near point to z axis. f is always > 0 but n can be < 0 */
+    float e, s; /* far and near clipping distance (<0) */
+    float c;    /* slope of center line = distance of far clipping center
+                 * to z axis / far clipping distance. */
+    float z;    /* projection of sphere center on z axis (<0) */
+
+    /* Find farthest corner and center of far clip plane. */
+    float corner[3] = {1.0f, 1.0f, 1.0f}; /* in clip space */
+    for (int i = 0; i < 4; i++) {
+      float point[3];
+      mul_v3_project_m4_v3(point, projinv, corner);
+      float len = len_squared_v3(point);
+      if (len > F) {
+        copy_v3_v3(nfar, corner);
+        copy_v3_v3(farpoint, point);
+        F = len;
+      }
+      add_v3_v3(farcenter, point);
+      /* rotate by 90 degree to walk through the 4 points of the far clip plane */
+      float tmp = corner[0];
+      corner[0] = -corner[1];
+      corner[1] = tmp;
+    }
+
+    /* the far center is the average of the far clipping points */
+    mul_v3_fl(farcenter, 0.25f);
+    /* the extreme near point is the opposite point on the near clipping plane */
+    copy_v3_fl3(nfar, -nfar[0], -nfar[1], -1.0f);
+    mul_v3_project_m4_v3(nearpoint, projinv, nfar);
+    /* this is a frustum projection */
+    N = len_squared_v3(nearpoint);
+    e = farpoint[2];
+    s = nearpoint[2];
+    /* distance to view Z axis */
+    f = len_v2(farpoint);
+    /* get corresponding point on the near plane */
+    mul_v2_v2fl(farxy, farpoint, s / e);
+    /* this formula preserve the sign of n */
+    sub_v2_v2(nearpoint, farxy);
+    n = f * s / e - len_v2(nearpoint);
+    c = len_v2(farcenter) / e;
+    /* the big formula, it simplifies to (F-N)/(2(e-s)) for the symmetric case */
+    z = (F - N) / (2.0f * (e - s + c * (f - n)));
+
+    bsphere->center[0] = farcenter[0] * z / e;
+    bsphere->center[1] = farcenter[1] * z / e;
+    bsphere->center[2] = z;
+    bsphere->radius = len_v3v3(bsphere->center, farpoint);
+
+    /* Transform to world space. */
+    mul_m4_v3(viewinv, bsphere->center);
+  }
+}
+
+static void draw_matrix_state_from_view(DRWMatrixState *mstate,
+                                        const float viewmat[4][4],
+                                        const float winmat[4][4])
+{
+  /* If only one the matrices is negative, then the
+   * polygon winding changes and we don't want that. */
+  BLI_assert(is_negative_m4(viewmat) != is_negative_m4(winmat));
+
+  copy_m4_m4(mstate->viewmat, viewmat);
+  invert_m4_m4(mstate->viewinv, mstate->viewmat);
+
+  copy_m4_m4(mstate->winmat, winmat);
+  invert_m4_m4(mstate->wininv, mstate->winmat);
+
+  mul_m4_m4m4(mstate->persmat, winmat, viewmat);
+  invert_m4_m4(mstate->persinv, mstate->persmat);
+}
+
+/* Create a view with culling. */
+DRWView *DRW_view_create(const float viewmat[4][4],
+                         const float winmat[4][4],
+                         const float (*culling_viewmat)[4],
+                         const float (*culling_winmat)[4],
+                         DRWCallVisibilityFn *visibility_fn)
+{
+  DRWView *view = BLI_memblock_alloc(DST.vmempool->views);
+
+  if (DST.primary_view_ct < MAX_CULLED_VIEWS) {
+    view->culling_mask = 1u << DST.primary_view_ct++;
+  }
+  else {
+    view->culling_mask = 0u;
+  }
+  view->clip_planes_len = 0;
+  view->visibility_fn = visibility_fn;
+  view->parent = NULL;
+
+  /* TODO move elsewhere */
+  if (DST.view_default) {
+    copy_v4_v4(view->storage.viewcamtexcofac, DST.view_default->storage.viewcamtexcofac);
+  }
+
+  DRW_view_update(view, viewmat, winmat, culling_viewmat, culling_winmat);
+
+  return view;
+}
+
+/* Create a view with culling done by another view. */
+DRWView *DRW_view_create_sub(const DRWView *parent_view,
+                             const float viewmat[4][4],
+                             const float winmat[4][4])
+{
+  BLI_assert(parent_view && parent_view->parent == NULL);
+
+  DRWView *view = BLI_memblock_alloc(DST.vmempool->views);
+
+  /* Perform copy. */
+  *view = *parent_view;
+  view->parent = (DRWView *)parent_view;
+
+  /* TODO move elsewhere */
+  if (DST.view_default) {
+    copy_v4_v4(view->storage.viewcamtexcofac, DST.view_default->storage.viewcamtexcofac);
+  }
+
+  DRW_view_update_sub(view, viewmat, winmat);
+
+  return view;
+}
+
+/**
+ * DRWView Update:
+ * This is meant to be done on existing views when rendering in a loop and there is no
+ * need to allocate more DRWViews.
+ **/
+
+/* Update matrices of a view created with DRW_view_create_sub. */
+void DRW_view_update_sub(DRWView *view, const float viewmat[4][4], const float winmat[4][4])
+{
+  BLI_assert(view->parent != NULL);
+  DRWMatrixState *mstate = &view->storage.matstate;
+
+  view->is_dirty = true;
+
+  draw_matrix_state_from_view(mstate, viewmat, winmat);
+}
+
+/* Update matrices of a view created with DRW_view_create. */
+void DRW_view_update(DRWView *view,
+                     const float viewmat[4][4],
+                     const float winmat[4][4],
+                     const float (*culling_viewmat)[4],
+                     const float (*culling_winmat)[4])
+{
+  /* DO NOT UPDATE THE DEFAULT VIEW.
+   * Create subviews instead, or a copy. */
+  BLI_assert(view != DST.view_default);
+  BLI_assert(view->parent == NULL);
+  DRWMatrixState *mstate = &view->storage.matstate;
+
+  view->is_dirty = true;
+
+  draw_matrix_state_from_view(mstate, viewmat, winmat);
+
+  /* Prepare frustum culling. */
+
+#ifdef DRW_DEBUG_CULLING
+  static float mv[MAX_CULLED_VIEWS][4][4], mw[MAX_CULLED_VIEWS][4][4];
+
+  /* Select view here. */
+  if (view->culling_mask != 0) {
+    uint index = bitscan_forward_uint(view->culling_mask);
+
+    if (G.debug_value == 0) {
+      copy_m4_m4(mv[index], culling_viewmat ? culling_viewmat : viewmat);
+      copy_m4_m4(mw[index], culling_winmat ? culling_winmat : winmat);
+    }
+    else {
+      culling_winmat = mw[index];
+      culling_viewmat = mv[index];
+    }
+  }
+#endif
+
+  float wininv[4][4];
+  if (culling_winmat) {
+    winmat = culling_winmat;
+    invert_m4_m4(wininv, winmat);
+  }
+  else {
+    copy_m4_m4(wininv, mstate->wininv);
+  }
+
+  float viewinv[4][4];
+  if (culling_viewmat) {
+    viewmat = culling_viewmat;
+    invert_m4_m4(viewinv, viewmat);
+  }
+  else {
+    copy_m4_m4(viewinv, mstate->viewinv);
+  }
+
+  draw_frustum_boundbox_calc(viewinv, winmat, &view->frustum_corners);
+  draw_frustum_culling_planes_calc(&view->frustum_corners, view->frustum_planes);
+  draw_frustum_bound_sphere_calc(
+      &view->frustum_corners, viewinv, winmat, wininv, &view->frustum_bsphere);
+
+#ifdef DRW_DEBUG_CULLING
+  if (G.debug_value != 0) {
+    DRW_debug_sphere(
+        view->frustum_bsphere.center, view->frustum_bsphere.radius, (const float[4]){1, 1, 0, 1});
+    DRW_debug_bbox(&view->frustum_corners, (const float[4]){1, 1, 0, 1});
+  }
+#endif
+}
+
+/* Return default view if it is a viewport render. */
+const DRWView *DRW_view_default_get(void)
+{
+  return DST.view_default;
+}
+
+/* MUST only be called once per render and only in render mode. Sets default view. */
+void DRW_view_default_set(DRWView *view)
+{
+  BLI_assert(DST.view_default == NULL);
+  DST.view_default = view;
+}
+
+/**
+ * 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).
+ */
+void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len)
+{
+  BLI_assert(plane_len <= MAX_CLIP_PLANES);
+  view->clip_planes_len = plane_len;
+  if (plane_len > 0) {
+    memcpy(view->storage.clipplanes, planes, sizeof(float) * 4 * plane_len);
+  }
+}
+
+/* Return world space frustum corners. */
+void DRW_view_frustum_corners_get(const DRWView *view, BoundBox *corners)
+{
+  memcpy(corners, &view->frustum_corners, sizeof(view->frustum_corners));
+}
+
+/* Return world space frustum sides as planes.
+ * See draw_frustum_culling_planes_calc() for the plane order. */
+void DRW_view_frustum_planes_get(const DRWView *view, float planes[6][4])
+{
+  memcpy(planes, &view->frustum_planes, sizeof(view->frustum_planes));
+}
+
+bool DRW_view_is_persp_get(const DRWView *view)
+{
+  view = (view) ? view : DST.view_active;
+  return view->storage.matstate.winmat[3][3] == 0.0f;
+}
+
+float DRW_view_near_distance_get(const DRWView *view)
+{
+  view = (view) ? view : DST.view_active;
+  const float(*projmat)[4] = view->storage.matstate.winmat;
+
+  if (DRW_view_is_persp_get(view)) {
+    return -projmat[3][2] / (projmat[2][2] - 1.0f);
+  }
+  else {
+    return -(projmat[3][2] + 1.0f) / projmat[2][2];
+  }
+}
+
+float DRW_view_far_distance_get(const DRWView *view)
+{
+  view = (view) ? view : DST.view_active;
+  const float(*projmat)[4] = view->storage.matstate.winmat;
+
+  if (DRW_view_is_persp_get(view)) {
+    return -projmat[3][2] / (projmat[2][2] + 1.0f);
+  }
+  else {
+    return -(projmat[3][2] - 1.0f) / projmat[2][2];
+  }
+}
+
+void DRW_view_viewmat_get(const DRWView *view, float mat[4][4], bool inverse)
+{
+  view = (view) ? view : DST.view_active;
+  const DRWMatrixState *state = &view->storage.matstate;
+  copy_m4_m4(mat, (inverse) ? state->viewinv : state->viewmat);
+}
+
+void DRW_view_winmat_get(const DRWView *view, float mat[4][4], bool inverse)
+{
+  view = (view) ? view : DST.view_active;
+  const DRWMatrixState *state = &view->storage.matstate;
+  copy_m4_m4(mat, (inverse) ? state->wininv : state->winmat);
+}
+
+void DRW_view_persmat_get(const DRWView *view, float mat[4][4], bool inverse)
+{
+  view = (view) ? view : DST.view_active;
+  const DRWMatrixState *state = &view->storage.matstate;
+  copy_m4_m4(mat, (inverse) ? state->persinv : state->persmat);
+}
+
+/** \} */
+
 /* -------------------------------------------------------------------- */
 /** \name Passes (DRW_pass)
  * \{ */
@@ -1118,8 +1592,8 @@ void DRW_pass_foreach_shgroup(DRWPass *pass,
 }
 
 typedef struct ZSortData {
-  float *axis;
-  float *origin;
+  const float *axis;
+  const float *origin;
 } ZSortData;
 
 static int pass_shgroup_dist_sort(void *thunk, const void *a, const void *b)
@@ -1182,8 +1656,7 @@ static int pass_shgroup_dist_sort(void *thunk, const void *a, const void *b)
  */
 void DRW_pass_sort_shgroup_z(DRWPass *pass)
 {
-  float(*viewinv)[4];
-  viewinv = DST.view_data.matstate.mat[DRW_MAT_VIEWINV];
+  const float(*viewinv)[4] = DST.view_active->storage.matstate.viewinv;
 
   ZSortData zsortdata = {viewinv[2], viewinv[3]};
 
index 49927b7..986c8fe 100644 (file)
@@ -238,7 +238,7 @@ void drw_state_set(DRWState state)
     int test;
     if ((test = CHANGED_TO(DRW_STATE_CLIP_PLANES))) {
       if (test == 1) {
-        for (int i = 0; i < DST.clip_planes_len; ++i) {
+        for (int i = 0; i < DST.view_active->clip_planes_len; ++i) {
           glEnable(GL_CLIP_DISTANCE0 + i);
         }
       }
@@ -395,274 +395,47 @@ void DRW_state_reset(void)
 void DRW_state_clip_planes_len_set(uint plane_len)
 {
   BLI_assert(plane_len <= MAX_CLIP_PLANES);
-  DST.clip_planes_len = plane_len;
+  /* DUMMY TO REMOVE */
 }
 
 void DRW_state_clip_planes_reset(void)
 {
-  DST.clip_planes_len = 0;
+  /* DUMMY TO REMOVE */
 }
 
 void DRW_state_clip_planes_set_from_rv3d(RegionView3D *rv3d)
 {
-  int max_len = 6;
-  int real_len = (rv3d->viewlock & RV3D_BOXCLIP) ? 4 : max_len;
-  while (real_len < max_len) {
-    /* Fill in dummy values that wont change results (6 is hard coded in shaders). */
-    copy_v4_v4(rv3d->clip[real_len], rv3d->clip[3]);
-    real_len++;
-  }
-
-  DRW_state_clip_planes_len_set(max_len);
+  /* DUMMY TO REMOVE */
 }
 
 /** \} */
 
 /* -------------------------------------------------------------------- */
-/** \name Clipping (DRW_clipping)
+/** \name Culling (DRW_culling)
  * \{ */
 
-/* Extract the 8 corners from a Projection Matrix.
- * Although less accurate, this solution can be simplified as follows:
- * BKE_boundbox_init_from_minmax(&bbox, (const float[3]){-1.0f, -1.0f, -1.0f}, (const
- * float[3]){1.0f, 1.0f, 1.0f}); for (int i = 0; i < 8; i++) {mul_project_m4_v3(projinv,
- * bbox.vec[i]);}
- */
-static void draw_frustum_boundbox_calc(const float (*projmat)[4], BoundBox *r_bbox)
+static bool draw_call_is_culled(DRWCall *call, DRWView *view)
 {
-  float left, right, bottom, top, near, far;
-  bool is_persp = projmat[3][3] == 0.0f;
-
-  projmat_dimensions(projmat, &left, &right, &bottom, &top, &near, &far);
-
-  if (is_persp) {
-    left *= near;
-    right *= near;
-    bottom *= near;
-    top *= near;
-  }
-
-  r_bbox->vec[0][2] = r_bbox->vec[3][2] = r_bbox->vec[7][2] = r_bbox->vec[4][2] = -near;
-  r_bbox->vec[0][0] = r_bbox->vec[3][0] = left;
-  r_bbox->vec[4][0] = r_bbox->vec[7][0] = right;
-  r_bbox->vec[0][1] = r_bbox->vec[4][1] = bottom;
-  r_bbox->vec[7][1] = r_bbox->vec[3][1] = top;
-
-  /* Get the coordinates of the far plane. */
-  if (is_persp) {
-    float sca_far = far / near;
-    left *= sca_far;
-    right *= sca_far;
-    bottom *= sca_far;
-    top *= sca_far;
-  }
-
-  r_bbox->vec[1][2] = r_bbox->vec[2][2] = r_bbox->vec[6][2] = r_bbox->vec[5][2] = -far;
-  r_bbox->vec[1][0] = r_bbox->vec[2][0] = left;
-  r_bbox->vec[6][0] = r_bbox->vec[5][0] = right;
-  r_bbox->vec[1][1] = r_bbox->vec[5][1] = bottom;
-  r_bbox->vec[2][1] = r_bbox->vec[6][1] = top;
+  return (call->state->culling->mask & view->culling_mask) != 0;
 }
 
-static void draw_clipping_setup_from_view(void)
+/* Set active view for rendering. */
+void DRW_view_set_active(DRWView *view)
 {
-  if (DST.clipping.updated) {
-    return;
-  }
-
-  float(*viewinv)[4] = DST.view_data.matstate.mat[DRW_MAT_VIEWINV];
-  float(*projmat)[4] = DST.view_data.matstate.mat[DRW_MAT_WIN];
-  float(*projinv)[4] = DST.view_data.matstate.mat[DRW_MAT_WININV];
-  BoundSphere *bsphere = &DST.clipping.frustum_bsphere;
-
-  /* Extract Clipping Planes */
-  BoundBox bbox;
-#if 0 /* It has accuracy problems. */
-  BKE_boundbox_init_from_minmax(
-      &bbox, (const float[3]){-1.0f, -1.0f, -1.0f}, (const float[3]){1.0f, 1.0f, 1.0f});
-  for (int i = 0; i < 8; i++) {
-    mul_project_m4_v3(projinv, bbox.vec[i]);
-  }
-#else
-  draw_frustum_boundbox_calc(projmat, &bbox);
-#endif
-  /* Transform into world space. */
-  for (int i = 0; i < 8; i++) {
-    mul_m4_v3(viewinv, bbox.vec[i]);
-  }
-
-  memcpy(&DST.clipping.frustum_corners, &bbox, sizeof(BoundBox));
-
-  /* Compute clip planes using the world space frustum corners. */
-  for (int p = 0; p < 6; p++) {
-    int q, r, s;
-    switch (p) {
-      case 0:
-        q = 1;
-        r = 2;
-        s = 3;
-        break; /* -X */
-      case 1:
-        q = 0;
-        r = 4;
-        s = 5;
-        break; /* -Y */
-      case 2:
-        q = 1;
-        r = 5;
-        s = 6;
-        break; /* +Z (far) */
-      case 3:
-        q = 2;
-        r = 6;
-        s = 7;
-        break; /* +Y */
-      case 4:
-        q = 0;
-        r = 3;
-        s = 7;
-        break; /* -Z (near) */
-      default:
-        q = 4;
-        r = 7;
-        s = 6;
-        break; /* +X */
-    }
-    if (DST.frontface == GL_CW) {
-      SWAP(int, q, s);
-    }
-
-    normal_quad_v3(
-        DST.clipping.frustum_planes[p], bbox.vec[p], bbox.vec[q], bbox.vec[r], bbox.vec[s]);
-    /* Increase precision and use the mean of all 4 corners. */
-    DST.clipping.frustum_planes[p][3] = -dot_v3v3(DST.clipping.frustum_planes[p], bbox.vec[p]);
-    DST.clipping.frustum_planes[p][3] += -dot_v3v3(DST.clipping.frustum_planes[p], bbox.vec[q]);
-    DST.clipping.frustum_planes[p][3] += -dot_v3v3(DST.clipping.frustum_planes[p], bbox.vec[r]);
-    DST.clipping.frustum_planes[p][3] += -dot_v3v3(DST.clipping.frustum_planes[p], bbox.vec[s]);
-    DST.clipping.frustum_planes[p][3] *= 0.25f;
-  }
-
-  /* Extract Bounding Sphere */
-  if (projmat[3][3] != 0.0f) {
-    /* Orthographic */
-    /* The most extreme points on the near and far plane. (normalized device coords). */
-    float *nearpoint = bbox.vec[0];
-    float *farpoint = bbox.vec[6];
-
-    /* just use median point */
-    mid_v3_v3v3(bsphere->center, farpoint, nearpoint);
-    bsphere->radius = len_v3v3(bsphere->center, farpoint);
-  }
-  else if (projmat[2][0] == 0.0f && projmat[2][1] == 0.0f) {
-    /* Perspective with symmetrical frustum. */
-
-    /* We obtain the center and radius of the circumscribed circle of the
-     * isosceles trapezoid composed by the diagonals of the near and far clipping plane */
-
-    /* center of each clipping plane */
-    float mid_min[3], mid_max[3];
-    mid_v3_v3v3(mid_min, bbox.vec[3], bbox.vec[4]);
-    mid_v3_v3v3(mid_max, bbox.vec[2], bbox.vec[5]);
-
-    /* square length of the diagonals of each clipping plane */
-    float a_sq = len_squared_v3v3(bbox.vec[3], bbox.vec[4]);
-    float b_sq = len_squared_v3v3(bbox.vec[2], bbox.vec[5]);
-
-    /* distance squared between clipping planes */
-    float h_sq = len_squared_v3v3(mid_min, mid_max);
-
-    float fac = (4 * h_sq + b_sq - a_sq) / (8 * h_sq);
-
-    /* The goal is to get the smallest sphere,
-     * not the sphere that passes through each corner */
-    CLAMP(fac, 0.0f, 1.0f);
-
-    interp_v3_v3v3(bsphere->center, mid_min, mid_max, fac);
-
-    /* distance from the center to one of the points of the far plane (1, 2, 5, 6) */
-    bsphere->radius = len_v3v3(bsphere->center, bbox.vec[1]);
-  }
-  else {
-    /* Perspective with asymmetrical frustum. */
-
-    /* We put the sphere center on the line that goes from origin
-     * to the center of the far clipping plane. */
-
-    /* Detect which of the corner of the far clipping plane is the farthest to the origin */
-    float nfar[4];               /* most extreme far point in NDC space */
-    float farxy[2];              /* farpoint projection onto the near plane */
-    float farpoint[3] = {0.0f};  /* most extreme far point in camera coordinate */
-    float nearpoint[3];          /* most extreme near point in camera coordinate */
-    float farcenter[3] = {0.0f}; /* center of far cliping plane in camera coordinate */
-    float F = -1.0f, N;          /* square distance of far and near point to origin */
-    float f, n; /* distance of far and near point to z axis. f is always > 0 but n can be < 0 */
-    float e, s; /* far and near clipping distance (<0) */
-    float c;    /* slope of center line = distance of far clipping center
-                 * to z axis / far clipping distance. */
-    float z;    /* projection of sphere center on z axis (<0) */
-
-    /* Find farthest corner and center of far clip plane. */
-    float corner[3] = {1.0f, 1.0f, 1.0f}; /* in clip space */
-    for (int i = 0; i < 4; i++) {
-      float point[3];
-      mul_v3_project_m4_v3(point, projinv, corner);
-      float len = len_squared_v3(point);
-      if (len > F) {
-        copy_v3_v3(nfar, corner);
-        copy_v3_v3(farpoint, point);
-        F = len;
-      }
-      add_v3_v3(farcenter, point);
-      /* rotate by 90 degree to walk through the 4 points of the far clip plane */
-      float tmp = corner[0];
-      corner[0] = -corner[1];
-      corner[1] = tmp;
-    }
-
-    /* the far center is the average of the far clipping points */
-    mul_v3_fl(farcenter, 0.25f);
-    /* the extreme near point is the opposite point on the near clipping plane */
-    copy_v3_fl3(nfar, -nfar[0], -nfar[1], -1.0f);
-    mul_v3_project_m4_v3(nearpoint, projinv, nfar);
-    /* this is a frustum projection */
-    N = len_squared_v3(nearpoint);
-    e = farpoint[2];
-    s = nearpoint[2];
-    /* distance to view Z axis */
-    f = len_v2(farpoint);
-    /* get corresponding point on the near plane */
-    mul_v2_v2fl(farxy, farpoint, s / e);
-    /* this formula preserve the sign of n */
-    sub_v2_v2(nearpoint, farxy);
-    n = f * s / e - len_v2(nearpoint);
-    c = len_v2(farcenter) / e;
-    /* the big formula, it simplifies to (F-N)/(2(e-s)) for the symmetric case */
-    z = (F - N) / (2.0f * (e - s + c * (f - n)));
-
-    bsphere->center[0] = farcenter[0] * z / e;
-    bsphere->center[1] = farcenter[1] * z / e;
-    bsphere->center[2] = z;
-    bsphere->radius = len_v3v3(bsphere->center, farpoint);
-
-    /* Transform to world space. */
-    mul_m4_v3(viewinv, bsphere->center);
-  }
-
-  DST.clipping.updated = true;
+  DST.view_active = (view) ? view : DST.view_default;
 }
 
 /* Return True if the given BoundSphere intersect the current view frustum */
-bool DRW_culling_sphere_test(BoundSphere *bsphere)
+static bool draw_culling_sphere_test(const BoundSphere *frustum_bsphere,
+                                     const float (*frustum_planes)[4],
+                                     const BoundSphere *bsphere)
 {
-  draw_clipping_setup_from_view();
-
   /* Bypass test if radius is negative. */
   if (bsphere->radius < 0.0f) {
     return true;
   }
 
   /* Do a rough test first: Sphere VS Sphere intersect. */
-  BoundSphere *frustum_bsphere = &DST.clipping.frustum_bsphere;
   float center_dist_sq = len_squared_v3v3(bsphere->center, frustum_bsphere->center);
   float radius_sum = bsphere->radius + frustum_bsphere->radius;
   if (center_dist_sq > SQUARE(radius_sum)) {
@@ -674,26 +447,21 @@ bool DRW_culling_sphere_test(BoundSphere *bsphere)
   /* TODO order planes with sides first then far then near clip. Should be better culling
    * heuristic when sculpting. */
   for (int p = 0; p < 6; p++) {
-    float dist = plane_point_side_v3(DST.clipping.frustum_planes[p], bsphere->center);
+    float dist = plane_point_side_v3(frustum_planes[p], bsphere->center);
     if (dist < -bsphere->radius) {
       return false;
     }
   }
-
   return true;
 }
 
-/* Return True if the given BoundBox intersect the current view frustum.
- * bbox must be in world space. */
-bool DRW_culling_box_test(BoundBox *bbox)
+static bool draw_culling_box_test(const float (*frustum_planes)[4], const BoundBox *bbox)
 {
-  draw_clipping_setup_from_view();
-
   /* 6 view frustum planes */
   for (int p = 0; p < 6; p++) {
     /* 8 box vertices. */
     for (int v = 0; v < 8; v++) {
-      float dist = plane_point_side_v3(DST.clipping.frustum_planes[p], bbox->vec[v]);
+      float dist = plane_point_side_v3(frustum_planes[p], bbox->vec[v]);
       if (dist > 0.0f) {
         /* At least one point in front of this plane.
          * Go to next plane. */
@@ -705,139 +473,134 @@ bool DRW_culling_box_test(BoundBox *bbox)
       }
     }
   }
-
   return true;
 }
 
-/* Return True if the current view frustum is inside or intersect the given plane */
-bool DRW_culling_plane_test(float plane[4])
+static bool draw_culling_plane_test(const BoundBox *corners, const float plane[4])
 {
-  draw_clipping_setup_from_view();
-
   /* Test against the 8 frustum corners. */
   for (int c = 0; c < 8; c++) {
-    float dist = plane_point_side_v3(plane, DST.clipping.frustum_corners.vec[c]);
+    float dist = plane_point_side_v3(plane, corners->vec[c]);
     if (dist < 0.0f) {
       return true;
     }
   }
-
   return false;
 }
 
-void DRW_culling_frustum_corners_get(BoundBox *corners)
+/* Return True if the given BoundSphere intersect the current view frustum.
+ * bsphere must be in world space. */
+bool DRW_culling_sphere_test(const BoundSphere *bsphere)
 {
-  draw_clipping_setup_from_view();
-  memcpy(corners, &DST.clipping.frustum_corners, sizeof(BoundBox));
+  return draw_culling_sphere_test(
+      &DST.view_active->frustum_bsphere, DST.view_active->frustum_planes, bsphere);
 }
 
-/* See draw_clipping_setup_from_view() for the plane order. */
-void DRW_culling_frustum_planes_get(float planes[6][4])
+/* Return True if the given BoundBox intersect the current view frustum.
+ * bbox must be in world space. */
+bool DRW_culling_box_test(const BoundBox *bbox)
 {
-  draw_clipping_setup_from_view();
-  memcpy(planes, &DST.clipping.frustum_planes, sizeof(DST.clipping.frustum_planes));
+  return draw_culling_box_test(DST.view_active->frustum_planes, bbox);
 }
 
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Draw (DRW_draw)
- * \{ */
-
-static void draw_visibility_eval(DRWCallState *st)
+/* Return True if the view frustum is inside or intersect the given plane.
+ * plane must be in world space. */
+bool DRW_culling_plane_test(const float plane[4])
 {
-  bool culled = st->flag & DRW_CALL_CULLED;
-
-  if (st->cache_id != DST.state_cache_id) {
-    /* Update culling result for this view. */
-    culled = !DRW_culling_sphere_test(&st->bsphere);
-  }
+  return draw_culling_plane_test(&DST.view_active->frustum_corners, plane);
+}
 
-  if (st->visibility_cb) {
-    culled = !st->visibility_cb(!culled, st->user_data);
-  }
+void DRW_culling_frustum_corners_get(BoundBox *corners)
+{
+  *corners = DST.view_active->frustum_corners;
+}
 
-  SET_FLAG_FROM_TEST(st->flag, culled, DRW_CALL_CULLED);
+void DRW_culling_frustum_planes_get(float planes[6][4])
+{
+  memcpy(planes, DST.view_active->frustum_planes, sizeof(float) * 6 * 4);
 }
 
-static void draw_matrices_model_prepare(DRWCallState *st)
+static void draw_compute_culling(DRWView *view)
 {
-  if (st->cache_id == DST.state_cache_id) {
-    /* Values are already updated for this view. */
-    return;
-  }
-  else {
-    st->cache_id = DST.state_cache_id;
-  }
+  view = view->parent ? view->parent : view;
 
-  /* No need to go further the call will not be used. */
-  if ((st->flag & DRW_CALL_CULLED) != 0 && (st->flag & DRW_CALL_BYPASS_CULLING) == 0) {
+  /* TODO(fclem) multithread this. */
+  /* TODO(fclem) compute all dirty views at once. */
+  if (!view->is_dirty) {
     return;
   }
 
-  if (st->matflag & DRW_CALL_MODELVIEWPROJECTION) {
-    mul_m4_m4m4(st->modelviewprojection, DST.view_data.matstate.mat[DRW_MAT_PERS], st->model);
+  BLI_memblock_iter iter;
+  BLI_memblock_iternew(DST.vmempool->cullstates, &iter);
+  DRWCullingState *cull;
+  while ((cull = BLI_memblock_iterstep(&iter))) {
+    if (cull->bsphere.radius < 0.0) {
+      cull->mask = 0;
+    }
+    else {
+      bool culled = !draw_culling_sphere_test(
+          &view->frustum_bsphere, view->frustum_planes, &cull->bsphere);
+
+#ifdef DRW_DEBUG_CULLING
+      if (G.debug_value != 0) {
+        if (culled) {
+          DRW_debug_sphere(
+              cull->bsphere.center, cull->bsphere.radius, (const float[4]){1, 0, 0, 1});
+        }
+        else {
+          DRW_debug_sphere(
+              cull->bsphere.center, cull->bsphere.radius, (const float[4]){0, 1, 0, 1});
+        }
+      }
+#endif
+
+      if (view->visibility_fn) {
+        culled = !view->visibility_fn(!culled, cull->user_data);
+      }
+
+      SET_FLAG_FROM_TEST(cull->mask, culled, view->culling_mask);
+    }
   }
+
+  view->is_dirty = false;
 }
 
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Draw (DRW_draw)
+ * \{ */
+
 static void draw_geometry_prepare(DRWShadingGroup *shgroup, DRWCall *call)
 {
-  /* step 1 : bind object dependent matrices */
-  if (call != NULL) {
-    DRWCallState *state = call->state;
+  BLI_assert(call);
+  DRWCallState *state = call->state;
 
-    if (shgroup->model != -1) {
-      GPU_shader_uniform_vector(shgroup->shader, shgroup->model, 16, 1, (float *)state->model);
-    }
-    if (shgroup->modelinverse != -1) {
-      GPU_shader_uniform_vector(
-          shgroup->shader, shgroup->modelinverse, 16, 1, (float *)state->modelinverse);
-    }
-    if (shgroup->modelviewprojection != -1) {
-      GPU_shader_uniform_vector(shgroup->shader,
-                                shgroup->modelviewprojection,
-                                16,
-                                1,
-                                (float *)state->modelviewprojection);
-    }
-    if (shgroup->objectinfo != -1) {
-      float infos[4];
-      infos[0] = state->ob_index;
-      // infos[1]; /* UNUSED. */
-      infos[2] = state->ob_random;
-      infos[3] = (state->flag & DRW_CALL_NEGSCALE) ? -1.0f : 1.0f;
-      GPU_shader_uniform_vector(shgroup->shader, shgroup->objectinfo, 4, 1, (float *)infos);
-    }
-    if (shgroup->orcotexfac != -1) {
-      GPU_shader_uniform_vector(
-          shgroup->shader, shgroup->orcotexfac, 3, 2, (float *)state->orcotexfac);
-    }
+  if (shgroup->model != -1) {
+    GPU_shader_uniform_vector(shgroup->shader, shgroup->model, 16, 1, (float *)state->model);
   }
-  else {
-    /* For instancing and batching. */
-    float unitmat[4][4];
-    unit_m4(unitmat);
-
-    if (shgroup->model != -1) {
-      GPU_shader_uniform_vector(shgroup->shader, shgroup->model, 16, 1, (float *)unitmat);
-    }
-    if (shgroup->modelinverse != -1) {
-      GPU_shader_uniform_vector(shgroup->shader, shgroup->modelinverse, 16, 1, (float *)unitmat);
-    }
-    if (shgroup->modelviewprojection != -1) {
-      GPU_shader_uniform_vector(shgroup->shader,
-                                shgroup->modelviewprojection,
-                                16,
-                                1,
-                                (float *)DST.view_data.matstate.mat[DRW_MAT_PERS]);
-    }
-    if (shgroup->objectinfo != -1) {
-      GPU_shader_uniform_vector(shgroup->shader, shgroup->objectinfo, 4, 1, (float *)unitmat);
-    }
-    if (shgroup->orcotexfac != -1) {
-      float orcofacs[2][3] = {{0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}};
-      GPU_shader_uniform_vector(shgroup->shader, shgroup->orcotexfac, 3, 2, (float *)orcofacs);
-    }
+  if (shgroup->modelinverse != -1) {
+    GPU_shader_uniform_vector(
+        shgroup->shader, shgroup->modelinverse, 16, 1, (float *)state->modelinverse);
+  }
+  if (shgroup->objectinfo != -1) {
+    float infos[4];
+    infos[0] = state->ob_index;
+    // infos[1]; /* UNUSED. */
+    infos[2] = state->ob_random;
+    infos[3] = (state->flag & DRW_CALL_NEGSCALE) ? -1.0f : 1.0f;
+    GPU_shader_uniform_vector(shgroup->shader, shgroup->objectinfo, 4, 1, (float *)infos);
+  }
+  if (shgroup->orcotexfac != -1) {
+    GPU_shader_uniform_vector(
+        shgroup->shader, shgroup->orcotexfac, 3, 2, (float *)state->orcotexfac);
+  }
+  /* Still supported for compatibility with gpu_shader_* but should be forbidden
+   * and is slow (since it does not cache the result). */
+  if (shgroup->modelviewprojection != -1) {
+    float mvp[4][4];
+    mul_m4_m4m4(mvp, DST.view_active->storage.matstate.persmat, state->model);
+    GPU_shader_uniform_vector(shgroup->shader, shgroup->modelviewprojection, 16, 1, (float *)mvp);
   }
 }
 
@@ -1161,10 +924,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
     int callid = 0;
     for (DRWCall *call = shgroup->calls.first; call; call = call->next) {
 
-      /* OPTI/IDEA(clem): Do this preparation in another thread. */
-      draw_visibility_eval(call->state);
-
-      if ((call->state->flag & DRW_CALL_CULLED) != 0) {
+      if (draw_call_is_culled(call, DST.view_active)) {
         continue;
       }
 
@@ -1206,29 +966,13 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
 
 static void drw_update_view(void)
 {
-  if (DST.dirty_mat) {
-    DST.state_cache_id++;
-    DST.dirty_mat = false;
-
-    DRW_uniformbuffer_update(G_draw.view_ubo, &DST.view_data);
-
-    /* Catch integer wrap around. */
-    if (UNLIKELY(DST.state_cache_id == 0)) {
-      DST.state_cache_id = 1;
-      /* We must reset all CallStates to ensure that not
-       * a single one stayed with cache_id equal to 1. */
-      BLI_memblock_iter iter;
-      DRWCallState *state;
-      BLI_memblock_iternew(DST.vmempool->states, &iter);
-      while ((state = BLI_memblock_iterstep(&iter))) {
-        state->cache_id = 0;
-      }
-    }
+  /* TODO(fclem) update a big UBO and only bind ranges here. */
+  DRW_uniformbuffer_update(G_draw.view_ubo, &DST.view_active->storage);
 
-    /* TODO dispatch threads to compute matrices/culling */
-  }
+  /* TODO get rid of this. */
+  DST.view_storage_cpy = DST.view_active->storage;
 
-  draw_clipping_setup_from_view();
+  draw_compute_culling(DST.view_active);
 }
 
 static void drw_draw_pass_ex(DRWPass *pass,
index 1bd94cb..2c22acd 100644 (file)
@@ -330,7 +330,8 @@ static DRWPass *edit_mesh_create_overlay_pass(float *face_alpha,
 
   float winmat[4][4];
   float viewdist = rv3d->dist;
-  DRW_viewport_matrix_get(winmat, DRW_MAT_WIN);
+  DRW_view_winmat_get(NULL, winmat, false);
+
   /* special exception for ortho camera (viewdist isnt used for perspective cameras) */
   if (rv3d->persp == RV3D_CAMOB && rv3d->is_persp == false) {
     viewdist = 1.0f / max_ff(fabsf(rv3d->winmat[0][0]), fabsf(rv3d->winmat[1][1]));
index 9802b91..845c615 100644 (file)
@@ -9,9 +9,10 @@ layout(std140) uniform viewBlock
   mat4 ProjectionMatrix;
   mat4 ProjectionMatrixInverse;
 
-  vec4 CameraTexCoFactors;
+  vec4 clipPlanes[6];
 
-  vec4 clipPlanes[2];
+  /* TODO move it elsewhere. */
+  vec4 CameraTexCoFactors;
 };
 
 uniform mat4 ModelMatrix;
index e61cfe3..e760027 100644 (file)
@@ -39,8 +39,10 @@ typedef struct GPUViewport GPUViewport;
 typedef struct ViewportMemoryPool {
   struct BLI_memblock *calls;
   struct BLI_memblock *states;
+  struct BLI_memblock *cullstates;
   struct BLI_memblock *shgroups;
   struct BLI_memblock *uniforms;
+  struct BLI_memblock *views;
   struct BLI_memblock *passes;
   struct BLI_memblock *images;
 } ViewportMemoryPool;
index b825819..4fd439f 100644 (file)
@@ -625,12 +625,18 @@ void GPU_viewport_free(GPUViewport *viewport)
   if (viewport->vmempool.states != NULL) {
     BLI_memblock_destroy(viewport->vmempool.states, NULL);
   }
+  if (viewport->vmempool.cullstates != NULL) {
+    BLI_memblock_destroy(viewport->vmempool.cullstates, NULL);
+  }
   if (viewport->vmempool.shgroups != NULL) {
     BLI_memblock_destroy(viewport->vmempool.shgroups, NULL);
   }
   if (viewport->vmempool.uniforms != NULL) {
     BLI_memblock_destroy(viewport->vmempool.uniforms, NULL);
   }
+  if (viewport->vmempool.views != NULL) {
+    BLI_memblock_destroy(viewport->vmempool.views, NULL);
+  }
   if (viewport->vmempool.passes != NULL) {
     BLI_memblock_destroy(viewport->vmempool.passes, NULL);
   }