From c476c36e400883d929a7149def8dcb6ad6157a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Wed, 11 Mar 2020 17:07:43 +0100 Subject: Workbench Simplification Refactor This patch is (almost) a complete rewrite of workbench engine. The features remain unchanged but the code quality is greatly improved. Hair shading is brighter but also more correct. This also introduce the concept of `DRWShaderLibrary` to make a simple include system inside the GLSL files. Differential Revision: https://developer.blender.org/D7060 --- .../draw/engines/workbench/workbench_engine.c | 547 ++++++++++++++++++++- 1 file changed, 543 insertions(+), 4 deletions(-) (limited to 'source/blender/draw/engines/workbench/workbench_engine.c') diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index 62a192bbb25..b91ca7cf070 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -19,18 +19,557 @@ /** \file * \ingroup draw_engine * - * Simple engine for drawing color and/or depth. - * When we only need simple flat shaders. + * Workbench Engine: + * + * Optimized engine to draw the working viewport with solid and transparent geometry. */ #include "DRW_render.h" +#include "BLI_alloca.h" + +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_particle.h" + +#include "DNA_image_types.h" +#include "DNA_fluid_types.h" +#include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" +#include "DNA_node_types.h" + #include "workbench_engine.h" #include "workbench_private.h" #define WORKBENCH_ENGINE "BLENDER_WORKBENCH" -/* Note: currently unused, we may want to register so we can see this when debugging the view. */ +void workbench_engine_init(void *ved) +{ + WORKBENCH_Data *vedata = ved; + WORKBENCH_StorageList *stl = vedata->stl; + WORKBENCH_TextureList *txl = vedata->txl; + + workbench_shader_library_ensure(); + + if (!stl->wpd) { + stl->wpd = MEM_callocN(sizeof(*stl->wpd), __func__); + stl->wpd->view_updated = true; + } + + WORKBENCH_PrivateData *wpd = stl->wpd; + workbench_private_data_init(wpd); + workbench_update_world_ubo(wpd); + + if (txl->dummy_image_tx == NULL) { + float fpixel[4] = {1.0f, 0.0f, 1.0f, 1.0f}; + txl->dummy_image_tx = DRW_texture_create_2d(1, 1, GPU_RGBA8, 0, fpixel); + } + wpd->dummy_image_tx = txl->dummy_image_tx; + + if (OBJECT_ID_PASS_ENABLED(wpd)) { + wpd->object_id_tx = DRW_texture_pool_query_fullscreen(GPU_R16UI, &draw_engine_workbench); + } + else { + /* Dont free because it's a pool texture. */ + wpd->object_id_tx = NULL; + } + + workbench_opaque_engine_init(vedata); + workbench_transparent_engine_init(vedata); + workbench_dof_engine_init(vedata); + workbench_antialiasing_engine_init(vedata); + workbench_volume_engine_init(vedata); +} + +void workbench_cache_init(void *ved) +{ + WORKBENCH_Data *vedata = ved; + + workbench_opaque_cache_init(vedata); + workbench_transparent_cache_init(vedata); + workbench_shadow_cache_init(vedata); + workbench_cavity_cache_init(vedata); + workbench_outline_cache_init(vedata); + workbench_dof_cache_init(vedata); + workbench_antialiasing_cache_init(vedata); + workbench_volume_cache_init(vedata); +} + +/* TODO(fclem) DRW_cache_object_surface_material_get needs a refactor to allow passing NULL + * instead of gpumat_array. Avoiding all this boilerplate code. */ +static struct GPUBatch **workbench_object_surface_material_get(Object *ob) +{ + const int materials_len = DRW_cache_object_material_count_get(ob); + struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len); + memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len); + + return DRW_cache_object_surface_material_get(ob, gpumat_array, materials_len); +} + +static void workbench_cache_sculpt_populate(WORKBENCH_PrivateData *wpd, + Object *ob, + eV3DShadingColorType color_type) +{ + const bool use_vcol = ELEM(color_type, V3D_SHADING_VERTEX_COLOR); + const bool use_single_drawcall = !ELEM(color_type, V3D_SHADING_MATERIAL_COLOR); + BLI_assert(wpd->shading.color_type != V3D_SHADING_TEXTURE_COLOR); + + if (use_single_drawcall) { + DRWShadingGroup *grp = workbench_material_setup(wpd, ob, 0, color_type, NULL); + DRW_shgroup_call_sculpt(grp, ob, false, false, use_vcol); + } + else { + const int materials_len = DRW_cache_object_material_count_get(ob); + struct DRWShadingGroup **shgrps = BLI_array_alloca(shgrps, materials_len); + for (int i = 0; i < materials_len; i++) { + shgrps[i] = workbench_material_setup(wpd, ob, i + 1, color_type, NULL); + } + DRW_shgroup_call_sculpt_with_materials(shgrps, ob, false); + } +} + +static void workbench_cache_texpaint_populate(WORKBENCH_PrivateData *wpd, Object *ob) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const Scene *scene = draw_ctx->scene; + const ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; + const bool use_single_drawcall = imapaint->mode == IMAGEPAINT_MODE_IMAGE; + + if (use_single_drawcall) { + struct GPUBatch *geom = DRW_cache_mesh_surface_texpaint_single_get(ob); + if (geom) { + Image *ima = imapaint->canvas; + int interp = (imapaint->interp == IMAGEPAINT_INTERP_LINEAR) ? SHD_INTERP_LINEAR : + SHD_INTERP_CLOSEST; + + DRWShadingGroup *grp = workbench_image_setup(wpd, ob, 0, ima, NULL, interp); + DRW_shgroup_call(grp, geom, ob); + } + } + else { + struct GPUBatch **geoms = DRW_cache_mesh_surface_texpaint_get(ob); + if (geoms) { + const int materials_len = DRW_cache_object_material_count_get(ob); + for (int i = 0; i < materials_len; i++) { + DRWShadingGroup *grp = workbench_image_setup(wpd, ob, i + 1, NULL, NULL, 0); + DRW_shgroup_call(grp, geoms[i], ob); + } + } + } +} + +static void workbench_cache_common_populate(WORKBENCH_PrivateData *wpd, + Object *ob, + eV3DShadingColorType color_type, + bool *r_transp) +{ + const bool use_tex = ELEM(color_type, V3D_SHADING_TEXTURE_COLOR); + const bool use_vcol = ELEM(color_type, V3D_SHADING_VERTEX_COLOR); + const bool use_single_drawcall = !ELEM( + color_type, V3D_SHADING_MATERIAL_COLOR, V3D_SHADING_TEXTURE_COLOR); + + if (use_single_drawcall) { + struct GPUBatch *geom = (use_vcol) ? DRW_cache_mesh_surface_vertpaint_get(ob) : + DRW_cache_object_surface_get(ob); + if (geom) { + DRWShadingGroup *grp = workbench_material_setup(wpd, ob, 0, color_type, r_transp); + DRW_shgroup_call(grp, geom, ob); + } + } + else { + struct GPUBatch **geoms = (use_tex) ? DRW_cache_mesh_surface_texpaint_get(ob) : + workbench_object_surface_material_get(ob); + if (geoms) { + const int materials_len = DRW_cache_object_material_count_get(ob); + for (int i = 0; i < materials_len; i++) { + DRWShadingGroup *grp = workbench_material_setup(wpd, ob, i + 1, color_type, r_transp); + DRW_shgroup_call(grp, geoms[i], ob); + } + } + } +} + +static void workbench_cache_hair_populate(WORKBENCH_PrivateData *wpd, + Object *ob, + ModifierData *md, + eV3DShadingColorType color_type, + bool use_texpaint_mode) +{ + ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; + ParticleSettings *part = psys->part; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const Scene *scene = draw_ctx->scene; + + const ImagePaintSettings *imapaint = use_texpaint_mode ? &scene->toolsettings->imapaint : NULL; + Image *ima = (imapaint && imapaint->mode == IMAGEPAINT_MODE_IMAGE) ? imapaint->canvas : NULL; + int interp = (imapaint && imapaint->interp == IMAGEPAINT_INTERP_LINEAR) ? SHD_INTERP_LINEAR : + SHD_INTERP_CLOSEST; + DRWShadingGroup *grp = (use_texpaint_mode) ? + workbench_image_hair_setup(wpd, ob, part->omat, ima, NULL, interp) : + workbench_material_hair_setup(wpd, ob, part->omat, color_type); + + DRW_shgroup_hair_create_sub(ob, psys, md, grp); +} + +/* Decide what colortype to draw the object with. + * In some cases it can be overwritten by workbench_material_setup(). */ +static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, + Object *ob, + bool *r_sculpt_pbvh, + bool *r_texpaint_mode, + bool *r_draw_shadow) +{ + eV3DShadingColorType color_type = wpd->shading.color_type; + const Mesh *me = (ob->type == OB_MESH) ? ob->data : NULL; + + const DRWContextState *draw_ctx = DRW_context_state_get(); + const bool is_active = (ob == draw_ctx->obact); + const bool is_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && + !DRW_state_is_image_render(); + const bool is_render = DRW_state_is_image_render() && (draw_ctx->v3d == NULL); + const bool is_texpaint_mode = is_active && (wpd->ctx_mode == CTX_MODE_PAINT_TEXTURE); + const bool is_vertpaint_mode = is_active && (wpd->ctx_mode == CTX_MODE_PAINT_VERTEX); + + if ((color_type == V3D_SHADING_TEXTURE_COLOR) && (ob->dt < OB_TEXTURE)) { + color_type = V3D_SHADING_MATERIAL_COLOR; + } + /* Disable color mode if data layer is unavailable. */ + if ((color_type == V3D_SHADING_TEXTURE_COLOR) && (me == NULL || me->mloopuv == NULL)) { + color_type = V3D_SHADING_MATERIAL_COLOR; + } + if ((color_type == V3D_SHADING_VERTEX_COLOR) && (me == NULL || me->mloopcol == NULL)) { + color_type = V3D_SHADING_OBJECT_COLOR; + } + + *r_sculpt_pbvh = is_sculpt_pbvh; + *r_texpaint_mode = false; + + if (!is_sculpt_pbvh && !is_render) { + /* Force texture or vertex mode if object is in paint mode. */ + if (is_texpaint_mode && me && me->mloopuv) { + color_type = V3D_SHADING_TEXTURE_COLOR; + *r_texpaint_mode = true; + } + else if (is_vertpaint_mode && me && me->mloopcol) { + color_type = V3D_SHADING_VERTEX_COLOR; + } + } + + if (r_draw_shadow) { + *r_draw_shadow = (ob->dtx & OB_DRAW_NO_SHADOW_CAST) == 0 && SHADOW_ENABLED(wpd); + /* Currently unsupported in sculpt mode. We could revert to the slow + * method in this case but I'm not sure if it's a good idea given that + * sculpted meshes are heavy to begin with. */ + if (is_sculpt_pbvh) { + *r_draw_shadow = false; + } + + if (is_active && DRW_object_use_hide_faces(ob)) { + *r_draw_shadow = false; + } + } + + return color_type; +} + +void workbench_cache_populate(void *ved, Object *ob) +{ + WORKBENCH_Data *vedata = ved; + WORKBENCH_StorageList *stl = vedata->stl; + WORKBENCH_PrivateData *wpd = stl->wpd; + + if (!DRW_object_is_renderable(ob)) { + return; + } + + if (ob->type == OB_MESH && ob->modifiers.first != NULL) { + bool use_sculpt_pbvh, use_texpaint_mode; + int color_type = workbench_color_type_get(wpd, ob, &use_sculpt_pbvh, &use_texpaint_mode, NULL); + + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type != eModifierType_ParticleSystem) { + continue; + } + ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; + if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) { + continue; + } + ParticleSettings *part = psys->part; + const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; + + if (draw_as == PART_DRAW_PATH) { + workbench_cache_hair_populate(wpd, ob, md, color_type, use_texpaint_mode); + } + } + } + + if (!(ob->base_flag & BASE_FROM_DUPLI)) { + ModifierData *md = modifiers_findByType(ob, eModifierType_Fluid); + if (md && modifier_isEnabled(wpd->scene, md, eModifierMode_Realtime)) { + FluidModifierData *fmd = (FluidModifierData *)md; + if (fmd->domain && fmd->domain->type == FLUID_DOMAIN_TYPE_GAS) { + workbench_volume_cache_populate(vedata, wpd->scene, ob, md); + return; /* Do not draw solid in this case. */ + } + } + } + + if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) { + return; + } + + if ((ob->dt < OB_SOLID) && !DRW_state_is_scene_render()) { + return; + } + + if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { + bool use_sculpt_pbvh, use_texpaint_mode, draw_shadow, has_transp_mat = false; + eV3DShadingColorType color_type = workbench_color_type_get( + wpd, ob, &use_sculpt_pbvh, &use_texpaint_mode, &draw_shadow); + + if (use_sculpt_pbvh) { + workbench_cache_sculpt_populate(wpd, ob, color_type); + } + else if (use_texpaint_mode) { + workbench_cache_texpaint_populate(wpd, ob); + } + else { + workbench_cache_common_populate(wpd, ob, color_type, &has_transp_mat); + } + + if (draw_shadow) { + workbench_shadow_cache_populate(vedata, ob, has_transp_mat); + } + } +} + +void workbench_cache_finish(void *ved) +{ + WORKBENCH_Data *vedata = ved; + WORKBENCH_StorageList *stl = vedata->stl; + WORKBENCH_FramebufferList *fbl = vedata->fbl; + WORKBENCH_PrivateData *wpd = stl->wpd; + + /* TODO(fclem) Only do this when really needed. */ + { + /* HACK we allocate the infront depth here to avoid the overhead when if is not needed. */ + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + DRW_texture_ensure_fullscreen_2d(&dtxl->depth_in_front, GPU_DEPTH24_STENCIL8, 0); + + GPU_framebuffer_ensure_config(&dfbl->in_front_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), + GPU_ATTACHMENT_TEXTURE(dtxl->color), + }); + + GPU_framebuffer_ensure_config(&fbl->opaque_infront_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), + GPU_ATTACHMENT_TEXTURE(wpd->material_buffer_tx), + GPU_ATTACHMENT_TEXTURE(wpd->normal_buffer_tx), + GPU_ATTACHMENT_TEXTURE(wpd->object_id_tx), + }); + + GPU_framebuffer_ensure_config(&fbl->transp_accum_infront_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), + GPU_ATTACHMENT_TEXTURE(wpd->accum_buffer_tx), + GPU_ATTACHMENT_TEXTURE(wpd->reveal_buffer_tx), + }); + } + + if (wpd->object_id_tx) { + GPU_framebuffer_ensure_config(&fbl->id_clear_fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(wpd->object_id_tx), + }); + } + else { + GPU_FRAMEBUFFER_FREE_SAFE(fbl->id_clear_fb); + } + + workbench_update_material_ubos(wpd); + + /* TODO don't free reuse next redraw. */ + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + if (wpd->prepass[i][j][k].material_hash) { + BLI_ghash_free(wpd->prepass[i][j][k].material_hash, NULL, NULL); + wpd->prepass[i][j][k].material_hash = NULL; + } + } + } + } +} + +/* Used by viewport rendering & final rendering. + * Do one render loop iteration (i.e: One TAA sample). */ +void workbench_draw_sample(void *ved) +{ + WORKBENCH_Data *vedata = ved; + WORKBENCH_FramebufferList *fbl = vedata->fbl; + WORKBENCH_PrivateData *wpd = vedata->stl->wpd; + WORKBENCH_PassList *psl = vedata->psl; + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float clear_col_with_alpha[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + + const bool do_render = workbench_antialiasing_setup(vedata); + const bool xray_is_visible = wpd->shading.xray_alpha > 0.0f; + const bool do_transparent_infront_pass = !DRW_pass_is_empty(psl->transp_accum_infront_ps); + const bool do_transparent_pass = !DRW_pass_is_empty(psl->transp_accum_ps); + const bool do_opaque_infront_pass = !DRW_pass_is_empty(psl->opaque_infront_ps); + const bool do_opaque_pass = !DRW_pass_is_empty(psl->opaque_ps) || do_opaque_infront_pass; + + if (dfbl->in_front_fb) { + GPU_framebuffer_bind(dfbl->in_front_fb); + GPU_framebuffer_clear_depth(dfbl->in_front_fb, 1.0f); + } + + if (do_render) { + GPU_framebuffer_bind(dfbl->default_fb); + GPU_framebuffer_clear_color_depth_stencil(dfbl->default_fb, wpd->background_color, 1.0f, 0x00); + + if (fbl->id_clear_fb) { + GPU_framebuffer_bind(fbl->id_clear_fb); + GPU_framebuffer_clear_color(fbl->id_clear_fb, clear_col); + } + + if (do_opaque_pass) { + GPU_framebuffer_bind(fbl->opaque_fb); + DRW_draw_pass(psl->opaque_ps); + + if (psl->shadow_ps[0]) { + DRW_draw_pass(psl->shadow_ps[0]); + DRW_draw_pass(psl->shadow_ps[1]); + } + + if (do_opaque_infront_pass) { + GPU_framebuffer_bind(fbl->opaque_infront_fb); + DRW_draw_pass(psl->opaque_infront_ps); + + GPU_framebuffer_bind(fbl->opaque_fb); + DRW_draw_pass(psl->merge_infront_ps); + } + + GPU_framebuffer_bind(dfbl->default_fb); + DRW_draw_pass(psl->composite_ps); + + if (psl->cavity_ps) { + GPU_framebuffer_bind(dfbl->color_only_fb); + DRW_draw_pass(psl->cavity_ps); + } + } + + workbench_volume_draw_pass(vedata); + + if (xray_is_visible) { + if (do_transparent_pass) { + GPU_framebuffer_bind(fbl->transp_accum_fb); + GPU_framebuffer_clear_color(fbl->transp_accum_fb, clear_col_with_alpha); + + DRW_draw_pass(psl->transp_accum_ps); + + GPU_framebuffer_bind(dfbl->color_only_fb); + DRW_draw_pass(psl->transp_resolve_ps); + } + + if (do_transparent_infront_pass) { + GPU_framebuffer_bind(fbl->transp_accum_infront_fb); + GPU_framebuffer_clear_color(fbl->transp_accum_infront_fb, clear_col_with_alpha); + + DRW_draw_pass(psl->transp_accum_infront_ps); + + GPU_framebuffer_bind(dfbl->color_only_fb); + DRW_draw_pass(psl->transp_resolve_ps); + } + } + + workbench_transparent_draw_depth_pass(vedata); + + if (psl->outline_ps) { + GPU_framebuffer_bind(dfbl->color_only_fb); + DRW_draw_pass(psl->outline_ps); + } + + workbench_dof_draw_pass(vedata); + } + + workbench_antialiasing_draw_pass(vedata); +} + +/* Viewport rendering. */ +static void workbench_draw_scene(void *ved) +{ + WORKBENCH_Data *vedata = ved; + WORKBENCH_PrivateData *wpd = vedata->stl->wpd; + + if (DRW_state_is_opengl_render()) { + while (wpd->taa_sample < max_ii(1, wpd->taa_sample_len)) { + workbench_update_world_ubo(wpd); + + workbench_draw_sample(vedata); + } + } + else { + workbench_draw_sample(vedata); + } + + workbench_draw_finish(vedata); +} + +void workbench_draw_finish(void *ved) +{ + WORKBENCH_Data *vedata = ved; + workbench_volume_draw_finish(vedata); +} + +static void workbench_engine_free(void) +{ + workbench_shader_free(); +} + +static void workbench_view_update(void *vedata) +{ + WORKBENCH_Data *data = vedata; + workbench_antialiasing_view_updated(data); +} + +static void workbench_id_update(void *UNUSED(vedata), struct ID *id) +{ + if (GS(id->name) == ID_OB) { + WORKBENCH_ObjectData *oed = (WORKBENCH_ObjectData *)DRW_drawdata_get(id, + &draw_engine_workbench); + if (oed != NULL && oed->dd.recalc != 0) { + oed->shadow_bbox_dirty = (oed->dd.recalc & ID_RECALC_ALL) != 0; + oed->dd.recalc = 0; + } + } +} + +static const DrawEngineDataSize workbench_data_size = DRW_VIEWPORT_DATA_SIZE(WORKBENCH_Data); + +DrawEngineType draw_engine_workbench = { + NULL, + NULL, + N_("Workbench"), + &workbench_data_size, + &workbench_engine_init, + &workbench_engine_free, + &workbench_cache_init, + &workbench_cache_populate, + &workbench_cache_finish, + &workbench_draw_scene, + &workbench_view_update, + &workbench_id_update, + &workbench_render, +}; + RenderEngineType DRW_engine_viewport_workbench_type = { NULL, NULL, @@ -44,7 +583,7 @@ RenderEngineType DRW_engine_viewport_workbench_type = { NULL, NULL, &workbench_render_update_passes, - &draw_engine_workbench_solid, + &draw_engine_workbench, {NULL, NULL, NULL}, }; -- cgit v1.2.3