* Brings us closer to core profile, all matrices are working, and apart from a problem with text drawing, Blender is working fine. * Reduce the coding overhead of having to setup/teardown when alternating between 2D and 3D drawing sessions. * Gives us fewer modes and states we need to keep track of. Unfortunatelly this also "rejects a fundamental change" the original design was trying to make - that 2D is different from 3D and deserves its own best implementation. That said, it is still aligned with the function API design as originally implemented (i.e., it still uses gpuTranslate2D, ...). Finally, if you build with core profile and this patch you get: https://developer.blender.org/F545352 [The text glitch is an unrelated issue]. Reviewers: merwin, sergey, brecht Differential Revision: https://developer.blender.org/D2626
1936 lines
53 KiB
C
1936 lines
53 KiB
C
/*
|
|
* 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_dynstr.h"
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_rect.h"
|
|
#include "BLI_string.h"
|
|
|
|
#include "BIF_glutil.h"
|
|
|
|
#include "BKE_global.h"
|
|
|
|
#include "BLT_translation.h"
|
|
#include "BLF_api.h"
|
|
|
|
#include "DRW_engine.h"
|
|
#include "DRW_render.h"
|
|
|
|
#include "DNA_view3d_types.h"
|
|
#include "DNA_screen_types.h"
|
|
|
|
#include "ED_space_api.h"
|
|
#include "ED_screen.h"
|
|
|
|
#include "GPU_batch.h"
|
|
#include "GPU_draw.h"
|
|
#include "GPU_extensions.h"
|
|
#include "GPU_framebuffer.h"
|
|
#include "GPU_lamp.h"
|
|
#include "GPU_shader.h"
|
|
#include "GPU_texture.h"
|
|
#include "GPU_uniformbuffer.h"
|
|
#include "GPU_viewport.h"
|
|
#include "GPU_matrix.h"
|
|
|
|
#include "PIL_time.h"
|
|
|
|
#include "RE_engine.h"
|
|
|
|
#include "UI_resources.h"
|
|
|
|
#include "draw_mode_engines.h"
|
|
#include "clay.h"
|
|
#include "eevee.h"
|
|
|
|
#define MAX_ATTRIB_NAME 32
|
|
#define MAX_PASS_NAME 32
|
|
|
|
extern char datatoc_gpu_shader_2D_vert_glsl[];
|
|
extern char datatoc_gpu_shader_3D_vert_glsl[];
|
|
extern char datatoc_gpu_shader_fullscreen_vert_glsl[];
|
|
|
|
/* Structures */
|
|
typedef enum {
|
|
DRW_UNIFORM_BOOL,
|
|
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, *prev;
|
|
DRWUniformType type;
|
|
int location;
|
|
int length;
|
|
int arraysize;
|
|
int bindloc;
|
|
const void *value;
|
|
};
|
|
|
|
typedef struct DRWAttrib {
|
|
struct DRWAttrib *next, *prev;
|
|
char name[MAX_ATTRIB_NAME];
|
|
int location;
|
|
int format_id;
|
|
int size; /* number of component */
|
|
int type;
|
|
} DRWAttrib;
|
|
|
|
struct DRWInterface {
|
|
ListBase uniforms; /* DRWUniform */
|
|
ListBase attribs; /* DRWAttrib */
|
|
int attribs_count;
|
|
int attribs_stride;
|
|
int attribs_size[16];
|
|
int attribs_loc[16];
|
|
/* matrices locations */
|
|
int model;
|
|
int modelview;
|
|
int projection;
|
|
int view;
|
|
int modelviewprojection;
|
|
int viewprojection;
|
|
int normal;
|
|
int worldnormal;
|
|
int eye;
|
|
/* Dynamic batch */
|
|
GLuint instance_vbo;
|
|
int instance_count;
|
|
VertexFormat vbo_format;
|
|
};
|
|
|
|
struct DRWPass {
|
|
ListBase shgroups; /* DRWShadingGroup */
|
|
DRWState state;
|
|
char name[MAX_PASS_NAME];
|
|
/* use two query to not stall the cpu waiting for queries to complete */
|
|
unsigned int timer_queries[2];
|
|
/* alternate between front and back query */
|
|
unsigned int front_idx;
|
|
unsigned int back_idx;
|
|
bool wasdrawn; /* if it was drawn during this frame */
|
|
};
|
|
|
|
typedef struct DRWCall {
|
|
struct DRWCall *next, *prev;
|
|
Batch *geometry;
|
|
float (*obmat)[4];
|
|
} DRWCall;
|
|
|
|
typedef struct DRWDynamicCall {
|
|
struct DRWDynamicCall *next, *prev;
|
|
const void *data[];
|
|
} DRWDynamicCall;
|
|
|
|
struct DRWShadingGroup {
|
|
struct DRWShadingGroup *next, *prev;
|
|
|
|
struct GPUShader *shader; /* Shader to bind */
|
|
struct DRWInterface *interface; /* Uniforms pointers */
|
|
ListBase calls; /* DRWCall or DRWDynamicCall depending of type*/
|
|
DRWState state; /* State changes for this batch only */
|
|
int type;
|
|
|
|
Batch *instance_geom; /* Geometry to instance */
|
|
Batch *batch_geom; /* Result of call batching */
|
|
};
|
|
|
|
/* Used by DRWShadingGroup.type */
|
|
enum {
|
|
DRW_SHG_NORMAL,
|
|
DRW_SHG_POINT_BATCH,
|
|
DRW_SHG_LINE_BATCH,
|
|
DRW_SHG_INSTANCE,
|
|
};
|
|
|
|
/* only 16 bits long */
|
|
enum {
|
|
STENCIL_SELECT = (1 << 0),
|
|
STENCIL_ACTIVE = (1 << 1),
|
|
};
|
|
|
|
/* Render State */
|
|
static struct DRWGlobalState {
|
|
/* Rendering state */
|
|
GPUShader *shader;
|
|
ListBase bound_texs;
|
|
int tex_bind_id;
|
|
|
|
/* Per viewport */
|
|
GPUViewport *viewport;
|
|
struct GPUFrameBuffer *default_framebuffer;
|
|
float size[2];
|
|
float screenvecs[2][3];
|
|
float pixsize;
|
|
|
|
/* Current rendering context */
|
|
const struct bContext *context;
|
|
ListBase enabled_engines; /* RenderEngineType */
|
|
} DST = {NULL};
|
|
|
|
ListBase DRW_engines = {NULL, NULL};
|
|
|
|
/* ***************************************** TEXTURES ******************************************/
|
|
static void drw_texture_get_format(DRWTextureFormat format, GPUTextureFormat *data_type, int *channels)
|
|
{
|
|
switch (format) {
|
|
case DRW_TEX_RGBA_8: *data_type = GPU_RGBA8; break;
|
|
case DRW_TEX_RGBA_16: *data_type = GPU_RGBA16F; break;
|
|
case DRW_TEX_RG_16: *data_type = GPU_RG16F; break;
|
|
case DRW_TEX_RG_32: *data_type = GPU_RG32F; break;
|
|
case DRW_TEX_R_8: *data_type = GPU_R8; break;
|
|
case DRW_TEX_R_16: *data_type = GPU_R16F; break;
|
|
#if 0
|
|
case DRW_TEX_RGBA_32: *data_type = GPU_RGBA32F; break;
|
|
case DRW_TEX_RGB_8: *data_type = GPU_RGB8; break;
|
|
case DRW_TEX_RGB_16: *data_type = GPU_RGB16F; break;
|
|
case DRW_TEX_RGB_32: *data_type = GPU_RGB32F; break;
|
|
case DRW_TEX_RG_8: *data_type = GPU_RG8; break;
|
|
case DRW_TEX_R_32: *data_type = GPU_R32F; break;
|
|
#endif
|
|
case DRW_TEX_DEPTH_16: *data_type = GPU_DEPTH_COMPONENT16; break;
|
|
case DRW_TEX_DEPTH_24: *data_type = GPU_DEPTH_COMPONENT24; break;
|
|
case DRW_TEX_DEPTH_32: *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:
|
|
*channels = 4;
|
|
break;
|
|
case DRW_TEX_RGB_8:
|
|
case DRW_TEX_RGB_16:
|
|
case DRW_TEX_RGB_32:
|
|
*channels = 3;
|
|
break;
|
|
case DRW_TEX_RG_8:
|
|
case DRW_TEX_RG_16:
|
|
case DRW_TEX_RG_32:
|
|
*channels = 2;
|
|
break;
|
|
default:
|
|
*channels = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void drw_texture_set_parameters(GPUTexture *tex, DRWTextureFlag flags)
|
|
{
|
|
GPU_texture_bind(tex, 0);
|
|
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_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_free(GPUTexture *tex)
|
|
{
|
|
GPU_texture_free(tex);
|
|
}
|
|
|
|
|
|
/* ************************************ UNIFORM BUFFER OBJECT **********************************/
|
|
|
|
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);
|
|
}
|
|
|
|
/* ****************************************** SHADERS ******************************************/
|
|
|
|
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;
|
|
|
|
DynStr *ds_vert = BLI_dynstr_new();
|
|
BLI_dynstr_append(ds_vert, lib);
|
|
BLI_dynstr_append(ds_vert, vert);
|
|
vert_with_lib = BLI_dynstr_get_cstring(ds_vert);
|
|
BLI_dynstr_free(ds_vert);
|
|
|
|
DynStr *ds_frag = BLI_dynstr_new();
|
|
BLI_dynstr_append(ds_frag, lib);
|
|
BLI_dynstr_append(ds_frag, frag);
|
|
frag_with_lib = BLI_dynstr_get_cstring(ds_frag);
|
|
BLI_dynstr_free(ds_frag);
|
|
|
|
if (geom) {
|
|
DynStr *ds_geom = BLI_dynstr_new();
|
|
BLI_dynstr_append(ds_geom, lib);
|
|
BLI_dynstr_append(ds_geom, geom);
|
|
geom_with_lib = BLI_dynstr_get_cstring(ds_geom);
|
|
BLI_dynstr_free(ds_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);
|
|
}
|
|
|
|
/* ***************************************** INTERFACE ******************************************/
|
|
|
|
static DRWInterface *DRW_interface_create(GPUShader *shader)
|
|
{
|
|
DRWInterface *interface = MEM_mallocN(sizeof(DRWInterface), "DRWInterface");
|
|
|
|
interface->model = GPU_shader_get_uniform(shader, "ModelMatrix");
|
|
interface->modelview = GPU_shader_get_uniform(shader, "ModelViewMatrix");
|
|
interface->projection = GPU_shader_get_uniform(shader, "ProjectionMatrix");
|
|
interface->view = GPU_shader_get_uniform(shader, "ViewMatrix");
|
|
interface->viewprojection = GPU_shader_get_uniform(shader, "ViewProjectionMatrix");
|
|
interface->modelviewprojection = GPU_shader_get_uniform(shader, "ModelViewProjectionMatrix");
|
|
interface->normal = GPU_shader_get_uniform(shader, "NormalMatrix");
|
|
interface->worldnormal = GPU_shader_get_uniform(shader, "WorldNormalMatrix");
|
|
interface->eye = GPU_shader_get_uniform(shader, "eye");
|
|
interface->instance_count = 0;
|
|
interface->attribs_count = 0;
|
|
interface->attribs_stride = 0;
|
|
interface->instance_vbo = 0;
|
|
|
|
memset(&interface->vbo_format, 0, sizeof(VertexFormat));
|
|
|
|
BLI_listbase_clear(&interface->uniforms);
|
|
BLI_listbase_clear(&interface->attribs);
|
|
|
|
return interface;
|
|
}
|
|
|
|
static void DRW_interface_uniform(DRWShadingGroup *shgroup, const char *name,
|
|
DRWUniformType type, const void *value, int length, int arraysize, int bindloc)
|
|
{
|
|
DRWUniform *uni = MEM_mallocN(sizeof(DRWUniform), "DRWUniform");
|
|
|
|
if (type == DRW_UNIFORM_BLOCK) {
|
|
uni->location = GPU_shader_get_uniform_block(shgroup->shader, name);
|
|
}
|
|
else {
|
|
uni->location = GPU_shader_get_uniform(shgroup->shader, name);
|
|
}
|
|
|
|
uni->type = type;
|
|
uni->value = value;
|
|
uni->length = length;
|
|
uni->arraysize = arraysize;
|
|
uni->bindloc = bindloc; /* for textures */
|
|
|
|
if (uni->location == -1) {
|
|
if (G.debug & G_DEBUG)
|
|
fprintf(stderr, "Uniform '%s' not found!\n", name);
|
|
|
|
MEM_freeN(uni);
|
|
return;
|
|
}
|
|
|
|
BLI_addtail(&shgroup->interface->uniforms, uni);
|
|
}
|
|
|
|
static void DRW_interface_attrib(DRWShadingGroup *shgroup, const char *name, DRWAttribType type, int size)
|
|
{
|
|
DRWAttrib *attrib = MEM_mallocN(sizeof(DRWAttrib), "DRWAttrib");
|
|
GLuint program = GPU_shader_get_program(shgroup->shader);
|
|
|
|
attrib->location = glGetAttribLocation(program, name);
|
|
attrib->type = type;
|
|
attrib->size = size;
|
|
|
|
if (attrib->location == -1) {
|
|
if (G.debug & G_DEBUG)
|
|
fprintf(stderr, "Attribute '%s' not found!\n", name);
|
|
|
|
MEM_freeN(attrib);
|
|
return;
|
|
}
|
|
|
|
BLI_assert(BLI_strnlen(name, 32) < 32);
|
|
BLI_strncpy(attrib->name, name, 32);
|
|
|
|
shgroup->interface->attribs_count += 1;
|
|
|
|
BLI_addtail(&shgroup->interface->attribs, attrib);
|
|
}
|
|
|
|
void DRW_get_dfdy_factors(float dfdyfac[2])
|
|
{
|
|
GPU_get_dfdy_factors(dfdyfac);
|
|
}
|
|
|
|
/* ***************************************** SHADING GROUP ******************************************/
|
|
|
|
DRWShadingGroup *DRW_shgroup_create(struct GPUShader *shader, DRWPass *pass)
|
|
{
|
|
DRWShadingGroup *shgroup = MEM_mallocN(sizeof(DRWShadingGroup), "DRWShadingGroup");
|
|
|
|
shgroup->type = DRW_SHG_NORMAL;
|
|
shgroup->shader = shader;
|
|
shgroup->interface = DRW_interface_create(shader);
|
|
shgroup->state = 0;
|
|
shgroup->batch_geom = NULL;
|
|
shgroup->instance_geom = NULL;
|
|
|
|
BLI_addtail(&pass->shgroups, shgroup);
|
|
BLI_listbase_clear(&shgroup->calls);
|
|
|
|
return shgroup;
|
|
}
|
|
|
|
DRWShadingGroup *DRW_shgroup_instance_create(struct GPUShader *shader, DRWPass *pass, 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;
|
|
}
|
|
|
|
void DRW_shgroup_free(struct DRWShadingGroup *shgroup)
|
|
{
|
|
BLI_freelistN(&shgroup->calls);
|
|
BLI_freelistN(&shgroup->interface->uniforms);
|
|
BLI_freelistN(&shgroup->interface->attribs);
|
|
|
|
if (shgroup->interface->instance_vbo) {
|
|
glDeleteBuffers(1, &shgroup->interface->instance_vbo);
|
|
}
|
|
|
|
MEM_freeN(shgroup->interface);
|
|
|
|
BATCH_DISCARD_ALL_SAFE(shgroup->batch_geom);
|
|
}
|
|
|
|
void DRW_shgroup_call_add(DRWShadingGroup *shgroup, Batch *geom, float (*obmat)[4])
|
|
{
|
|
BLI_assert(geom != NULL);
|
|
|
|
DRWCall *call = MEM_callocN(sizeof(DRWCall), "DRWCall");
|
|
|
|
call->obmat = obmat;
|
|
call->geometry = geom;
|
|
|
|
BLI_addtail(&shgroup->calls, call);
|
|
}
|
|
|
|
void DRW_shgroup_dynamic_call_add_array(DRWShadingGroup *shgroup, const void *attr[], unsigned int attr_len)
|
|
{
|
|
DRWInterface *interface = shgroup->interface;
|
|
unsigned int data_size = sizeof(void *) * interface->attribs_count;
|
|
int size = sizeof(ListBase) + data_size;
|
|
|
|
DRWDynamicCall *call = MEM_callocN(size, "DRWDynamicCall");
|
|
|
|
BLI_assert(attr_len == interface->attribs_count);
|
|
|
|
memcpy((void *) call->data, attr, data_size);
|
|
|
|
interface->instance_count += 1;
|
|
|
|
BLI_addtail(&shgroup->calls, call);
|
|
}
|
|
|
|
/* Make sure you know what you do when using this,
|
|
* State is not revert back at the end of the shgroup */
|
|
void DRW_shgroup_state_set(DRWShadingGroup *shgroup, DRWState state)
|
|
{
|
|
shgroup->state = state;
|
|
}
|
|
|
|
void DRW_shgroup_attrib_float(DRWShadingGroup *shgroup, const char *name, int size)
|
|
{
|
|
DRW_interface_attrib(shgroup, name, DRW_ATTRIB_FLOAT, size);
|
|
}
|
|
|
|
void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup, const char *name, const GPUTexture *tex, int loc)
|
|
{
|
|
DRW_interface_uniform(shgroup, name, DRW_UNIFORM_TEXTURE, tex, 0, 0, loc);
|
|
}
|
|
|
|
void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, const char *name, const GPUUniformBuffer *ubo, int loc)
|
|
{
|
|
DRW_interface_uniform(shgroup, name, DRW_UNIFORM_BLOCK, ubo, 0, 0, loc);
|
|
}
|
|
|
|
void DRW_shgroup_uniform_buffer(DRWShadingGroup *shgroup, const char *name, GPUTexture **tex, int loc)
|
|
{
|
|
DRW_interface_uniform(shgroup, name, DRW_UNIFORM_BUFFER, tex, 0, 0, loc);
|
|
}
|
|
|
|
void DRW_shgroup_uniform_bool(DRWShadingGroup *shgroup, const char *name, const bool *value, int arraysize)
|
|
{
|
|
DRW_interface_uniform(shgroup, name, DRW_UNIFORM_BOOL, value, 1, arraysize, 0);
|
|
}
|
|
|
|
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, 0);
|
|
}
|
|
|
|
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, 0);
|
|
}
|
|
|
|
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, 0);
|
|
}
|
|
|
|
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, 0);
|
|
}
|
|
|
|
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, 0);
|
|
}
|
|
|
|
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, 0);
|
|
}
|
|
|
|
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, 0);
|
|
}
|
|
|
|
void DRW_shgroup_uniform_mat3(DRWShadingGroup *shgroup, const char *name, const float *value)
|
|
{
|
|
DRW_interface_uniform(shgroup, name, DRW_UNIFORM_MAT3, value, 9, 1, 0);
|
|
}
|
|
|
|
void DRW_shgroup_uniform_mat4(DRWShadingGroup *shgroup, const char *name, const float *value)
|
|
{
|
|
DRW_interface_uniform(shgroup, name, DRW_UNIFORM_MAT4, value, 16, 1, 0);
|
|
}
|
|
|
|
#ifdef WITH_CLAY_ENGINE
|
|
|
|
/* Creates a VBO containing OGL primitives for all DRWDynamicCall */
|
|
static void shgroup_dynamic_batch(DRWShadingGroup *shgroup)
|
|
{
|
|
DRWInterface *interface = shgroup->interface;
|
|
int nbr = interface->instance_count;
|
|
|
|
PrimitiveType type = (shgroup->type == DRW_SHG_POINT_BATCH) ? PRIM_POINTS : PRIM_LINES;
|
|
|
|
if (nbr == 0)
|
|
return;
|
|
|
|
/* Upload Data */
|
|
if (interface->vbo_format.attrib_ct == 0) {
|
|
for (DRWAttrib *attrib = interface->attribs.first; attrib; attrib = attrib->next) {
|
|
BLI_assert(attrib->size <= 4); /* matrices have no place here for now */
|
|
if (attrib->type == DRW_ATTRIB_FLOAT) {
|
|
attrib->format_id = VertexFormat_add_attrib(&interface->vbo_format, attrib->name, COMP_F32, attrib->size, KEEP_FLOAT);
|
|
}
|
|
else if (attrib->type == DRW_ATTRIB_INT) {
|
|
attrib->format_id = VertexFormat_add_attrib(&interface->vbo_format, attrib->name, COMP_I8, attrib->size, KEEP_INT);
|
|
}
|
|
else {
|
|
BLI_assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
VertexBuffer *vbo = VertexBuffer_create_with_format(&interface->vbo_format);
|
|
VertexBuffer_allocate_data(vbo, nbr);
|
|
|
|
int j = 0;
|
|
for (DRWDynamicCall *call = shgroup->calls.first; call; call = call->next, j++) {
|
|
int i = 0;
|
|
for (DRWAttrib *attrib = interface->attribs.first; attrib; attrib = attrib->next, i++) {
|
|
VertexBuffer_set_attrib(vbo, attrib->format_id, j, call->data[i]);
|
|
}
|
|
}
|
|
|
|
/* TODO make the batch dynamic instead of freeing it every times */
|
|
if (shgroup->batch_geom)
|
|
Batch_discard_all(shgroup->batch_geom);
|
|
|
|
shgroup->batch_geom = Batch_create(type, vbo, NULL);
|
|
}
|
|
|
|
static void shgroup_dynamic_instance(DRWShadingGroup *shgroup)
|
|
{
|
|
int i = 0;
|
|
int offset = 0;
|
|
DRWInterface *interface = shgroup->interface;
|
|
int vert_nbr = interface->instance_count;
|
|
int buffer_size = 0;
|
|
|
|
if (vert_nbr == 0) {
|
|
if (interface->instance_vbo) {
|
|
glDeleteBuffers(1, &interface->instance_vbo);
|
|
interface->instance_vbo = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* only once */
|
|
if (interface->attribs_stride == 0) {
|
|
for (DRWAttrib *attrib = interface->attribs.first; attrib; attrib = attrib->next, i++) {
|
|
BLI_assert(attrib->type == DRW_ATTRIB_FLOAT); /* Only float for now */
|
|
interface->attribs_stride += attrib->size;
|
|
interface->attribs_size[i] = attrib->size;
|
|
interface->attribs_loc[i] = attrib->location;
|
|
}
|
|
}
|
|
|
|
/* Gather Data */
|
|
buffer_size = sizeof(float) * interface->attribs_stride * vert_nbr;
|
|
float *data = MEM_mallocN(buffer_size, "Instance VBO data");
|
|
|
|
for (DRWDynamicCall *call = shgroup->calls.first; call; call = call->next) {
|
|
for (int j = 0; j < interface->attribs_count; ++j) {
|
|
memcpy(data + offset, call->data[j], sizeof(float) * interface->attribs_size[j]);
|
|
offset += interface->attribs_size[j];
|
|
}
|
|
}
|
|
|
|
/* TODO poke mike to add this to gawain */
|
|
if (interface->instance_vbo) {
|
|
glDeleteBuffers(1, &interface->instance_vbo);
|
|
interface->instance_vbo = 0;
|
|
}
|
|
|
|
glGenBuffers(1, &interface->instance_vbo);
|
|
glBindBuffer(GL_ARRAY_BUFFER, interface->instance_vbo);
|
|
glBufferData(GL_ARRAY_BUFFER, buffer_size, data, GL_STATIC_DRAW);
|
|
|
|
MEM_freeN(data);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
#endif /* WITH_CLAY_ENGINE */
|
|
|
|
/* ***************************************** PASSES ******************************************/
|
|
|
|
DRWPass *DRW_pass_create(const char *name, DRWState state)
|
|
{
|
|
DRWPass *pass = MEM_callocN(sizeof(DRWPass), name);
|
|
pass->state = state;
|
|
BLI_strncpy(pass->name, name, MAX_PASS_NAME);
|
|
|
|
BLI_listbase_clear(&pass->shgroups);
|
|
|
|
return pass;
|
|
}
|
|
|
|
void DRW_pass_free(DRWPass *pass)
|
|
{
|
|
for (DRWShadingGroup *shgroup = pass->shgroups.first; shgroup; shgroup = shgroup->next) {
|
|
DRW_shgroup_free(shgroup);
|
|
}
|
|
|
|
glDeleteQueries(2, pass->timer_queries);
|
|
BLI_freelistN(&pass->shgroups);
|
|
}
|
|
|
|
/* ****************************************** DRAW ******************************************/
|
|
|
|
#ifdef WITH_CLAY_ENGINE
|
|
static void set_state(DRWState flag, const bool reset)
|
|
{
|
|
/* TODO Keep track of the state and only revert what is needed */
|
|
|
|
if (reset) {
|
|
/* Depth Write */
|
|
if (flag & DRW_STATE_WRITE_DEPTH)
|
|
glDepthMask(GL_TRUE);
|
|
else
|
|
glDepthMask(GL_FALSE);
|
|
|
|
/* Color Write */
|
|
if (flag & DRW_STATE_WRITE_COLOR)
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
else
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
|
|
|
/* Backface Culling */
|
|
if (flag & DRW_STATE_CULL_BACK ||
|
|
flag & DRW_STATE_CULL_FRONT)
|
|
{
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
if (flag & DRW_STATE_CULL_BACK)
|
|
glCullFace(GL_BACK);
|
|
else if (flag & DRW_STATE_CULL_FRONT)
|
|
glCullFace(GL_FRONT);
|
|
}
|
|
else {
|
|
glDisable(GL_CULL_FACE);
|
|
}
|
|
|
|
/* Depht Test */
|
|
if (flag & (DRW_STATE_DEPTH_LESS | DRW_STATE_DEPTH_EQUAL | DRW_STATE_DEPTH_GREATER))
|
|
{
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
if (flag & DRW_STATE_DEPTH_LESS)
|
|
glDepthFunc(GL_LEQUAL);
|
|
else if (flag & DRW_STATE_DEPTH_EQUAL)
|
|
glDepthFunc(GL_EQUAL);
|
|
else if (flag & DRW_STATE_DEPTH_GREATER)
|
|
glDepthFunc(GL_GREATER);
|
|
}
|
|
else {
|
|
glDisable(GL_DEPTH_TEST);
|
|
}
|
|
}
|
|
|
|
/* Wire Width */
|
|
if (flag & DRW_STATE_WIRE) {
|
|
glLineWidth(1.0f);
|
|
}
|
|
else if (flag & DRW_STATE_WIRE_LARGE) {
|
|
glLineWidth(UI_GetThemeValuef(TH_OUTLINE_WIDTH) * 2.0f);
|
|
}
|
|
|
|
/* Points Size */
|
|
if (flag & DRW_STATE_POINT) {
|
|
GPU_enable_program_point_size();
|
|
glPointSize(5.0f);
|
|
}
|
|
else if (reset) {
|
|
GPU_disable_program_point_size();
|
|
}
|
|
|
|
/* Blending (all buffer) */
|
|
if (flag & DRW_STATE_BLEND) {
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
else if (reset) {
|
|
glDisable(GL_BLEND);
|
|
}
|
|
|
|
/* Line Stipple */
|
|
if (flag & DRW_STATE_STIPPLE_2) {
|
|
setlinestyle(2);
|
|
}
|
|
else if (flag & DRW_STATE_STIPPLE_3) {
|
|
setlinestyle(3);
|
|
}
|
|
else if (flag & DRW_STATE_STIPPLE_4) {
|
|
setlinestyle(4);
|
|
}
|
|
else if (reset) {
|
|
setlinestyle(0);
|
|
}
|
|
|
|
/* Stencil */
|
|
if (flag & (DRW_STATE_WRITE_STENCIL_SELECT | DRW_STATE_WRITE_STENCIL_ACTIVE |
|
|
DRW_STATE_TEST_STENCIL_SELECT | DRW_STATE_TEST_STENCIL_ACTIVE))
|
|
{
|
|
glEnable(GL_STENCIL_TEST);
|
|
|
|
/* Stencil Write */
|
|
if (flag & DRW_STATE_WRITE_STENCIL_SELECT) {
|
|
glStencilMask(STENCIL_SELECT);
|
|
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
|
|
glStencilFunc(GL_ALWAYS, 0xFF, STENCIL_SELECT);
|
|
}
|
|
else if (flag & DRW_STATE_WRITE_STENCIL_ACTIVE) {
|
|
glStencilMask(STENCIL_ACTIVE);
|
|
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
|
|
glStencilFunc(GL_ALWAYS, 0xFF, STENCIL_ACTIVE);
|
|
}
|
|
/* Stencil Test */
|
|
else if (flag & DRW_STATE_TEST_STENCIL_SELECT) {
|
|
glStencilMask(0x00); /* disable write */
|
|
glStencilFunc(GL_NOTEQUAL, 0xFF, STENCIL_SELECT);
|
|
}
|
|
else if (flag & DRW_STATE_TEST_STENCIL_ACTIVE) {
|
|
glStencilMask(0x00); /* disable write */
|
|
glStencilFunc(GL_NOTEQUAL, 0xFF, STENCIL_ACTIVE);
|
|
}
|
|
}
|
|
else if (reset) {
|
|
/* disable write & test */
|
|
glStencilMask(0x00);
|
|
glStencilFunc(GL_ALWAYS, 1, 0xFF);
|
|
glDisable(GL_STENCIL_TEST);
|
|
}
|
|
}
|
|
|
|
typedef struct DRWBoundTexture {
|
|
struct DRWBoundTexture *next, *prev;
|
|
GPUTexture *tex;
|
|
} DRWBoundTexture;
|
|
|
|
static void draw_geometry(DRWShadingGroup *shgroup, Batch *geom, const float (*obmat)[4])
|
|
{
|
|
RegionView3D *rv3d = CTX_wm_region_view3d(DST.context);
|
|
DRWInterface *interface = shgroup->interface;
|
|
|
|
float mvp[4][4], mv[4][4], n[3][3], wn[3][3];
|
|
float eye[3] = { 0.0f, 0.0f, 1.0f }; /* looking into the screen */
|
|
|
|
bool do_mvp = (interface->modelviewprojection != -1);
|
|
bool do_mv = (interface->modelview != -1);
|
|
bool do_n = (interface->normal != -1);
|
|
bool do_wn = (interface->worldnormal != -1);
|
|
bool do_eye = (interface->eye != -1);
|
|
|
|
if (do_mvp) {
|
|
mul_m4_m4m4(mvp, rv3d->persmat, obmat);
|
|
}
|
|
if (do_mv || do_n || do_eye) {
|
|
mul_m4_m4m4(mv, rv3d->viewmat, obmat);
|
|
}
|
|
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);
|
|
}
|
|
|
|
/* Should be really simple */
|
|
/* step 1 : bind object dependent matrices */
|
|
if (interface->model != -1) {
|
|
GPU_shader_uniform_vector(shgroup->shader, interface->model, 16, 1, (float *)obmat);
|
|
}
|
|
if (interface->modelviewprojection != -1) {
|
|
GPU_shader_uniform_vector(shgroup->shader, interface->modelviewprojection, 16, 1, (float *)mvp);
|
|
}
|
|
if (interface->viewprojection != -1) {
|
|
GPU_shader_uniform_vector(shgroup->shader, interface->viewprojection, 16, 1, (float *)rv3d->persmat);
|
|
}
|
|
if (interface->projection != -1) {
|
|
GPU_shader_uniform_vector(shgroup->shader, interface->projection, 16, 1, (float *)rv3d->winmat);
|
|
}
|
|
if (interface->view != -1) {
|
|
GPU_shader_uniform_vector(shgroup->shader, interface->view, 16, 1, (float *)rv3d->viewmat);
|
|
}
|
|
if (interface->modelview != -1) {
|
|
GPU_shader_uniform_vector(shgroup->shader, interface->modelview, 16, 1, (float *)mv);
|
|
}
|
|
if (interface->normal != -1) {
|
|
GPU_shader_uniform_vector(shgroup->shader, interface->normal, 9, 1, (float *)n);
|
|
}
|
|
if (interface->worldnormal != -1) {
|
|
GPU_shader_uniform_vector(shgroup->shader, interface->worldnormal, 9, 1, (float *)wn);
|
|
}
|
|
if (interface->eye != -1) {
|
|
GPU_shader_uniform_vector(shgroup->shader, interface->eye, 3, 1, (float *)eye);
|
|
}
|
|
|
|
/* step 2 : bind vertex array & draw */
|
|
Batch_set_program(geom, GPU_shader_get_program(shgroup->shader), GPU_shader_get_interface(shgroup->shader));
|
|
if (interface->instance_vbo) {
|
|
Batch_draw_stupid_instanced(geom, interface->instance_vbo, interface->instance_count, interface->attribs_count,
|
|
interface->attribs_stride, interface->attribs_size, interface->attribs_loc);
|
|
}
|
|
else {
|
|
Batch_draw_stupid(geom);
|
|
}
|
|
}
|
|
|
|
static void draw_shgroup(DRWShadingGroup *shgroup)
|
|
{
|
|
BLI_assert(shgroup->shader);
|
|
BLI_assert(shgroup->interface);
|
|
|
|
DRWInterface *interface = shgroup->interface;
|
|
GPUTexture *tex;
|
|
|
|
if (DST.shader != shgroup->shader) {
|
|
if (DST.shader) GPU_shader_unbind();
|
|
GPU_shader_bind(shgroup->shader);
|
|
DST.shader = shgroup->shader;
|
|
}
|
|
|
|
if (shgroup->type != DRW_SHG_NORMAL) {
|
|
shgroup_dynamic_batch_from_calls(shgroup);
|
|
}
|
|
|
|
if (shgroup->state != 0) {
|
|
set_state(shgroup->state, false);
|
|
}
|
|
|
|
/* Binding Uniform */
|
|
/* Don't check anything, Interface should already contain the least uniform as possible */
|
|
for (DRWUniform *uni = interface->uniforms.first; uni; uni = uni->next) {
|
|
DRWBoundTexture *bound_tex;
|
|
|
|
switch (uni->type) {
|
|
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;
|
|
GPU_texture_bind(tex, uni->bindloc);
|
|
|
|
bound_tex = MEM_callocN(sizeof(DRWBoundTexture), "DRWBoundTexture");
|
|
bound_tex->tex = tex;
|
|
BLI_addtail(&DST.bound_texs, bound_tex);
|
|
|
|
GPU_shader_uniform_texture(shgroup->shader, uni->location, tex);
|
|
break;
|
|
case DRW_UNIFORM_BUFFER:
|
|
tex = *((GPUTexture **)uni->value);
|
|
GPU_texture_bind(tex, uni->bindloc);
|
|
GPU_texture_compare_mode(tex, false);
|
|
GPU_texture_filter_mode(tex, false);
|
|
|
|
bound_tex = MEM_callocN(sizeof(DRWBoundTexture), "DRWBoundTexture");
|
|
bound_tex->tex = tex;
|
|
BLI_addtail(&DST.bound_texs, bound_tex);
|
|
|
|
GPU_shader_uniform_texture(shgroup->shader, uni->location, tex);
|
|
break;
|
|
case DRW_UNIFORM_BLOCK:
|
|
GPU_uniformbuffer_bind((GPUUniformBuffer *)uni->value, uni->bindloc);
|
|
GPU_shader_uniform_buffer(shgroup->shader, uni->location, (GPUUniformBuffer *)uni->value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Rendering Calls */
|
|
if (shgroup->type != DRW_SHG_NORMAL) {
|
|
/* Replacing multiple calls with only one */
|
|
float obmat[4][4];
|
|
unit_m4(obmat);
|
|
|
|
if (shgroup->type == DRW_SHG_INSTANCE && interface->instance_count > 0) {
|
|
draw_geometry(shgroup, shgroup->instance_geom, obmat);
|
|
}
|
|
else {
|
|
/* Some dynamic batch can have no geom (no call to aggregate) */
|
|
if (shgroup->batch_geom) {
|
|
draw_geometry(shgroup, shgroup->batch_geom, obmat);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (DRWCall *call = shgroup->calls.first; call; call = call->next) {
|
|
draw_geometry(shgroup, call->geometry, call->obmat);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DRW_draw_pass(DRWPass *pass)
|
|
{
|
|
/* Start fresh */
|
|
DST.shader = NULL;
|
|
DST.tex_bind_id = 0;
|
|
|
|
set_state(pass->state, true);
|
|
BLI_listbase_clear(&DST.bound_texs);
|
|
|
|
pass->wasdrawn = true;
|
|
|
|
/* Init Timer queries */
|
|
if (pass->timer_queries[0] == 0) {
|
|
pass->front_idx = 0;
|
|
pass->back_idx = 1;
|
|
|
|
glGenQueries(2, pass->timer_queries);
|
|
|
|
/* dummy query, avoid gl error */
|
|
glBeginQuery(GL_TIME_ELAPSED, pass->timer_queries[pass->front_idx]);
|
|
glEndQuery(GL_TIME_ELAPSED);
|
|
}
|
|
else {
|
|
/* swap indices */
|
|
unsigned int tmp = pass->back_idx;
|
|
pass->back_idx = pass->front_idx;
|
|
pass->front_idx = tmp;
|
|
}
|
|
|
|
/* issue query for the next frame */
|
|
glBeginQuery(GL_TIME_ELAPSED, pass->timer_queries[pass->back_idx]);
|
|
|
|
for (DRWShadingGroup *shgroup = pass->shgroups.first; shgroup; shgroup = shgroup->next) {
|
|
draw_shgroup(shgroup);
|
|
}
|
|
|
|
/* Clear Bound textures */
|
|
for (DRWBoundTexture *bound_tex = DST.bound_texs.first; bound_tex; bound_tex = bound_tex->next) {
|
|
GPU_texture_unbind(bound_tex->tex);
|
|
}
|
|
DST.tex_bind_id = 0;
|
|
BLI_freelistN(&DST.bound_texs);
|
|
|
|
if (DST.shader) {
|
|
GPU_shader_unbind();
|
|
DST.shader = NULL;
|
|
}
|
|
|
|
glEndQuery(GL_TIME_ELAPSED);
|
|
}
|
|
|
|
void DRW_draw_callbacks_pre_scene(void)
|
|
{
|
|
struct ARegion *ar = CTX_wm_region(DST.context);
|
|
RegionView3D *rv3d = CTX_wm_region_view3d(DST.context);
|
|
|
|
gpuLoadProjectionMatrix3D(rv3d->winmat);
|
|
gpuLoadMatrix3D(rv3d->viewmat);
|
|
|
|
ED_region_draw_cb_draw(DST.context, ar, REGION_DRAW_PRE_VIEW);
|
|
}
|
|
|
|
void DRW_draw_callbacks_post_scene(void)
|
|
{
|
|
struct ARegion *ar = CTX_wm_region(DST.context);
|
|
RegionView3D *rv3d = CTX_wm_region_view3d(DST.context);
|
|
|
|
gpuLoadProjectionMatrix3D(rv3d->winmat);
|
|
gpuLoadMatrix3D(rv3d->viewmat);
|
|
|
|
ED_region_draw_cb_draw(DST.context, ar, REGION_DRAW_POST_VIEW);
|
|
}
|
|
|
|
/* Reset state to not interfer with other UI drawcall */
|
|
void DRW_state_reset(void)
|
|
{
|
|
DRWState state = 0;
|
|
state |= DRW_STATE_WRITE_DEPTH;
|
|
state |= DRW_STATE_WRITE_COLOR;
|
|
state |= DRW_STATE_DEPTH_LESS;
|
|
set_state(state, true);
|
|
}
|
|
|
|
#else /* !WITH_CLAY_ENGINE */
|
|
|
|
void DRW_draw_pass(DRWPass *UNUSED(pass)) {}
|
|
void DRW_draw_callbacks_pre_scene(void) {}
|
|
void DRW_draw_callbacks_post_scene(void) {}
|
|
void DRW_state_reset(void) {}
|
|
|
|
#endif /* WITH_CLAY_ENGINE */
|
|
|
|
|
|
/* ****************************************** Settings ******************************************/
|
|
|
|
bool DRW_is_object_renderable(Object *ob)
|
|
{
|
|
Scene *scene = CTX_data_scene(DST.context);
|
|
Object *obedit = scene->obedit;
|
|
|
|
if (ob->type == OB_MESH) {
|
|
if (ob == obedit) {
|
|
IDProperty *props = BKE_object_collection_engine_get(ob, COLLECTION_MODE_EDIT, "");
|
|
bool do_occlude_wire = BKE_collection_engine_property_value_get_bool(props, "show_occlude_wire");
|
|
|
|
if (do_occlude_wire)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* ****************************************** Framebuffers ******************************************/
|
|
|
|
static GPUTextureFormat convert_tex_format(int fbo_format, int *channels, bool *is_depth)
|
|
{
|
|
*is_depth = ((fbo_format == DRW_BUF_DEPTH_16) ||
|
|
(fbo_format == DRW_BUF_DEPTH_24));
|
|
|
|
switch (fbo_format) {
|
|
case DRW_BUF_RGBA_8: *channels = 4; return GPU_RGBA8;
|
|
case DRW_BUF_RGBA_16: *channels = 4; return GPU_RGBA16F;
|
|
case DRW_BUF_DEPTH_24: *channels = 1; return GPU_DEPTH_COMPONENT24;
|
|
default:
|
|
BLI_assert(false);
|
|
*channels = 4; return GPU_RGBA8;
|
|
}
|
|
}
|
|
|
|
void DRW_framebuffer_init(struct GPUFrameBuffer **fb, int width, int height, DRWFboTexture textures[MAX_FBO_TEX],
|
|
int texnbr)
|
|
{
|
|
BLI_assert(texnbr <= MAX_FBO_TEX);
|
|
|
|
if (!*fb) {
|
|
int color_attachment = -1;
|
|
*fb = GPU_framebuffer_create();
|
|
|
|
for (int i = 0; i < texnbr; ++i) {
|
|
DRWFboTexture fbotex = textures[i];
|
|
|
|
if (!*fbotex.tex) {
|
|
int channels;
|
|
bool is_depth;
|
|
GPUTextureFormat gpu_format = convert_tex_format(fbotex.format, &channels, &is_depth);
|
|
|
|
*fbotex.tex = GPU_texture_create_2D_custom(width, height, channels, gpu_format, NULL, NULL);
|
|
drw_texture_set_parameters(*fbotex.tex, fbotex.flag);
|
|
|
|
if (!is_depth) {
|
|
++color_attachment;
|
|
}
|
|
}
|
|
|
|
GPU_framebuffer_texture_attach(*fb, *fbotex.tex, color_attachment);
|
|
}
|
|
|
|
if (!GPU_framebuffer_check_valid(*fb, NULL)) {
|
|
printf("Error invalid framebuffer\n");
|
|
}
|
|
|
|
GPU_framebuffer_bind(DST.default_framebuffer);
|
|
}
|
|
}
|
|
|
|
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_texture_attach(struct GPUFrameBuffer *fb, GPUTexture *tex, int slot)
|
|
{
|
|
GPU_framebuffer_texture_attach(fb, tex, slot);
|
|
}
|
|
|
|
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)
|
|
{
|
|
GPU_framebuffer_blit(fb_read, 0, fb_write, 0, depth);
|
|
}
|
|
|
|
/* ****************************************** Viewport ******************************************/
|
|
|
|
static void *DRW_viewport_engine_data_get(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;
|
|
}
|
|
|
|
/* 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(const bContext *C)
|
|
{
|
|
RegionView3D *rv3d = CTX_wm_region_view3d(C);
|
|
|
|
/* Refresh DST.size */
|
|
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;
|
|
|
|
/* 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;
|
|
|
|
/* Save context for all later needs */
|
|
DST.context = C;
|
|
}
|
|
|
|
void DRW_viewport_matrix_get(float mat[4][4], DRWViewportMatrixType type)
|
|
{
|
|
RegionView3D *rv3d = CTX_wm_region_view3d(DST.context);
|
|
|
|
if (type == DRW_MAT_PERS)
|
|
copy_m4_m4(mat, rv3d->persmat);
|
|
else if (type == DRW_MAT_VIEW)
|
|
copy_m4_m4(mat, rv3d->viewmat);
|
|
else if (type == DRW_MAT_VIEWINV)
|
|
copy_m4_m4(mat, rv3d->viewinv);
|
|
else if (type == DRW_MAT_WIN)
|
|
copy_m4_m4(mat, rv3d->winmat);
|
|
}
|
|
|
|
bool DRW_viewport_is_persp_get(void)
|
|
{
|
|
RegionView3D *rv3d = CTX_wm_region_view3d(DST.context);
|
|
return rv3d->is_persp;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/* **************************************** OBJECTS *************************************** */
|
|
|
|
typedef struct ObjectEngineData {
|
|
struct ObjectEngineData *next, *prev;
|
|
DrawEngineType *engine_type;
|
|
void *storage;
|
|
} ObjectEngineData;
|
|
|
|
void **DRW_object_engine_data_get(Object *ob, DrawEngineType *engine_type)
|
|
{
|
|
ObjectEngineData *oed;
|
|
|
|
for (oed = ob->drawdata.first; oed; oed = oed->next) {
|
|
if (oed->engine_type == engine_type) {
|
|
return &oed->storage;
|
|
}
|
|
}
|
|
|
|
oed = MEM_callocN(sizeof(ObjectEngineData), "ObjectEngineData");
|
|
oed->engine_type = engine_type;
|
|
BLI_addtail(&ob->drawdata, oed);
|
|
|
|
return &oed->storage;
|
|
}
|
|
|
|
void DRW_object_engine_data_free(Object *ob)
|
|
{
|
|
for (ObjectEngineData *oed = ob->drawdata.first; oed; oed = oed->next) {
|
|
if (oed->storage) {
|
|
MEM_freeN(oed->storage);
|
|
}
|
|
}
|
|
|
|
BLI_freelistN(&ob->drawdata);
|
|
}
|
|
|
|
LampEngineData *DRW_lamp_engine_data_get(Object *ob, RenderEngineType *engine_type)
|
|
{
|
|
BLI_assert(ob->type == OB_LAMP);
|
|
|
|
Scene *scene = CTX_data_scene(DST.context);
|
|
|
|
/* TODO Dupliobjects */
|
|
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);
|
|
}
|
|
|
|
/* **************************************** RENDERING ************************************** */
|
|
|
|
#define TIMER_FALLOFF 0.1f
|
|
|
|
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_get(engine);
|
|
double stime = PIL_check_seconds_timer();
|
|
|
|
if (engine->engine_init) {
|
|
engine->engine_init(data);
|
|
}
|
|
|
|
double ftime = (PIL_check_seconds_timer() - stime) * 1e3;
|
|
data->init_time = data->init_time * (1.0f - TIMER_FALLOFF) + ftime * TIMER_FALLOFF; /* exp average */
|
|
}
|
|
}
|
|
|
|
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_get(engine);
|
|
double stime = PIL_check_seconds_timer();
|
|
data->cache_time = 0.0;
|
|
|
|
if (engine->cache_init) {
|
|
engine->cache_init(data);
|
|
}
|
|
|
|
data->cache_time += (PIL_check_seconds_timer() - stime) * 1e3;
|
|
}
|
|
}
|
|
|
|
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_get(engine);
|
|
double stime = PIL_check_seconds_timer();
|
|
|
|
if (engine->cache_populate) {
|
|
engine->cache_populate(data, ob);
|
|
}
|
|
|
|
data->cache_time += (PIL_check_seconds_timer() - stime) * 1e3;
|
|
}
|
|
}
|
|
|
|
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_get(engine);
|
|
double stime = PIL_check_seconds_timer();
|
|
|
|
if (engine->cache_finish) {
|
|
engine->cache_finish(data);
|
|
}
|
|
|
|
data->cache_time += (PIL_check_seconds_timer() - stime) * 1e3;
|
|
}
|
|
}
|
|
|
|
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_get(engine);
|
|
double stime = PIL_check_seconds_timer();
|
|
|
|
if (engine->draw_background) {
|
|
engine->draw_background(data);
|
|
return;
|
|
}
|
|
|
|
double ftime = (PIL_check_seconds_timer() - stime) * 1e3;
|
|
data->background_time = data->background_time * (1.0f - TIMER_FALLOFF) + ftime * TIMER_FALLOFF; /* exp average */
|
|
}
|
|
|
|
/* No draw_background found, doing default 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_get(engine);
|
|
double stime = PIL_check_seconds_timer();
|
|
|
|
if (engine->draw_scene) {
|
|
engine->draw_scene(data);
|
|
}
|
|
|
|
double ftime = (PIL_check_seconds_timer() - stime) * 1e3;
|
|
data->render_time = data->render_time * (1.0f - TIMER_FALLOFF) + ftime * TIMER_FALLOFF; /* exp average */
|
|
}
|
|
}
|
|
|
|
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(const bContext *C)
|
|
{
|
|
/* TODO layers */
|
|
Scene *scene = CTX_data_scene(C);
|
|
RenderEngineType *type = RE_engines_find(scene->r.engine);
|
|
use_drw_engine(type->draw_engine);
|
|
|
|
/* TODO Refine the folowing logic based on objects states
|
|
* not on global state.
|
|
* Order is important */
|
|
use_drw_engine(&draw_engine_object_type);
|
|
|
|
switch (CTX_data_mode_enum(C)) {
|
|
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_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;
|
|
}
|
|
}
|
|
|
|
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 cache_tot_time = 0.0, 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 = CTX_wm_region(DST.context);
|
|
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, "Cache");
|
|
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_get(engine);
|
|
|
|
draw_stat(&rect, u++, v, engine->idname, sizeof(engine->idname));
|
|
|
|
cache_tot_time += data->cache_time;
|
|
sprintf(time_to_txt, "%.2fms", data->cache_time);
|
|
draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
|
|
|
|
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", cache_tot_time);
|
|
draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
|
|
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));
|
|
}
|
|
|
|
/* 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 = CTX_wm_region(DST.context);
|
|
ED_region_visible_rect(ar, &rect);
|
|
|
|
UI_FontThemeColor(BLF_default(), TH_TEXT_HI);
|
|
|
|
char time_to_txt[16];
|
|
char pass_name[MAX_PASS_NAME + 8];
|
|
int v = BLI_listbase_count(&DST.enabled_engines) + 3;
|
|
GLuint64 tot_time = 0;
|
|
|
|
for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
|
|
GLuint64 engine_time = 0;
|
|
DrawEngineType *engine = link->data;
|
|
ViewportEngineData *data = DRW_viewport_engine_data_get(engine);
|
|
int vsta = v;
|
|
|
|
draw_stat(&rect, 0, v, engine->idname, sizeof(engine->idname));
|
|
v++;
|
|
|
|
for (int i = 0; i < engine->vedata_size->psl_len; ++i) {
|
|
DRWPass *pass = data->psl->passes[i];
|
|
if (pass != NULL) {
|
|
GLuint64 time;
|
|
glGetQueryObjectui64v(pass->timer_queries[pass->front_idx], GL_QUERY_RESULT, &time);
|
|
tot_time += time;
|
|
engine_time += time;
|
|
|
|
sprintf(pass_name, " |--> %s", pass->name);
|
|
draw_stat(&rect, 0, v, pass_name, sizeof(pass_name));
|
|
|
|
if (pass->wasdrawn)
|
|
sprintf(time_to_txt, "%.2fms", time / 1000000.0);
|
|
else
|
|
sprintf(time_to_txt, "Not drawn");
|
|
draw_stat(&rect, 2, v++, time_to_txt, sizeof(time_to_txt));
|
|
|
|
pass->wasdrawn = false;
|
|
}
|
|
}
|
|
/* engine total time */
|
|
sprintf(time_to_txt, "%.2fms", engine_time / 1000000.0);
|
|
draw_stat(&rect, 2, vsta, time_to_txt, sizeof(time_to_txt));
|
|
v++;
|
|
}
|
|
|
|
sprintf(pass_name, "Total GPU time %.2fms (%.1f fps)", tot_time / 1000000.0, 1000000000.0 / tot_time);
|
|
draw_stat(&rect, 0, v, pass_name, sizeof(pass_name));
|
|
}
|
|
|
|
static void drw_draw_view_set_recursive(Scene *scene)
|
|
{
|
|
if (scene->set) {
|
|
drw_draw_view_set_recursive(scene->set);
|
|
}
|
|
|
|
SceneLayer *sl = BKE_scene_layer_render_active(scene);
|
|
DEG_OBJECT_ITER(sl, ob);
|
|
{
|
|
/* XXX FIXME!!! - dont de-select users data!
|
|
* (set drawing should use a fixed color - ignoring select and other theme colors) */
|
|
ob->base_flag &= ~BASE_SELECTED;
|
|
DRW_engines_cache_populate(ob);
|
|
}
|
|
DEG_OBJECT_ITER_END
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
bool cache_is_dirty;
|
|
RegionView3D *rv3d = CTX_wm_region_view3d(C);
|
|
View3D *v3d = CTX_wm_view3d(C);
|
|
DST.viewport = rv3d->viewport;
|
|
v3d->zbuf = true;
|
|
|
|
/* Get list of enabled engines */
|
|
DRW_engines_enable(C);
|
|
|
|
/* Setup viewport */
|
|
cache_is_dirty = GPU_viewport_cache_validate(DST.viewport, DRW_engines_get_hash());
|
|
DRW_viewport_var_init(C);
|
|
|
|
/* Update ubos */
|
|
DRW_globals_update();
|
|
|
|
/* Init engines */
|
|
DRW_engines_init();
|
|
|
|
/* TODO : tag to refresh by the deps graph */
|
|
/* ideally only refresh when objects are added/removed */
|
|
/* or render properties / materials change */
|
|
if (cache_is_dirty) {
|
|
SceneLayer *sl;
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
DRW_engines_cache_init();
|
|
|
|
/* draw set first */
|
|
if (scene->set) {
|
|
drw_draw_view_set_recursive(scene->set);
|
|
}
|
|
|
|
sl = CTX_data_scene_layer(C);
|
|
DEG_OBJECT_ITER(sl, ob);
|
|
{
|
|
DRW_engines_cache_populate(ob);
|
|
}
|
|
DEG_OBJECT_ITER_END
|
|
|
|
DRW_engines_cache_finish();
|
|
}
|
|
|
|
/* Start Drawing */
|
|
DRW_engines_draw_background();
|
|
|
|
DRW_draw_callbacks_pre_scene();
|
|
// DRW_draw_grid();
|
|
DRW_engines_draw_scene();
|
|
DRW_draw_callbacks_post_scene();
|
|
|
|
DRW_draw_manipulator();
|
|
|
|
DRW_draw_region_info();
|
|
|
|
if (G.debug_value > 20) {
|
|
DRW_debug_cpu_stats();
|
|
DRW_debug_gpu_stats();
|
|
}
|
|
|
|
DRW_state_reset();
|
|
DRW_engines_disable();
|
|
}
|
|
|
|
/* ****************************************** OTHER ***************************************** */
|
|
|
|
const bContext *DRW_get_context(void)
|
|
{
|
|
return DST.context;
|
|
}
|
|
|
|
/* ****************************************** INIT ***************************************** */
|
|
|
|
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, &viewport_clay_type);
|
|
RE_engines_register(NULL, &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);
|
|
#endif
|
|
}
|
|
|
|
extern struct GPUUniformBuffer *globals_ubo; /* draw_common.c */
|
|
void DRW_engines_free(void)
|
|
{
|
|
#ifdef WITH_CLAY_ENGINE
|
|
DRW_shape_cache_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);
|
|
|
|
BLI_remlink(&R_engines, &viewport_clay_type);
|
|
#endif
|
|
}
|