diff options
Diffstat (limited to 'source/blender/draw')
95 files changed, 2869 insertions, 625 deletions
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 1ff7585165b..3381dbadbab 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -69,6 +69,7 @@ set(SRC intern/mesh_extractors/extract_mesh_vbo_uv.cc intern/mesh_extractors/extract_mesh_vbo_vcol.cc intern/mesh_extractors/extract_mesh_vbo_weights.cc + intern/draw_attributes.cc intern/draw_cache_impl_curve.cc intern/draw_cache_impl_curves.cc intern/draw_cache_impl_displist.c @@ -85,7 +86,7 @@ set(SRC intern/draw_curves.cc intern/draw_debug.c intern/draw_fluid.c - intern/draw_hair.c + intern/draw_hair.cc intern/draw_instance_data.c intern/draw_manager.c intern/draw_manager_data.c @@ -95,7 +96,7 @@ set(SRC intern/draw_manager_text.c intern/draw_manager_texture.c intern/draw_select_buffer.c - intern/draw_shader.c + intern/draw_shader.cc intern/draw_texture_pool.cc intern/draw_view.c intern/draw_view_data.cc @@ -133,6 +134,7 @@ set(SRC engines/eevee/eevee_subsurface.c engines/eevee/eevee_temporal_sampling.c engines/eevee/eevee_volumes.c + engines/eevee_next/eevee_camera.cc engines/eevee_next/eevee_engine.cc engines/eevee_next/eevee_instance.cc engines/eevee_next/eevee_material.cc @@ -140,6 +142,7 @@ set(SRC engines/eevee_next/eevee_shader.cc engines/eevee_next/eevee_sync.cc engines/eevee_next/eevee_view.cc + engines/eevee_next/eevee_velocity.cc engines/eevee_next/eevee_world.cc engines/workbench/workbench_data.c engines/workbench/workbench_effect_antialiasing.c @@ -196,6 +199,7 @@ set(SRC DRW_select_buffer.h intern/DRW_gpu_wrapper.hh intern/DRW_render.h + intern/draw_attributes.h intern/draw_cache.h intern/draw_cache_extract.h intern/draw_cache_impl.h @@ -352,6 +356,7 @@ set(GLSL_SRC engines/eevee/shaders/world_vert.glsl engines/eevee_next/shaders/eevee_attributes_lib.glsl + engines/eevee_next/shaders/eevee_camera_lib.glsl engines/eevee_next/shaders/eevee_geom_curves_vert.glsl engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl @@ -362,6 +367,11 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_surf_forward_frag.glsl engines/eevee_next/shaders/eevee_surf_lib.glsl engines/eevee_next/shaders/eevee_surf_world_frag.glsl + engines/eevee_next/shaders/eevee_velocity_lib.glsl + engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl + + engines/eevee_next/eevee_defines.hh + engines/eevee_next/eevee_shader_shared.hh engines/workbench/shaders/workbench_cavity_lib.glsl engines/workbench/shaders/workbench_common_lib.glsl @@ -422,8 +432,8 @@ set(GLSL_SRC intern/shaders/common_subdiv_vbo_lnor_comp.glsl intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl - intern/draw_shader_shared.h intern/draw_common_shader_shared.h + intern/draw_shader_shared.h engines/gpencil/shaders/gpencil_frag.glsl engines/gpencil/shaders/gpencil_vert.glsl diff --git a/source/blender/draw/DRW_select_buffer.h b/source/blender/draw/DRW_select_buffer.h index 324ebebfbe6..5bc7aee92ba 100644 --- a/source/blender/draw/DRW_select_buffer.h +++ b/source/blender/draw/DRW_select_buffer.h @@ -9,6 +9,10 @@ #include "BLI_sys_types.h" /* for bool and uint */ +#ifdef __cplusplus +extern "C" { +#endif + struct ARegion; struct Base; struct Depsgraph; @@ -133,3 +137,7 @@ uint DRW_select_buffer_find_nearest_to_point(struct Depsgraph *depsgraph, uint id_max, uint *dist); void DRW_select_buffer_context_create(struct Base **bases, uint bases_len, short select_mode); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index b17efe4b68d..33063e14c03 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -68,7 +68,7 @@ BLI_INLINE int eevee_cryptomatte_layers_count(const ViewLayer *view_layer) } /* The number of render result passes are needed to store a single cryptomatte layer. Per - * renderpass 2 cryptomatte samples can be stored. */ + * render-pass 2 cryptomatte samples can be stored. */ BLI_INLINE int eevee_cryptomatte_passes_per_layer(const ViewLayer *view_layer) { const int num_cryptomatte_levels = view_layer->cryptomatte_levels; diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index 4f562dd9804..7f722ff1764 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -95,7 +95,7 @@ typedef struct EEVEE_LightBake { /** Target layer to store the data to. */ int layer; /** Sample count for the convolution. */ - float samples_ct, invsamples_ct; + float samples_count, invsamples_count; /** Sampling bias during convolution step. */ float lod_factor; /** Max cube-map LOD to sample when convolving. */ @@ -282,14 +282,14 @@ static void irradiance_pool_size_get(int visibility_size, int total_samples, int (visibility_size / IRRADIANCE_SAMPLE_SIZE_Y); /* The irradiance itself take one layer, hence the +1 */ - int layer_ct = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER); + int layer_count = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER); - int texel_ct = (int)ceilf((float)total_samples / (float)(layer_ct - 1)); + int texel_count = (int)ceilf((float)total_samples / (float)(layer_count - 1)); r_size[0] = visibility_size * - max_ii(1, min_ii(texel_ct, (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); + max_ii(1, min_ii(texel_count, (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); r_size[1] = visibility_size * - max_ii(1, (texel_ct / (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); - r_size[2] = layer_ct; + max_ii(1, (texel_count / (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); + r_size[2] = layer_count; } static bool EEVEE_lightcache_validate(const LightCache *light_cache, @@ -1118,7 +1118,7 @@ static void eevee_lightbake_render_grid_sample(void *ved, void *user_data) SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex); /* TODO: do this once for the whole bake when we have independent DRWManagers. - * Warning: Some of the things above require this. */ + * WARNING: Some of the things above require this. */ eevee_lightbake_cache_create(vedata, lbake); /* Compute sample position */ diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index 43d0b050cc8..a4bd789438d 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -112,7 +112,7 @@ void EEVEE_lookdev_init(EEVEE_Data *vedata) if (sphere_size != effects->sphere_size || rect->xmax != effects->anchor[0] || rect->ymin != effects->anchor[1]) { - /* Make sphere resolution adaptive to viewport_scale, dpi and lookdev_sphere_size */ + /* Make sphere resolution adaptive to viewport_scale, DPI and #U.lookdev_sphere_size. */ float res_scale = clamp_f( (U.lookdev_sphere_size / 400.0f) * viewport_scale * U.dpi_fac, 0.1f, 1.0f); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index b03a4fa70b4..0a7c8e185c4 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -301,7 +301,7 @@ typedef struct EEVEE_PassList { struct DRWPass *maxz_copydepth_ps; struct DRWPass *maxz_copydepth_layer_ps; - /* Renderpass Accumulation. */ + /* Render-pass Accumulation. */ struct DRWPass *material_accum_ps; struct DRWPass *background_accum_ps; struct DRWPass *cryptomatte_ps; @@ -1069,7 +1069,7 @@ typedef struct EEVEE_PrivateData { GPUTexture *renderpass_col_input; GPUTexture *renderpass_light_input; GPUTexture *renderpass_transmittance_input; - /* Renderpass ubo reference used by material pass. */ + /* Render-pass UBO reference used by material pass. */ struct GPUUniformBuf *renderpass_ubo; /** For rendering shadows. */ struct DRWView *cube_views[6]; diff --git a/source/blender/draw/engines/eevee/eevee_shaders_extra.cc b/source/blender/draw/engines/eevee/eevee_shaders_extra.cc index 05577944140..216a15de2b9 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders_extra.cc +++ b/source/blender/draw/engines/eevee/eevee_shaders_extra.cc @@ -118,6 +118,10 @@ void eevee_shader_material_create_info_amend(GPUMaterial *gpumat, info.vertex_inputs_.clear(); } + if (is_hair) { + info.additional_info("draw_curves_infos"); + } + if (!is_volume) { info.define("EEVEE_GENERATED_INTERFACE"); info.vertex_out(*stage_interface); diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl index 5295a05b965..2926f8c5a89 100644 --- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl @@ -65,6 +65,22 @@ vec3 attr_load_orco(samplerBuffer cd_buf) } # endif +/* Per attribute scope follows loading order. */ +int g_curves_attr_id = 0; + +/* Return the index to use for looking up the attribute value in the sampler + * based on the attribute scope (point or spline). */ +int curves_attribute_element_id() +{ + int id = hairStrandID; + if (drw_curves.is_point_attribute[g_curves_attr_id] != 0) { + id = hair_get_base_id(); + } + + g_curves_attr_id += 1; + return id; +} + vec4 attr_load_tangent(samplerBuffer cd_buf) { /* Not supported. */ @@ -73,22 +89,22 @@ vec4 attr_load_tangent(samplerBuffer cd_buf) vec4 attr_load_vec4(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rgba; + return texelFetch(cd_buf, curves_attribute_element_id()).rgba; } vec3 attr_load_vec3(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rgb; + return texelFetch(cd_buf, curves_attribute_element_id()).rgb; } vec2 attr_load_vec2(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rg; + return texelFetch(cd_buf, curves_attribute_element_id()).rg; } float attr_load_float(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).r; + return texelFetch(cd_buf, curves_attribute_element_id()).r; } #else diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl index 81f73979723..8e1bafe8d92 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -201,4 +201,4 @@ vec3 coordinate_incoming(vec3 P) #else return cameraVec(P); #endif -}
\ No newline at end of file +} diff --git a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl index 04f38978076..a8e95e13b12 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl @@ -72,6 +72,22 @@ vec3 attr_load_orco(samplerBuffer cd_buf) } # endif +/* Per attribute scope follows loading order. */ +int g_curves_attr_id = 0; + +/* Return the index to use for looking up the attribute value in the sampler + * based on the attribute scope (point or spline). */ +int curves_attribute_element_id() +{ + int id = hairStrandID; + if (drw_curves.is_point_attribute[g_curves_attr_id] != 0) { + id = hair_get_base_id(); + } + + g_curves_attr_id += 1; + return id; +} + vec4 attr_load_tangent(samplerBuffer cd_buf) { return vec4(hairTangent, 1.0); @@ -79,22 +95,22 @@ vec4 attr_load_tangent(samplerBuffer cd_buf) vec4 attr_load_vec4(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rgba; + return texelFetch(cd_buf, curves_attribute_element_id()).rgba; } vec3 attr_load_vec3(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rgb; + return texelFetch(cd_buf, curves_attribute_element_id()).rgb; } vec2 attr_load_vec2(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rg; + return texelFetch(cd_buf, curves_attribute_element_id()).rg; } float attr_load_float(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).r; + return texelFetch(cd_buf, curves_attribute_element_id()).r; } #else diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl index d6eeedd8640..a81d37f4e6f 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -8,7 +8,7 @@ flat in int slice; -/* Warning: these are not attributes, these are global vars. */ +/* WARNING: these are not attributes, these are global vars. */ vec3 worldPosition = vec3(0.0); vec3 objectPosition = vec3(0.0); vec3 viewPosition = vec3(0.0); @@ -80,8 +80,8 @@ void main() volumeOrco = OrcoTexCoFactors[0].xyz + objectPosition * OrcoTexCoFactors[1].xyz; if (any(lessThan(volumeOrco, vec3(0.0))) || any(greaterThan(volumeOrco, vec3(1.0)))) { - /* Note: Discard is not an explicit return in Metal prior to versions 2.3. - * adding return after discard ensures consistent behaviour and avoids GPU + /* NOTE: Discard is not an explicit return in Metal prior to versions 2.3. + * adding return after discard ensures consistent behavior and avoids GPU * side-effects where control flow continues with undefined values. */ discard; return; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl index 527bbd18896..3ce54b3122a 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl @@ -70,7 +70,7 @@ void main() vec3 Tr = exp(-s_extinction * s_len); /* integrate along the current step segment */ - /* Note: Original calculation carries precision issues when compiling for AMD GPUs + /* NOTE: Original calculation carries precision issues when compiling for AMD GPUs * and running Metal. This version of the equation retains precision well for all * macOS HW configurations. */ Lscat = (Lscat * (1.0f - Tr)) / max(vec3(1e-8), s_extinction); diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl index ce863bdf660..186f438b03b 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl @@ -76,4 +76,4 @@ vec3 coordinate_reflect(vec3 P, vec3 N) vec3 coordinate_incoming(vec3 P) { return vec3(0.0); -}
\ No newline at end of file +} diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.cc b/source/blender/draw/engines/eevee_next/eevee_camera.cc new file mode 100644 index 00000000000..e6d2e2db764 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_camera.cc @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#include <array> + +#include "DRW_render.h" + +#include "DNA_camera_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_camera.h" +#include "DEG_depsgraph_query.h" +#include "RE_pipeline.h" + +#include "eevee_camera.hh" +#include "eevee_instance.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Camera + * \{ */ + +void Camera::init() +{ + const Object *camera_eval = inst_.camera_eval_object; + synced_ = false; + data_.swap(); + + CameraData &data = data_.current(); + + if (camera_eval) { + const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data); + switch (cam->type) { + default: + case CAM_PERSP: + data.type = CAMERA_PERSP; + break; + case CAM_ORTHO: + data.type = CAMERA_ORTHO; + break; +#if 0 /* TODO(fclem): Make fisheye properties inside blender. */ + case CAM_PANO: { + switch (cam->panorama_type) { + default: + case CAM_PANO_EQUIRECTANGULAR: + data.type = CAMERA_PANO_EQUIRECT; + break; + case CAM_PANO_FISHEYE_EQUIDISTANT: + data.type = CAMERA_PANO_EQUIDISTANT; + break; + case CAM_PANO_FISHEYE_EQUISOLID: + data.type = CAMERA_PANO_EQUISOLID; + break; + case CAM_PANO_MIRRORBALL: + data.type = CAMERA_PANO_MIRROR; + break; + } + } +#endif + } + } + else if (inst_.drw_view) { + data.type = DRW_view_is_persp_get(inst_.drw_view) ? CAMERA_PERSP : CAMERA_ORTHO; + } + else { + /* Light-probe baking. */ + data.type = CAMERA_PERSP; + } +} + +void Camera::sync() +{ + const Object *camera_eval = inst_.camera_eval_object; + CameraData &data = data_.current(); + + data.filter_size = inst_.scene->r.gauss; + + if (inst_.drw_view) { + DRW_view_viewmat_get(inst_.drw_view, data.viewmat.ptr(), false); + DRW_view_viewmat_get(inst_.drw_view, data.viewinv.ptr(), true); + DRW_view_winmat_get(inst_.drw_view, data.winmat.ptr(), false); + DRW_view_winmat_get(inst_.drw_view, data.wininv.ptr(), true); + DRW_view_persmat_get(inst_.drw_view, data.persmat.ptr(), false); + DRW_view_persmat_get(inst_.drw_view, data.persinv.ptr(), true); + DRW_view_camtexco_get(inst_.drw_view, data.uv_scale); + } + else if (inst_.render) { + /* TODO(@fclem): Over-scan. */ + // RE_GetCameraWindowWithOverscan(inst_.render->re, g_data->overscan, data.winmat); + RE_GetCameraWindow(inst_.render->re, camera_eval, data.winmat.ptr()); + RE_GetCameraModelMatrix(inst_.render->re, camera_eval, data.viewinv.ptr()); + invert_m4_m4(data.viewmat.ptr(), data.viewinv.ptr()); + invert_m4_m4(data.wininv.ptr(), data.winmat.ptr()); + mul_m4_m4m4(data.persmat.ptr(), data.winmat.ptr(), data.viewmat.ptr()); + invert_m4_m4(data.persinv.ptr(), data.persmat.ptr()); + data.uv_scale = float2(1.0f); + data.uv_bias = float2(0.0f); + } + else { + data.viewmat = float4x4::identity(); + data.viewinv = float4x4::identity(); + perspective_m4(data.winmat.ptr(), -0.1f, 0.1f, -0.1f, 0.1f, 0.1f, 1.0f); + data.wininv = data.winmat.inverted(); + data.persmat = data.winmat * data.viewmat; + data.persinv = data.persmat.inverted(); + } + + if (camera_eval) { + const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data); + data.clip_near = cam->clip_start; + data.clip_far = cam->clip_end; +#if 0 /* TODO(fclem): Make fisheye properties inside blender. */ + data.fisheye_fov = cam->fisheye_fov; + data.fisheye_lens = cam->fisheye_lens; + data.equirect_bias.x = -cam->longitude_min + M_PI_2; + data.equirect_bias.y = -cam->latitude_min + M_PI_2; + data.equirect_scale.x = cam->longitude_min - cam->longitude_max; + data.equirect_scale.y = cam->latitude_min - cam->latitude_max; + /* Combine with uv_scale/bias to avoid doing extra computation. */ + data.equirect_bias += data.uv_bias * data.equirect_scale; + data.equirect_scale *= data.uv_scale; + + data.equirect_scale_inv = 1.0f / data.equirect_scale; +#endif + } + else if (inst_.drw_view) { + data.clip_near = DRW_view_near_distance_get(inst_.drw_view); + data.clip_far = DRW_view_far_distance_get(inst_.drw_view); + data.fisheye_fov = data.fisheye_lens = -1.0f; + data.equirect_bias = float2(0.0f); + data.equirect_scale = float2(0.0f); + } + + data_.current().push_update(); + + synced_ = true; + + /* Detect changes in parameters. */ + if (data_.current() != data_.previous()) { + // inst_.sampling.reset(); + } +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.hh b/source/blender/draw/engines/eevee_next/eevee_camera.hh index 3db343703e0..dfec738b1f3 100644 --- a/source/blender/draw/engines/eevee_next/eevee_camera.hh +++ b/source/blender/draw/engines/eevee_next/eevee_camera.hh @@ -1,11 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2021 Blender Foundation. - */ + * Copyright 2021 Blender Foundation. */ + +#pragma once /** \file * \ingroup eevee */ +#include "eevee_shader_shared.hh" + namespace blender::eevee { class Instance; @@ -43,4 +46,85 @@ static const float cubeface_mat[6][4][4] = { {0.0f, 0.0f, 0.0f, 1.0f}}, }; +inline void cubeface_winmat_get(float4x4 &winmat, float near, float far) +{ + /* Simple 90° FOV projection. */ + perspective_m4(winmat.ptr(), -near, near, -near, near, near, far); +} + +/* -------------------------------------------------------------------- */ +/** \name CameraData operators + * \{ */ + +inline bool operator==(const CameraData &a, const CameraData &b) +{ + return compare_m4m4(a.persmat.ptr(), b.persmat.ptr(), FLT_MIN) && (a.uv_scale == b.uv_scale) && + (a.uv_bias == b.uv_bias) && (a.equirect_scale == b.equirect_scale) && + (a.equirect_bias == b.equirect_bias) && (a.fisheye_fov == b.fisheye_fov) && + (a.fisheye_lens == b.fisheye_lens) && (a.filter_size == b.filter_size) && + (a.type == b.type); +} + +inline bool operator!=(const CameraData &a, const CameraData &b) +{ + return !(a == b); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Camera + * \{ */ + +/** + * Point of view in the scene. Can be init from viewport or camera object. + */ +class Camera { + private: + Instance &inst_; + + /** Double buffered to detect changes and have history for re-projection. */ + SwapChain<CameraDataBuf, 2> data_; + /** Detects wrong usage. */ + bool synced_ = false; + + public: + Camera(Instance &inst) : inst_(inst){}; + ~Camera(){}; + + void init(); + void sync(); + + /** + * Getters + **/ + const CameraData &data_get() const + { + BLI_assert(synced_); + return data_.current(); + } + const GPUUniformBuf *ubo_get() const + { + return data_.current(); + } + bool is_panoramic() const + { + return eevee::is_panoramic(data_.current().type); + } + bool is_orthographic() const + { + return data_.current().type == CAMERA_ORTHO; + } + const float3 &position() const + { + return *reinterpret_cast<const float3 *>(data_.current().viewinv[3]); + } + const float3 &forward() const + { + return *reinterpret_cast<const float3 *>(data_.current().viewinv[2]); + } +}; + +/** \} */ + } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index 35eb33671db..7141928a20d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -11,9 +11,11 @@ #pragma once -/* Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536. */ -/* Current limiting factor is the sorting phase which is single pass and only sort within a - * threadgroup which maximum size is 1024. */ +/** + Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536. + * Current limiting factor is the sorting phase which is single pass and only sort within a + * thread-group which maximum size is 1024. + */ #define CULLING_BATCH_SIZE 1024 /** diff --git a/source/blender/draw/engines/eevee_next/eevee_engine.cc b/source/blender/draw/engines/eevee_next/eevee_engine.cc index a7d183c1c88..be0adfad568 100644 --- a/source/blender/draw/engines/eevee_next/eevee_engine.cc +++ b/source/blender/draw/engines/eevee_next/eevee_engine.cc @@ -5,6 +5,7 @@ #include "BKE_global.h" #include "BLI_rect.h" +#include "GPU_capabilities.h" #include "GPU_framebuffer.h" #include "ED_view3d.h" @@ -24,10 +25,17 @@ struct EEVEE_Data { DRWViewportEmptyList *psl; DRWViewportEmptyList *stl; eevee::Instance *instance; + + char info[GPU_INFO_SIZE]; }; static void eevee_engine_init(void *vedata) { + /* TODO(fclem): Remove once it is minimum required. */ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + EEVEE_Data *ved = reinterpret_cast<EEVEE_Data *>(vedata); if (ved->instance == nullptr) { ved->instance = new eevee::Instance(); @@ -81,31 +89,50 @@ static void eevee_engine_init(void *vedata) static void eevee_draw_scene(void *vedata) { + EEVEE_Data *ved = reinterpret_cast<EEVEE_Data *>(vedata); + if (!GPU_shader_storage_buffer_objects_support()) { + STRNCPY(ved->info, "Error: No shader storage buffer support"); + return; + } DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - reinterpret_cast<EEVEE_Data *>(vedata)->instance->draw_viewport(dfbl); + ved->instance->draw_viewport(dfbl); + STRNCPY(ved->info, ved->instance->info.c_str()); } static void eevee_cache_init(void *vedata) { + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } reinterpret_cast<EEVEE_Data *>(vedata)->instance->begin_sync(); } static void eevee_cache_populate(void *vedata, Object *object) { + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } reinterpret_cast<EEVEE_Data *>(vedata)->instance->object_sync(object); } static void eevee_cache_finish(void *vedata) { + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } reinterpret_cast<EEVEE_Data *>(vedata)->instance->end_sync(); } static void eevee_engine_free() { + eevee::ShaderModule::module_free(); } static void eevee_instance_free(void *instance) { + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } delete reinterpret_cast<eevee::Instance *>(instance); } @@ -114,11 +141,17 @@ static void eevee_render_to_image(void *UNUSED(vedata), struct RenderLayer *layer, const struct rcti *UNUSED(rect)) { + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } UNUSED_VARS(engine, layer); } static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer) { + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } UNUSED_VARS(engine, scene, view_layer); } diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 922f6c9e1ae..606630bcdef 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -8,6 +8,8 @@ * An instance contains all structures needed to do a complete render. */ +#include <sstream> + #include "BKE_global.h" #include "BKE_object.h" #include "BLI_rect.h" @@ -21,9 +23,9 @@ namespace blender::eevee { /* -------------------------------------------------------------------- */ -/** \name Init +/** \name Initialization * - * Init funcions need to be called once at the start of a frame. + * Initialization functions need to be called once at the start of a frame. * Active camera, render extent and enabled render passes are immutable until next init. * This takes care of resizing output buffers and view in case a parameter changed. * IMPORTANT: xxx.init() functions are NOT meant to acquire and allocate DRW resources. @@ -41,26 +43,36 @@ void Instance::init(const int2 &output_res, const View3D *v3d_, const RegionView3D *rv3d_) { - UNUSED_VARS(light_probe_, camera_object_, output_rect); + UNUSED_VARS(light_probe_, output_rect); render = render_; depsgraph = depsgraph_; + camera_orig_object = camera_object_; render_layer = render_layer_; drw_view = drw_view_; v3d = v3d_; rv3d = rv3d_; + info = ""; + update_eval_members(); main_view.init(output_res); } +void Instance::set_time(float time) +{ + BLI_assert(render); + DRW_render_set_time(render, depsgraph, floorf(time), fractf(time)); + update_eval_members(); +} + void Instance::update_eval_members() { scene = DEG_get_evaluated_scene(depsgraph); view_layer = DEG_get_evaluated_view_layer(depsgraph); - // camera_eval_object = (camera_orig_object) ? - // DEG_get_evaluated_object(depsgraph, camera_orig_object) : - // nullptr; + camera_eval_object = (camera_orig_object) ? + DEG_get_evaluated_object(depsgraph, camera_orig_object) : + nullptr; } /** \} */ @@ -76,6 +88,7 @@ void Instance::update_eval_members() void Instance::begin_sync() { materials.begin_sync(); + velocity.begin_sync(); pipelines.sync(); main_view.sync(); @@ -135,6 +148,7 @@ void Instance::object_sync(Object *ob) void Instance::end_sync() { + velocity.end_sync(); } void Instance::render_sync() @@ -171,6 +185,13 @@ void Instance::draw_viewport(DefaultFramebufferList *dfbl) { UNUSED_VARS(dfbl); render_sample(); + velocity.step_swap(); + + if (materials.queued_shaders_count > 0) { + std::stringstream ss; + ss << "Compiling Shaders " << materials.queued_shaders_count; + info = ss.str(); + } } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index c3cf08c8390..84be59fc5f0 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -15,6 +15,7 @@ #include "DNA_lightprobe_types.h" #include "DRW_render.h" +#include "eevee_camera.hh" #include "eevee_material.hh" #include "eevee_pipeline.hh" #include "eevee_shader.hh" @@ -29,11 +30,15 @@ namespace blender::eevee { * \brief A running instance of the engine. */ class Instance { + friend VelocityModule; + public: ShaderModule &shaders; SyncModule sync; MaterialModule materials; PipelineModule pipelines; + VelocityModule velocity; + Camera camera; MainView main_view; World world; @@ -42,6 +47,8 @@ class Instance { /** Evaluated IDs. */ Scene *scene; ViewLayer *view_layer; + Object *camera_eval_object; + Object *camera_orig_object; /** Only available when rendering for final render. */ const RenderLayer *render_layer; RenderEngine *render; @@ -51,7 +58,7 @@ class Instance { const RegionView3D *rv3d; /* Info string displayed at the top of the render / viewport. */ - char info[64]; + std::string info = ""; public: Instance() @@ -59,6 +66,8 @@ class Instance { sync(*this), materials(*this), pipelines(*this), + velocity(*this), + camera(*this), main_view(*this), world(*this){}; ~Instance(){}; @@ -83,12 +92,37 @@ class Instance { void draw_viewport(DefaultFramebufferList *dfbl); + bool is_viewport(void) + { + return !DRW_state_is_scene_render(); + } + + bool use_scene_lights(void) const + { + return (!v3d) || + ((v3d->shading.type == OB_MATERIAL) && + (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS)) || + ((v3d->shading.type == OB_RENDER) && + (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER)); + } + + /* Light the scene using the selected HDRI in the viewport shading pop-over. */ + bool use_studio_light(void) const + { + return (v3d) && (((v3d->shading.type == OB_MATERIAL) && + ((v3d->shading.flag & V3D_SHADING_SCENE_WORLD) == 0)) || + ((v3d->shading.type == OB_RENDER) && + ((v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER) == 0))); + } + private: void render_sample(); void mesh_sync(Object *ob, ObjectHandle &ob_handle); void update_eval_members(); + + void set_time(float time); }; } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc index 7452e5c26a4..1676c89d679 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.cc +++ b/source/blender/draw/engines/eevee_next/eevee_material.cc @@ -51,7 +51,6 @@ DefaultSurfaceNodeTree::~DefaultSurfaceNodeTree() MEM_SAFE_FREE(ntree_); } -/* Configure a default nodetree with the given material. */ bNodeTree *DefaultSurfaceNodeTree::nodetree_get(::Material *ma) { /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */ @@ -75,11 +74,11 @@ MaterialModule::MaterialModule(Instance &inst) : inst_(inst) { bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname); - diffuse_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default diffuse"); - diffuse_mat_->nodetree = ntree; - diffuse_mat_->use_nodes = true; + diffuse_mat = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default diffuse"); + diffuse_mat->nodetree = ntree; + diffuse_mat->use_nodes = true; /* To use the forward pipeline. */ - diffuse_mat_->blend_method = MA_BM_BLEND; + diffuse_mat->blend_method = MA_BM_BLEND; bNode *bsdf = nodeAddStaticNode(nullptr, ntree, SH_NODE_BSDF_DIFFUSE); bNodeSocket *base_color = nodeFindSocket(bsdf, SOCK_IN, "Color"); @@ -98,11 +97,11 @@ MaterialModule::MaterialModule(Instance &inst) : inst_(inst) { bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname); - glossy_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default metal"); - glossy_mat_->nodetree = ntree; - glossy_mat_->use_nodes = true; + glossy_mat = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default metal"); + glossy_mat->nodetree = ntree; + glossy_mat->use_nodes = true; /* To use the forward pipeline. */ - glossy_mat_->blend_method = MA_BM_BLEND; + glossy_mat->blend_method = MA_BM_BLEND; bNode *bsdf = nodeAddStaticNode(nullptr, ntree, SH_NODE_BSDF_GLOSSY); bNodeSocket *base_color = nodeFindSocket(bsdf, SOCK_IN, "Color"); @@ -149,14 +148,14 @@ MaterialModule::~MaterialModule() for (Material *mat : material_map_.values()) { delete mat; } - BKE_id_free(nullptr, glossy_mat_); - BKE_id_free(nullptr, diffuse_mat_); + BKE_id_free(nullptr, glossy_mat); + BKE_id_free(nullptr, diffuse_mat); BKE_id_free(nullptr, error_mat_); } void MaterialModule::begin_sync() { - queued_shaders_count_ = 0; + queued_shaders_count = 0; for (Material *mat : material_map_.values()) { mat->init = false; @@ -180,7 +179,7 @@ MaterialPass MaterialModule::material_pass_get(::Material *blender_mat, case GPU_MAT_SUCCESS: break; case GPU_MAT_QUEUED: - queued_shaders_count_++; + queued_shaders_count++; blender_mat = (geometry_type == MAT_GEOM_VOLUME) ? BKE_material_default_volume() : BKE_material_default_surface(); matpass.gpumat = inst_.shaders.material_shader_get( @@ -223,7 +222,7 @@ MaterialPass MaterialModule::material_pass_get(::Material *blender_mat, /* IMPORTANT: We always create a subgroup so that all subgroups are inserted after the * first "empty" shgroup. This avoids messing the order of subgroups when there is more * nested subgroup (i.e: hair drawing). */ - /* TODO(fclem) Remove material resource binding from the first group creation. */ + /* TODO(@fclem): Remove material resource binding from the first group creation. */ matpass.shgrp = DRW_shgroup_create_sub(grp); DRW_shgroup_add_material_resources(matpass.shgrp, matpass.gpumat); } @@ -232,21 +231,25 @@ MaterialPass MaterialModule::material_pass_get(::Material *blender_mat, return matpass; } -Material &MaterialModule::material_sync(::Material *blender_mat, eMaterialGeometry geometry_type) +Material &MaterialModule::material_sync(::Material *blender_mat, + eMaterialGeometry geometry_type, + bool has_motion) { eMaterialPipeline surface_pipe = (blender_mat->blend_method == MA_BM_BLEND) ? MAT_PIPE_FORWARD : MAT_PIPE_DEFERRED; eMaterialPipeline prepass_pipe = (blender_mat->blend_method == MA_BM_BLEND) ? - MAT_PIPE_FORWARD_PREPASS : - MAT_PIPE_DEFERRED_PREPASS; + (has_motion ? MAT_PIPE_FORWARD_PREPASS_VELOCITY : + MAT_PIPE_FORWARD_PREPASS) : + (has_motion ? MAT_PIPE_DEFERRED_PREPASS_VELOCITY : + MAT_PIPE_DEFERRED_PREPASS); - /* Test */ + /* TEST until we have deferred pipeline up and running. */ surface_pipe = MAT_PIPE_FORWARD; - prepass_pipe = MAT_PIPE_FORWARD_PREPASS; + prepass_pipe = has_motion ? MAT_PIPE_FORWARD_PREPASS_VELOCITY : MAT_PIPE_FORWARD_PREPASS; MaterialKey material_key(blender_mat, geometry_type, surface_pipe); - /* TODO allocate in blocks to avoid memory fragmentation. */ + /* TODO: allocate in blocks to avoid memory fragmentation. */ auto add_cb = [&]() { return new Material(); }; Material &mat = *material_map_.lookup_or_add_cb(material_key, add_cb); @@ -270,7 +273,6 @@ Material &MaterialModule::material_sync(::Material *blender_mat, eMaterialGeomet return mat; } -/* Return correct material or empty default material if slot is empty. */ ::Material *MaterialModule::material_from_slot(Object *ob, int slot) { if (ob->base_flag & BASE_HOLDOUT) { @@ -281,16 +283,12 @@ Material &MaterialModule::material_sync(::Material *blender_mat, eMaterialGeomet if (ob->type == OB_VOLUME) { return BKE_material_default_volume(); } - else { - return BKE_material_default_surface(); - } + return BKE_material_default_surface(); } return ma; } -/* Returned Material references are valid until the next call to this function or - * material_get(). */ -MaterialArray &MaterialModule::material_array_get(Object *ob) +MaterialArray &MaterialModule::material_array_get(Object *ob, bool has_motion) { material_array_.materials.clear(); material_array_.gpu_materials.clear(); @@ -299,22 +297,23 @@ MaterialArray &MaterialModule::material_array_get(Object *ob) for (auto i : IndexRange(materials_len)) { ::Material *blender_mat = material_from_slot(ob, i); - Material &mat = material_sync(blender_mat, to_material_geometry(ob)); + Material &mat = material_sync(blender_mat, to_material_geometry(ob), has_motion); material_array_.materials.append(&mat); material_array_.gpu_materials.append(mat.shading.gpumat); } return material_array_; } -/* Returned Material references are valid until the next call to this function or - * material_array_get(). */ -Material &MaterialModule::material_get(Object *ob, int mat_nr, eMaterialGeometry geometry_type) +Material &MaterialModule::material_get(Object *ob, + bool has_motion, + int mat_nr, + eMaterialGeometry geometry_type) { ::Material *blender_mat = material_from_slot(ob, mat_nr); - Material &mat = material_sync(blender_mat, geometry_type); + Material &mat = material_sync(blender_mat, geometry_type, has_motion); return mat; } /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_material.hh b/source/blender/draw/engines/eevee_next/eevee_material.hh index 56f9b077f7a..23165a741b9 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.hh +++ b/source/blender/draw/engines/eevee_next/eevee_material.hh @@ -27,19 +27,21 @@ class Instance; enum eMaterialPipeline { MAT_PIPE_DEFERRED = 0, - MAT_PIPE_FORWARD = 1, - MAT_PIPE_DEFERRED_PREPASS = 2, - MAT_PIPE_FORWARD_PREPASS = 3, - MAT_PIPE_VOLUME = 4, - MAT_PIPE_SHADOW = 5, + MAT_PIPE_FORWARD, + MAT_PIPE_DEFERRED_PREPASS, + MAT_PIPE_DEFERRED_PREPASS_VELOCITY, + MAT_PIPE_FORWARD_PREPASS, + MAT_PIPE_FORWARD_PREPASS_VELOCITY, + MAT_PIPE_VOLUME, + MAT_PIPE_SHADOW, }; enum eMaterialGeometry { MAT_GEOM_MESH = 0, - MAT_GEOM_CURVES = 1, - MAT_GEOM_GPENCIL = 2, - MAT_GEOM_VOLUME = 3, - MAT_GEOM_WORLD = 4, + MAT_GEOM_CURVES, + MAT_GEOM_GPENCIL, + MAT_GEOM_VOLUME, + MAT_GEOM_WORLD, }; static inline void material_type_from_shader_uuid(uint64_t shader_uuid, @@ -104,7 +106,7 @@ static inline eMaterialGeometry to_material_geometry(const Object *ob) } } -/** Unique key to identify each material in the hashmap. */ +/** Unique key to identify each material in the hash-map. */ struct MaterialKey { Material *mat; uint64_t options; @@ -169,7 +171,7 @@ struct ShaderKey { /** \} */ /* -------------------------------------------------------------------- */ -/** \name Default Material Nodetree +/** \name Default Material Node-Tree * * In order to support materials without nodetree we reuse and configure a standalone nodetree that * we pass for shader generation. The GPUMaterial is still stored inside the Material even if @@ -189,6 +191,7 @@ class DefaultSurfaceNodeTree { DefaultSurfaceNodeTree(); ~DefaultSurfaceNodeTree(); + /** Configure a default node-tree with the given material. */ bNodeTree *nodetree_get(::Material *ma); }; @@ -217,8 +220,10 @@ struct MaterialArray { class MaterialModule { public: - ::Material *diffuse_mat_; - ::Material *glossy_mat_; + ::Material *diffuse_mat; + ::Material *glossy_mat; + + int64_t queued_shaders_count = 0; private: Instance &inst_; @@ -232,20 +237,28 @@ class MaterialModule { ::Material *error_mat_; - int64_t queued_shaders_count_ = 0; - public: MaterialModule(Instance &inst); ~MaterialModule(); void begin_sync(); - MaterialArray &material_array_get(Object *ob); - Material &material_get(Object *ob, int mat_nr, eMaterialGeometry geometry_type); + /** + * Returned Material references are valid until the next call to this function or material_get(). + */ + MaterialArray &material_array_get(Object *ob, bool has_motion); + /** + * Returned Material references are valid until the next call to this function or + * material_array_get(). + */ + Material &material_get(Object *ob, bool has_motion, int mat_nr, eMaterialGeometry geometry_type); private: - Material &material_sync(::Material *blender_mat, eMaterialGeometry geometry_type); + Material &material_sync(::Material *blender_mat, + eMaterialGeometry geometry_type, + bool has_motion); + /** Return correct material or empty default material if slot is empty. */ ::Material *material_from_slot(Object *ob, int slot); MaterialPass material_pass_get(::Material *blender_mat, eMaterialPipeline pipeline_type, @@ -254,4 +267,4 @@ class MaterialModule { /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index e31372e770d..33853eba06c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -54,11 +54,17 @@ void ForwardPipeline::sync() { DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; prepass_ps_ = DRW_pass_create("Forward.Opaque.Prepass", state); + prepass_velocity_ps_ = DRW_pass_create("Forward.Opaque.Prepass.Velocity", + state | DRW_STATE_WRITE_COLOR); state |= DRW_STATE_CULL_BACK; prepass_culled_ps_ = DRW_pass_create("Forward.Opaque.Prepass.Culled", state); + prepass_culled_velocity_ps_ = DRW_pass_create("Forward.Opaque.Prepass.Velocity", + state | DRW_STATE_WRITE_COLOR); - DRW_pass_link(prepass_ps_, prepass_culled_ps_); + DRW_pass_link(prepass_ps_, prepass_velocity_ps_); + DRW_pass_link(prepass_velocity_ps_, prepass_culled_ps_); + DRW_pass_link(prepass_culled_ps_, prepass_culled_velocity_ps_); } { DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL; @@ -110,11 +116,17 @@ DRWShadingGroup *ForwardPipeline::material_opaque_add(::Material *blender_mat, G return grp; } -DRWShadingGroup *ForwardPipeline::prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat) +DRWShadingGroup *ForwardPipeline::prepass_opaque_add(::Material *blender_mat, + GPUMaterial *gpumat, + bool has_motion) { - DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? prepass_culled_ps_ : - prepass_ps_; + DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? + (has_motion ? prepass_culled_velocity_ps_ : prepass_culled_ps_) : + (has_motion ? prepass_velocity_ps_ : prepass_ps_); DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass); + if (has_motion) { + inst_.velocity.bind_resources(grp); + } return grp; } @@ -181,15 +193,19 @@ DRWShadingGroup *ForwardPipeline::prepass_transparent_add(::Material *blender_ma } void ForwardPipeline::render(const DRWView *view, + Framebuffer &prepass_fb, + Framebuffer &combined_fb, GPUTexture *depth_tx, GPUTexture *UNUSED(combined_tx)) { - UNUSED_VARS(view, depth_tx); + UNUSED_VARS(view, depth_tx, prepass_fb, combined_fb); // HiZBuffer &hiz = inst_.hiz_front; DRW_stats_group_start("ForwardOpaque"); + GPU_framebuffer_bind(prepass_fb); DRW_draw_pass(prepass_ps_); + // hiz.set_dirty(); // if (inst_.raytracing.enabled()) { @@ -199,6 +215,7 @@ void ForwardPipeline::render(const DRWView *view, // inst_.shadows.set_view(view, depth_tx); + GPU_framebuffer_bind(combined_fb); DRW_draw_pass(opaque_ps_); DRW_stats_group_end(); @@ -218,4 +235,4 @@ void ForwardPipeline::render(const DRWView *view, /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index a5a6847f62e..3bdc718767b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -53,7 +53,9 @@ class ForwardPipeline { Instance &inst_; DRWPass *prepass_ps_ = nullptr; + DRWPass *prepass_velocity_ps_ = nullptr; DRWPass *prepass_culled_ps_ = nullptr; + DRWPass *prepass_culled_velocity_ps_ = nullptr; DRWPass *opaque_ps_ = nullptr; DRWPass *opaque_culled_ps_ = nullptr; DRWPass *transparent_ps_ = nullptr; @@ -72,19 +74,25 @@ class ForwardPipeline { material_opaque_add(blender_mat, gpumat); } - DRWShadingGroup *prepass_add(::Material *blender_mat, GPUMaterial *gpumat) + DRWShadingGroup *prepass_add(::Material *blender_mat, GPUMaterial *gpumat, bool has_motion) { return (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) ? prepass_transparent_add(blender_mat, gpumat) : - prepass_opaque_add(blender_mat, gpumat); + prepass_opaque_add(blender_mat, gpumat, has_motion); } DRWShadingGroup *material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat); - DRWShadingGroup *prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat); + DRWShadingGroup *prepass_opaque_add(::Material *blender_mat, + GPUMaterial *gpumat, + bool has_motion); DRWShadingGroup *material_transparent_add(::Material *blender_mat, GPUMaterial *gpumat); DRWShadingGroup *prepass_transparent_add(::Material *blender_mat, GPUMaterial *gpumat); - void render(const DRWView *view, GPUTexture *depth_tx, GPUTexture *combined_tx); + void render(const DRWView *view, + Framebuffer &prepass_fb, + Framebuffer &combined_fb, + GPUTexture *depth_tx, + GPUTexture *combined_tx); }; /** \} */ @@ -191,10 +199,15 @@ class PipelineModule { { switch (pipeline_type) { case MAT_PIPE_DEFERRED_PREPASS: - // return deferred.prepass_add(blender_mat, gpumat); + // return deferred.prepass_add(blender_mat, gpumat, false); + break; + case MAT_PIPE_DEFERRED_PREPASS_VELOCITY: + // return deferred.prepass_add(blender_mat, gpumat, true); break; case MAT_PIPE_FORWARD_PREPASS: - return forward.prepass_add(blender_mat, gpumat); + return forward.prepass_add(blender_mat, gpumat, false); + case MAT_PIPE_FORWARD_PREPASS_VELOCITY: + return forward.prepass_add(blender_mat, gpumat, true); case MAT_PIPE_DEFERRED: // return deferred.material_add(blender_mat, gpumat); break; @@ -213,4 +226,4 @@ class PipelineModule { /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 086c5f9f358..09aa97e49e9 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -25,7 +25,7 @@ ShaderModule *ShaderModule::g_shader_module = nullptr; ShaderModule *ShaderModule::module_get() { if (g_shader_module == nullptr) { - /* TODO(fclem) threadsafety. */ + /* TODO(@fclem) thread-safety. */ g_shader_module = new ShaderModule(); } return g_shader_module; @@ -34,7 +34,7 @@ ShaderModule *ShaderModule::module_get() void ShaderModule::module_free() { if (g_shader_module != nullptr) { - /* TODO(fclem) threadsafety. */ + /* TODO(@fclem) thread-safety. */ delete g_shader_module; g_shader_module = nullptr; } @@ -78,6 +78,8 @@ ShaderModule::~ShaderModule() const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_type) { switch (shader_type) { + case VELOCITY_RESOLVE: + return "eevee_velocity_resolve"; /* To avoid compiler warning about missing case. */ case MAX_SHADER_TYPE: return ""; @@ -148,7 +150,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu /** Noop. */ break; case MAT_GEOM_CURVES: - /** Hair attributes comme from sampler buffer. Transfer attributes to sampler. */ + /** Hair attributes come from sampler buffer. Transfer attributes to sampler. */ for (auto &input : info.vertex_inputs_) { if (input.name == "orco") { /** NOTE: Orco is generated from strand position for now. */ @@ -159,18 +161,19 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu } } info.vertex_inputs_.clear(); + info.additional_info("draw_curves_infos"); break; case MAT_GEOM_WORLD: /** * Only orco layer is supported by world and it is procedurally generated. These are here to - * make the attribs_load function calls valids. + * make the attribs_load function calls valid. */ ATTR_FALLTHROUGH; case MAT_GEOM_GPENCIL: /** * Only one uv and one color attribute layer are supported by gpencil objects and they are * already declared in another createInfo. These are here to make the attribs_load - * function calls valids. + * function calls valid. */ for (auto &input : info.vertex_inputs_) { global_vars << input.type << " " << input.name << ";\n"; @@ -190,7 +193,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu const StageInterfaceInfo &iface = *info.vertex_out_interfaces_.first(); /* Globals the attrib_load() can write to when it is in the fragment shader. */ global_vars << "struct " << iface.name << " {\n"; - for (auto &inout : iface.inouts) { + for (const auto &inout : iface.inouts) { global_vars << " " << inout.type << " " << inout.name << ";\n"; } global_vars << "};\n"; @@ -289,6 +292,10 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu break; default: switch (pipeline_type) { + case MAT_PIPE_FORWARD_PREPASS_VELOCITY: + case MAT_PIPE_DEFERRED_PREPASS_VELOCITY: + info.additional_info("eevee_surf_depth", "eevee_velocity_geom"); + break; case MAT_PIPE_FORWARD_PREPASS: case MAT_PIPE_DEFERRED_PREPASS: case MAT_PIPE_SHADOW: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index ba7c97b3b6a..0f42e880a10 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -26,7 +26,9 @@ namespace blender::eevee { /* Keep alphabetical order and clean prefix. */ enum eShaderType { - MAX_SHADER_TYPE = 0, + VELOCITY_RESOLVE = 0, + + MAX_SHADER_TYPE, }; /** @@ -36,7 +38,7 @@ class ShaderModule { private: std::array<GPUShader *, MAX_SHADER_TYPE> shaders_; - /** Shared shader module accross all engine instances. */ + /** Shared shader module across all engine instances. */ static ShaderModule *g_shader_module; public: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 2225ccac43a..1261c855a82 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -19,6 +19,7 @@ namespace blender::eevee { using draw::Framebuffer; +using draw::SwapChain; using draw::Texture; using draw::TextureFromPool; @@ -27,7 +28,101 @@ using draw::TextureFromPool; #define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14 /* -------------------------------------------------------------------- */ -/** \name Raytracing +/** \name Camera + * \{ */ + +enum eCameraType : uint32_t { + CAMERA_PERSP = 0u, + CAMERA_ORTHO = 1u, + CAMERA_PANO_EQUIRECT = 2u, + CAMERA_PANO_EQUISOLID = 3u, + CAMERA_PANO_EQUIDISTANT = 4u, + CAMERA_PANO_MIRROR = 5u +}; + +static inline bool is_panoramic(eCameraType type) +{ + return type > CAMERA_ORTHO; +} + +struct CameraData { + /* View Matrices of the camera, not from any view! */ + float4x4 persmat; + float4x4 persinv; + float4x4 viewmat; + float4x4 viewinv; + float4x4 winmat; + float4x4 wininv; + /** Camera UV scale and bias. Also known as `viewcamtexcofac`. */ + float2 uv_scale; + float2 uv_bias; + /** Panorama parameters. */ + float2 equirect_scale; + float2 equirect_scale_inv; + float2 equirect_bias; + float fisheye_fov; + float fisheye_lens; + /** Clipping distances. */ + float clip_near; + float clip_far; + /** Film pixel filter radius. */ + float filter_size; + eCameraType type; +}; +BLI_STATIC_ASSERT_ALIGN(CameraData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name VelocityModule + * \{ */ + +#define VELOCITY_INVALID 512.0 + +enum eVelocityStep : uint32_t { + STEP_PREVIOUS = 0, + STEP_NEXT = 1, + STEP_CURRENT = 2, +}; + +struct VelocityObjectIndex { + /** Offset inside #VelocityObjectBuf for each timestep. Indexed using eVelocityStep. */ + int3 ofs; + /** Temporary index to copy this to the #VelocityIndexBuf. */ + uint resource_id; + +#ifdef __cplusplus + VelocityObjectIndex() : ofs(-1, -1, -1), resource_id(-1){}; +#endif +}; +BLI_STATIC_ASSERT_ALIGN(VelocityObjectIndex, 16) + +struct VelocityGeometryIndex { + /** Offset inside #VelocityGeometryBuf for each timestep. Indexed using eVelocityStep. */ + int3 ofs; + /** If true, compute deformation motion blur. */ + bool1 do_deform; + /** Length of data inside #VelocityGeometryBuf for each timestep. Indexed using eVelocityStep. */ + int3 len; + + int _pad0; + +#ifdef __cplusplus + VelocityGeometryIndex() : ofs(-1, -1, -1), do_deform(false), len(-1, -1, -1), _pad0(1){}; +#endif +}; +BLI_STATIC_ASSERT_ALIGN(VelocityGeometryIndex, 16) + +struct VelocityIndex { + VelocityObjectIndex obj; + VelocityGeometryIndex geo; +}; +BLI_STATIC_ASSERT_ALIGN(VelocityGeometryIndex, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Ray-Tracing * \{ */ enum eClosureBits : uint32_t { @@ -83,5 +178,10 @@ float4 utility_tx_sample(sampler2DArray util_tx, float2 uv, float layer) #ifdef __cplusplus +using CameraDataBuf = draw::UniformBuffer<CameraData>; +using VelocityIndexBuf = draw::StorageArrayBuffer<VelocityIndex, 16>; +using VelocityObjectBuf = draw::StorageArrayBuffer<float4x4, 16>; +using VelocityGeometryBuf = draw::StorageArrayBuffer<float4, 16, true>; + } // namespace blender::eevee #endif diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.cc b/source/blender/draw/engines/eevee_next/eevee_sync.cc index efa5fdc89ab..42af251d770 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sync.cc @@ -104,7 +104,9 @@ static inline void shgroup_geometry_call(DRWShadingGroup *grp, void SyncModule::sync_mesh(Object *ob, ObjectHandle &ob_handle) { - MaterialArray &material_array = inst_.materials.material_array_get(ob); + bool has_motion = inst_.velocity.step_object_sync(ob, ob_handle.object_key, ob_handle.recalc); + + MaterialArray &material_array = inst_.materials.material_array_get(ob, has_motion); GPUBatch **mat_geom = DRW_cache_object_surface_material_get( ob, material_array.gpu_materials.data(), material_array.gpu_materials.size()); @@ -129,9 +131,6 @@ void SyncModule::sync_mesh(Object *ob, ObjectHandle &ob_handle) is_alpha_blend = is_alpha_blend || material->is_alpha_blend_transparent; } - UNUSED_VARS(ob_handle); - // shading_passes.velocity.mesh_add(ob, ob_handle); - // shadows.sync_object(ob, ob_handle, is_shadow_caster, is_alpha_blend); } @@ -156,8 +155,11 @@ struct gpIterData { int vcount = 0; bool instancing = false; - gpIterData(Instance &inst_, Object *ob_) - : inst(inst_), ob(ob_), material_array(inst_.materials.material_array_get(ob_)) + gpIterData(Instance &inst_, Object *ob_, ObjectHandle &ob_handle) + : inst(inst_), + ob(ob_), + material_array(inst_.materials.material_array_get( + ob_, inst_.velocity.step_object_sync(ob, ob_handle.object_key, ob_handle.recalc))) { cfra = DEG_get_ctime(inst.depsgraph); }; @@ -253,16 +255,12 @@ void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle) /* TODO(fclem): Waiting for a user option to use the render engine instead of gpencil engine. */ return; - gpIterData iter(inst_, ob); + gpIterData iter(inst_, ob, ob_handle); BKE_gpencil_visible_stroke_iter((bGPdata *)ob->data, nullptr, gpencil_stroke_sync, &iter); gpencil_drawcall_flush(iter); - UNUSED_VARS(ob_handle); - /* TODO(fclem) Gpencil velocity. */ - // shading_passes.velocity.gpencil_add(ob, ob_handle); - // bool is_caster = true; /* TODO material.shadow.shgrp. */ // bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */ // shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend); @@ -304,12 +302,13 @@ void SyncModule::sync_curves(Object *ob, ObjectHandle &ob_handle, ModifierData * mat_nr = part_settings->omat; } - Material &material = inst_.materials.material_get(ob, mat_nr - 1, MAT_GEOM_CURVES); + bool has_motion = inst_.velocity.step_object_sync(ob, ob_handle.object_key, ob_handle.recalc); + Material &material = inst_.materials.material_get(ob, has_motion, mat_nr - 1, MAT_GEOM_CURVES); shgroup_curves_call(material.shading, ob, part_sys, modifier_data); shgroup_curves_call(material.prepass, ob, part_sys, modifier_data); shgroup_curves_call(material.shadow, ob, part_sys, modifier_data); - UNUSED_VARS(ob_handle); + /* TODO(fclem) Hair velocity. */ // shading_passes.velocity.gpencil_add(ob, ob_handle); diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.hh b/source/blender/draw/engines/eevee_next/eevee_sync.hh index 51e0f86fe5c..bd8147a2882 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.hh +++ b/source/blender/draw/engines/eevee_next/eevee_sync.hh @@ -27,7 +27,8 @@ class Instance; /* -------------------------------------------------------------------- */ /** \name ObjectKey * - * Unique key to identify each object in the hashmap. + * Unique key to identify each object in the hash-map. + * Note that we get a unique key for each object component. * \{ */ struct ObjectKey { diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.cc b/source/blender/draw/engines/eevee_next/eevee_velocity.cc new file mode 100644 index 00000000000..9f8dce43910 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.cc @@ -0,0 +1,420 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * The velocity pass outputs motion vectors to use for either + * temporal re-projection or motion blur. + * + * It is the module that tracks the objects between frames updates. + * + * #VelocityModule contains all motion steps data and logic. + * #VelocityPass contains the resolve pass for static geometry. + * #VelocityView is a per view instance that contain the velocity buffer. + */ + +#include "BKE_duplilist.h" +#include "BKE_object.h" +#include "BLI_map.hh" +#include "DEG_depsgraph_query.h" +#include "DNA_rigidbody_types.h" + +#include "eevee_instance.hh" +// #include "eevee_renderpasses.hh" +#include "eevee_shader.hh" +#include "eevee_shader_shared.hh" +#include "eevee_velocity.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name VelocityModule + * + * \{ */ + +void VelocityModule::init() +{ +#if 0 /* TODO renderpasses */ + if (inst_.render && (inst_.render_passes.vector != nullptr)) { + /* No motion blur and the vector pass was requested. Do the step sync here. */ + const Scene *scene = inst_.scene; + float initial_time = scene->r.cfra + scene->r.subframe; + step_sync(STEP_PREVIOUS, initial_time - 1.0f); + step_sync(STEP_NEXT, initial_time + 1.0f); + inst_.set_time(initial_time); + } +#endif +} + +static void step_object_sync_render(void *velocity, + Object *ob, + RenderEngine *UNUSED(engine), + Depsgraph *UNUSED(depsgraph)) +{ + ObjectKey object_key(ob); + reinterpret_cast<VelocityModule *>(velocity)->step_object_sync(ob, object_key); +} + +void VelocityModule::step_sync(eVelocityStep step, float time) +{ + inst_.set_time(time); + step_ = step; + object_steps_usage[step_] = 0; + step_camera_sync(); + DRW_render_object_iter(this, inst_.render, inst_.depsgraph, step_object_sync_render); +} + +void VelocityModule::step_camera_sync() +{ + inst_.camera.sync(); + *camera_steps[step_] = inst_.camera.data_get(); +} + +bool VelocityModule::step_object_sync(Object *ob, + ObjectKey &object_key, + int /*IDRecalcFlag*/ recalc) +{ + bool has_motion = object_has_velocity(ob) || (recalc & ID_RECALC_TRANSFORM); + /* NOTE: Fragile. This will only work with 1 frame of lag since we can't record every geometry + * just in case there might be an update the next frame. */ + bool has_deform = object_is_deform(ob) || (recalc & ID_RECALC_GEOMETRY); + + if (!has_motion && !has_deform) { + return false; + } + + uint32_t resource_id = DRW_object_resource_id_get(ob); + + /* Object motion. */ + /* FIXME(fclem) As we are using original objects pointers, there is a chance the previous + * object key matches a totally different object if the scene was changed by user or python + * callback. In this case, we cannot correctly match objects between updates. + * What this means is that there will be incorrect motion vectors for these objects. + * We live with that until we have a correct way of identifying new objects. */ + VelocityObjectData &vel = velocity_map.lookup_or_add_default(object_key); + vel.obj.ofs[step_] = object_steps_usage[step_]++; + vel.obj.resource_id = resource_id; + vel.id = (ID *)ob->data; + object_steps[step_]->get_or_resize(vel.obj.ofs[step_]) = ob->obmat; + if (step_ == STEP_CURRENT) { + /* Replace invalid steps. Can happen if object was hidden in one of those steps. */ + if (vel.obj.ofs[STEP_PREVIOUS] == -1) { + vel.obj.ofs[STEP_PREVIOUS] = object_steps_usage[STEP_PREVIOUS]++; + object_steps[STEP_PREVIOUS]->get_or_resize(vel.obj.ofs[STEP_PREVIOUS]) = ob->obmat; + } + if (vel.obj.ofs[STEP_NEXT] == -1) { + vel.obj.ofs[STEP_NEXT] = object_steps_usage[STEP_NEXT]++; + object_steps[STEP_NEXT]->get_or_resize(vel.obj.ofs[STEP_NEXT]) = ob->obmat; + } + } + + /* Geometry motion. */ + if (has_deform) { + auto add_cb = [&]() { + VelocityGeometryData data; + switch (ob->type) { + case OB_CURVES: + data.pos_buf = DRW_curves_pos_buffer_get(ob); + break; + default: + data.pos_buf = DRW_cache_object_pos_vertbuf_get(ob); + break; + } + return data; + }; + + const VelocityGeometryData &data = geometry_map.lookup_or_add_cb(vel.id, add_cb); + + if (data.pos_buf == nullptr) { + has_deform = false; + } + } + + /* Avoid drawing object that has no motions but were tagged as such. */ + if (step_ == STEP_CURRENT && has_motion == true && has_deform == false) { + float4x4 &obmat_curr = (*object_steps[STEP_CURRENT])[vel.obj.ofs[STEP_CURRENT]]; + float4x4 &obmat_prev = (*object_steps[STEP_PREVIOUS])[vel.obj.ofs[STEP_PREVIOUS]]; + float4x4 &obmat_next = (*object_steps[STEP_NEXT])[vel.obj.ofs[STEP_NEXT]]; + if (inst_.is_viewport()) { + has_motion = (obmat_curr != obmat_prev); + } + else { + has_motion = (obmat_curr != obmat_prev || obmat_curr != obmat_next); + } + } + +#if 0 + if (!has_motion && !has_deform) { + std::cout << "Detected no motion on " << ob->id.name << std::endl; + } + if (has_deform) { + std::cout << "Geometry Motion on " << ob->id.name << std::endl; + } + if (has_motion) { + std::cout << "Object Motion on " << ob->id.name << std::endl; + } +#endif + + if (!has_motion && !has_deform) { + return false; + } + + /* TODO(@fclem): Reset sampling here? Should ultimately be covered by depsgraph update tags. */ + // inst_.sampling.reset(); + + return true; +} + +/** + * Moves next frame data to previous frame data. Nullify next frame data. + * IMPORTANT: This runs AFTER drawing in the viewport (so after `begin_sync()`) but BEFORE drawing + * in render mode (so before `begin_sync()`). In viewport the data will be used the next frame. + */ +void VelocityModule::step_swap() +{ + { + /* Now that vertex buffers are guaranteed to be updated, proceed with + * offset computation and copy into the geometry step buffer. */ + uint dst_ofs = 0; + for (VelocityGeometryData &geom : geometry_map.values()) { + uint src_len = GPU_vertbuf_get_vertex_len(geom.pos_buf); + geom.len = src_len; + geom.ofs = dst_ofs; + dst_ofs += src_len; + } + /* TODO(@fclem): Fail gracefully (disable motion blur + warning print) if + `tot_len * sizeof(float4)` is greater than max SSBO size. */ + geometry_steps[step_]->resize(max_ii(16, dst_ofs)); + + for (VelocityGeometryData &geom : geometry_map.values()) { + GPU_storagebuf_copy_sub_from_vertbuf(*geometry_steps[step_], + geom.pos_buf, + geom.ofs * sizeof(float4), + 0, + geom.len * sizeof(float4)); + } + /* Copy back the #VelocityGeometryIndex into #VelocityObjectData which are + * indexed using persistent keys (unlike geometries which are indexed by volatile ID). */ + for (VelocityObjectData &vel : velocity_map.values()) { + const VelocityGeometryData &geom = geometry_map.lookup_default(vel.id, + VelocityGeometryData()); + vel.geo.len[step_] = geom.len; + vel.geo.ofs[step_] = geom.ofs; + /* Avoid reuse. */ + vel.id = nullptr; + } + + geometry_map.clear(); + } + + auto swap_steps = [&](eVelocityStep step_a, eVelocityStep step_b) { + SWAP(VelocityObjectBuf *, object_steps[step_a], object_steps[step_b]); + SWAP(VelocityGeometryBuf *, geometry_steps[step_a], geometry_steps[step_b]); + SWAP(CameraDataBuf *, camera_steps[step_a], camera_steps[step_b]); + + for (VelocityObjectData &vel : velocity_map.values()) { + vel.obj.ofs[step_a] = vel.obj.ofs[step_b]; + vel.obj.ofs[step_b] = (uint)-1; + vel.geo.ofs[step_a] = vel.geo.ofs[step_b]; + vel.geo.len[step_a] = vel.geo.len[step_b]; + vel.geo.ofs[step_b] = (uint)-1; + vel.geo.len[step_b] = (uint)-1; + } + }; + + if (inst_.is_viewport()) { + /* For viewport we only use the last rendered redraw as previous frame. + * We swap current with previous step at the end of a redraw. + * We do not support motion blur as it is rendered to avoid conflicting motions + * for temporal reprojection. */ + swap_steps(eVelocityStep::STEP_PREVIOUS, eVelocityStep::STEP_CURRENT); + } + else { + /* Render case: The STEP_CURRENT is left untouched. */ + swap_steps(eVelocityStep::STEP_PREVIOUS, eVelocityStep::STEP_NEXT); + } +} + +void VelocityModule::begin_sync() +{ + if (inst_.is_viewport()) { + /* Viewport always evaluate current step. */ + step_ = STEP_CURRENT; + } + step_camera_sync(); + object_steps_usage[step_] = 0; +} + +/* This is the end of the current frame sync. Not the step_sync. */ +void VelocityModule::end_sync() +{ + Vector<ObjectKey, 0> deleted_obj; + + uint32_t max_resource_id_ = 0u; + + for (Map<ObjectKey, VelocityObjectData>::Item item : velocity_map.items()) { + if (item.value.obj.resource_id == (uint)-1) { + deleted_obj.append(item.key); + } + else { + max_resource_id_ = max_uu(max_resource_id_, item.value.obj.resource_id); + } + } + + if (deleted_obj.size() > 0) { + // inst_.sampling.reset(); + } + + for (auto key : deleted_obj) { + velocity_map.remove(key); + } + + indirection_buf.resize(power_of_2_max_u(max_resource_id_ + 1)); + + /* Avoid uploading more data to the GPU as well as an extra level of + * indirection on the GPU by copying back offsets the to VelocityIndex. */ + for (VelocityObjectData &vel : velocity_map.values()) { + /* Disable deform if vertex count mismatch. */ + if (inst_.is_viewport()) { + /* Current geometry step will be copied at the end of the frame. + * Thus vel.geo.len[STEP_CURRENT] is not yet valid and the current length is manually + * retrieved. */ + GPUVertBuf *pos_buf = geometry_map.lookup_default(vel.id, VelocityGeometryData()).pos_buf; + vel.geo.do_deform = pos_buf != nullptr && + (vel.geo.len[STEP_PREVIOUS] == GPU_vertbuf_get_vertex_len(pos_buf)); + } + else { + vel.geo.do_deform = (vel.geo.len[STEP_PREVIOUS] == vel.geo.len[STEP_CURRENT]) && + (vel.geo.len[STEP_NEXT] == vel.geo.len[STEP_CURRENT]); + } + indirection_buf[vel.obj.resource_id] = vel; + /* Reset for next sync. */ + vel.obj.resource_id = (uint)-1; + } + + object_steps[STEP_PREVIOUS]->push_update(); + object_steps[STEP_NEXT]->push_update(); + camera_steps[STEP_PREVIOUS]->push_update(); + camera_steps[STEP_CURRENT]->push_update(); + camera_steps[STEP_NEXT]->push_update(); + indirection_buf.push_update(); + + { + resolve_ps_ = DRW_pass_create("Velocity.Resolve", (DRWState)0); + GPUShader *sh = inst_.shaders.static_shader_get(VELOCITY_RESOLVE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_); + DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_); + DRW_shgroup_uniform_image_ref(grp, "velocity_view_img", &velocity_view_tx_); + DRW_shgroup_uniform_image_ref(grp, "velocity_camera_img", &velocity_camera_tx_); + DRW_shgroup_uniform_block(grp, "camera_prev", *camera_steps[STEP_PREVIOUS]); + DRW_shgroup_uniform_block(grp, "camera_curr", *camera_steps[STEP_CURRENT]); + DRW_shgroup_uniform_block(grp, "camera_next", *camera_steps[STEP_NEXT]); + DRW_shgroup_call_compute_ref(grp, resolve_dispatch_size_); + } +} + +bool VelocityModule::object_has_velocity(const Object *ob) +{ +#if 0 + RigidBodyOb *rbo = ob->rigidbody_object; + /* Active rigidbody objects only, as only those are affected by sim. */ + const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE)); + /* For now we assume dupli objects are moving. */ + const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0; + const bool object_moves = is_dupli || has_rigidbody || BKE_object_moves_in_time(ob, true); +#else + UNUSED_VARS(ob); + /* BKE_object_moves_in_time does not work in some cases. + * Better detect non moving object after evaluation. */ + const bool object_moves = true; +#endif + return object_moves; +} + +bool VelocityModule::object_is_deform(const Object *ob) +{ + RigidBodyOb *rbo = ob->rigidbody_object; + /* Active rigidbody objects only, as only those are affected by sim. */ + const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE)); + const bool is_deform = BKE_object_is_deform_modified(inst_.scene, (Object *)ob) || + (has_rigidbody && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0); + + return is_deform; +} + +void VelocityModule::bind_resources(DRWShadingGroup *grp) +{ + /* For viewport, only previous motion is supported. + * Still bind previous step to avoid undefined behavior. */ + eVelocityStep next = inst_.is_viewport() ? STEP_PREVIOUS : STEP_NEXT; + DRW_shgroup_storage_block_ref(grp, "velocity_obj_prev_buf", &(*object_steps[STEP_PREVIOUS])); + DRW_shgroup_storage_block_ref(grp, "velocity_obj_next_buf", &(*object_steps[next])); + DRW_shgroup_storage_block_ref(grp, "velocity_geo_prev_buf", &(*geometry_steps[STEP_PREVIOUS])); + DRW_shgroup_storage_block_ref(grp, "velocity_geo_next_buf", &(*geometry_steps[next])); + DRW_shgroup_uniform_block_ref(grp, "camera_prev", &(*camera_steps[STEP_PREVIOUS])); + DRW_shgroup_uniform_block_ref(grp, "camera_curr", &(*camera_steps[STEP_CURRENT])); + DRW_shgroup_uniform_block_ref(grp, "camera_next", &(*camera_steps[next])); + DRW_shgroup_storage_block_ref(grp, "velocity_indirection_buf", &indirection_buf); +} + +/* Resolve pass for static geometry and to camera space projection. */ +void VelocityModule::resolve_camera_motion(GPUTexture *depth_tx, + GPUTexture *velocity_view_tx, + GPUTexture *velocity_camera_tx) +{ + input_depth_tx_ = depth_tx; + velocity_view_tx_ = velocity_view_tx; + velocity_camera_tx_ = velocity_camera_tx; + + resolve_dispatch_size_.x = divide_ceil_u(GPU_texture_width(depth_tx), 8); + resolve_dispatch_size_.y = divide_ceil_u(GPU_texture_height(depth_tx), 8); + + DRW_draw_pass(resolve_ps_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Velocity View + * \{ */ + +void VelocityView::sync() +{ + /* TODO: Remove. */ + velocity_view_tx_.sync(); + velocity_camera_tx_.sync(); +} + +void VelocityView::acquire(int2 extent) +{ + /* WORKAROUND: View name should be unique and static. + * With this, we can reuse the same texture across views. */ + DrawEngineType *owner = (DrawEngineType *)view_name_.c_str(); + + /* Only RG16F when only doing only reprojection or motion blur. */ + eGPUTextureFormat format = inst_.is_viewport() ? GPU_RG16F : GPU_RGBA16F; + velocity_view_tx_.acquire(extent, format, owner); + if (false /* TODO(fclem): Panoramic camera. */) { + velocity_camera_tx_.acquire(extent, format, owner); + } + else { + velocity_camera_tx_.acquire(int2(1), format, owner); + } +} + +void VelocityView::resolve(GPUTexture *depth_tx) +{ + inst_.velocity.resolve_camera_motion(depth_tx, velocity_view_tx_, velocity_camera_tx_); +} + +void VelocityView::release() +{ + velocity_view_tx_.release(); + velocity_camera_tx_.release(); +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.hh b/source/blender/draw/engines/eevee_next/eevee_velocity.hh new file mode 100644 index 00000000000..1bfd9f8c18f --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.hh @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * The velocity pass outputs motion vectors to use for either + * temporal re-projection or motion blur. + * + * It is the module that tracks the objects data between frames updates. + */ + +#pragma once + +#include "BLI_map.hh" + +#include "eevee_shader_shared.hh" +#include "eevee_sync.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name VelocityModule + * + * \{ */ + +/** Container for scene velocity data. */ +class VelocityModule { + friend class VelocityView; + + public: + struct VelocityObjectData : public VelocityIndex { + /** ID to retrieve the corresponding #VelocityGeometryData after copy. */ + ID *id; + }; + struct VelocityGeometryData { + /** VertBuf not yet ready to be copied to the #VelocityGeometryBuf. */ + GPUVertBuf *pos_buf = nullptr; + /* Offset in the #VelocityGeometryBuf to the start of the data. In vertex. */ + int ofs; + /* Length of the vertex buffer. In vertex. */ + int len; + }; + /** + * The map contains indirection indices to the obmat and geometry in each step buffer. + * Note that each object component gets its own resource id so one component correspond to one + * geometry offset. + */ + Map<ObjectKey, VelocityObjectData> velocity_map; + /** Geometry to be copied to VelocityGeometryBuf. Indexed by evaluated ID *. Empty after */ + Map<ID *, VelocityGeometryData> geometry_map; + /** Contains all objects matrices for each time step. */ + std::array<VelocityObjectBuf *, 3> object_steps; + /** Contains all Geometry steps from deforming objects for each time step. */ + std::array<VelocityGeometryBuf *, 3> geometry_steps; + /** Number of occupied slot in each `object_steps`. */ + int3 object_steps_usage = int3(0); + /** Buffer of all #VelocityIndex used in this frame. Indexed by draw manager resource id. */ + VelocityIndexBuf indirection_buf; + + /** + * Copies of camera data. One for previous and one for next time step. + */ + std::array<CameraDataBuf *, 3> camera_steps; + + private: + Instance &inst_; + + eVelocityStep step_ = STEP_CURRENT; + + DRWPass *resolve_ps_ = nullptr; + + /** Reference only. Not owned. */ + GPUTexture *input_depth_tx_; + GPUTexture *velocity_view_tx_; + GPUTexture *velocity_camera_tx_; + + int3 resolve_dispatch_size_ = int3(1, 1, 1); + + public: + VelocityModule(Instance &inst) : inst_(inst) + { + for (VelocityObjectBuf *&step_buf : object_steps) { + step_buf = new VelocityObjectBuf(); + } + for (VelocityGeometryBuf *&step_buf : geometry_steps) { + step_buf = new VelocityGeometryBuf(); + } + for (CameraDataBuf *&step_buf : camera_steps) { + step_buf = new CameraDataBuf(); + } + }; + + ~VelocityModule() + { + for (VelocityObjectBuf *step_buf : object_steps) { + delete step_buf; + } + for (VelocityGeometryBuf *step_buf : geometry_steps) { + delete step_buf; + } + for (CameraDataBuf *step_buf : camera_steps) { + delete step_buf; + } + } + + void init(); + + void step_camera_sync(); + void step_sync(eVelocityStep step, float time); + + /* Gather motion data. Returns true if the object **can** have motion. */ + bool step_object_sync(Object *ob, ObjectKey &ob_key, int recalc = 0); + + /* Moves next frame data to previous frame data. Nullify next frame data. */ + void step_swap(); + + void begin_sync(); + void end_sync(); + + void bind_resources(DRWShadingGroup *grp); + + private: + bool object_has_velocity(const Object *ob); + bool object_is_deform(const Object *ob); + + void resolve_camera_motion(GPUTexture *depth_tx, + GPUTexture *velocity_view_tx, + GPUTexture *velocity_camera_tx); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Velocity + * + * \{ */ + +/** + * Per view module. + */ +class VelocityView { + private: + Instance &inst_; + + StringRefNull view_name_; + + TextureFromPool velocity_camera_tx_ = {"velocity_camera_tx_"}; + TextureFromPool velocity_view_tx_ = {"velocity_view_tx_"}; + + public: + VelocityView(Instance &inst, const char *name) : inst_(inst), view_name_(name){}; + ~VelocityView(){}; + + void sync(); + + void acquire(int2 extent); + void release(); + + void resolve(GPUTexture *depth_tx); + + /** + * Getters + **/ + GPUTexture *view_vectors_get() const + { + return velocity_view_tx_; + } + GPUTexture *camera_vectors_get() const + { + return (velocity_camera_tx_.is_valid()) ? velocity_camera_tx_ : velocity_view_tx_; + } +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index df45200c712..e21342c5ef6 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -9,7 +9,7 @@ * - The entire main view. * - A fragment of the main view (for panoramic projections). * - A shadow map view. - * - A lightprobe view (either planar, cubemap, irradiance grid). + * - A light-probe view (either planar, cube-map, irradiance grid). * * A pass is a container for scene data. It is view agnostic but has specific logic depending on * its type. Passes are shared between views. @@ -40,7 +40,7 @@ void ShadingView::sync(int2 render_extent_) int64_t render_pixel_count = render_extent_.x * (int64_t)render_extent_.y; /* Divide pixel count between the 6 views. Rendering to a square target. */ extent_[0] = extent_[1] = ceilf(sqrtf(1 + (render_pixel_count / 6))); - /* TODO(fclem) Clip unused views heres. */ + /* TODO(@fclem): Clip unused views here. */ is_enabled_ = true; } else { @@ -60,8 +60,8 @@ void ShadingView::sync(int2 render_extent_) const float(*viewmat_p)[4] = viewmat.ptr(), (*winmat_p)[4] = winmat.ptr(); #if 0 if (false /* inst_.camera.is_panoramic() */) { - /* TODO(fclem) Overscans. */ - /* For now a mandatory 5% overscan for DoF. */ + /* TODO(@fclem) Over-scans. */ + /* For now a mandatory 5% over-scan for DoF. */ float side = data.clip_near * 1.05f; float near = data.clip_near; float far = data.clip_far; @@ -86,7 +86,7 @@ void ShadingView::sync(int2 render_extent_) // dof_.sync(winmat_p, extent_); // mb_.sync(extent_); - // velocity_.sync(extent_); + velocity_.sync(); // rt_buffer_opaque_.sync(extent_); // rt_buffer_refract_.sync(extent_); // inst_.hiz_back.view_sync(extent_); @@ -108,22 +108,30 @@ void ShadingView::render() * With this, we can reuse the same texture across views. */ DrawEngineType *owner = (DrawEngineType *)name_; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + depth_tx_.ensure_2d(GPU_DEPTH24_STENCIL8, extent_); combined_tx_.acquire(extent_, GPU_RGBA16F, owner); - view_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_), GPU_ATTACHMENT_TEXTURE(combined_tx_)); + velocity_.acquire(extent_); + // combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_), GPU_ATTACHMENT_TEXTURE(combined_tx_)); + // prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_), + // GPU_ATTACHMENT_TEXTURE(velocity_.view_vectors_get())); + combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(dtxl->color)); + prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_TEXTURE(velocity_.view_vectors_get())); update_view(); DRW_stats_group_start(name_); // DRW_view_set_active(render_view_); + float4 clear_velocity(VELOCITY_INVALID); + GPU_framebuffer_bind(prepass_fb_); + GPU_framebuffer_clear_color(prepass_fb_, clear_velocity); /* Alpha stores transmittance. So start at 1. */ float4 clear_color = {0.0f, 0.0f, 0.0f, 1.0f}; - // GPU_framebuffer_bind(view_fb_); - // GPU_framebuffer_clear_color_depth(view_fb_, clear_color, 1.0f); - DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - GPU_framebuffer_bind(dfbl->default_fb); - GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_color, 1.0f); + GPU_framebuffer_bind(combined_fb_); + GPU_framebuffer_clear_color_depth(combined_fb_, clear_color, 1.0f); inst_.pipelines.world.render(); @@ -134,12 +142,13 @@ void ShadingView::render() // inst_.lookdev.render_overlay(view_fb_); - inst_.pipelines.forward.render(render_view_, depth_tx_, combined_tx_); + inst_.pipelines.forward.render(render_view_, prepass_fb_, combined_fb_, depth_tx_, combined_tx_); // inst_.lights.debug_draw(view_fb_); // inst_.shadows.debug_draw(view_fb_); - // velocity_.render(depth_tx_); + // velocity_.resolve(depth_tx_); + velocity_.resolve(dtxl->depth); // if (inst_.render_passes.vector) { // inst_.render_passes.vector->accumulate(velocity_.camera_vectors_get(), sub_view_); @@ -159,6 +168,7 @@ void ShadingView::render() combined_tx_.release(); postfx_tx_.release(); + velocity_.release(); } GPUTexture *ShadingView::render_post(GPUTexture *input_tx) @@ -205,4 +215,4 @@ void ShadingView::update_view() /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh index ab7b5722de1..95ec1760c63 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.hh +++ b/source/blender/draw/engines/eevee_next/eevee_view.hh @@ -8,7 +8,7 @@ * A view is either: * - The entire main view. * - A portion of the main view (for panoramic projections). - * - A lightprobe view (either planar, cubemap, irradiance grid). + * - A light-probe view (either planar, cube-map, irradiance grid). * * A pass is a container for scene data. It is view agnostic but has specific logic depending on * its type. Passes are shared between views. @@ -21,6 +21,7 @@ #include "eevee_camera.hh" #include "eevee_pipeline.hh" #include "eevee_shader.hh" +#include "eevee_velocity.hh" namespace blender::eevee { @@ -43,13 +44,14 @@ class ShadingView { /** Post-fx modules. */ // DepthOfField dof_; // MotionBlur mb_; - // Velocity velocity_; + VelocityView velocity_; /** Raytracing persistent buffers. Only opaque and refraction can have surface tracing. */ // RaytraceBuffer rt_buffer_opaque_; // RaytraceBuffer rt_buffer_refract_; - Framebuffer view_fb_; + Framebuffer prepass_fb_; + Framebuffer combined_fb_; Texture depth_tx_; TextureFromPool combined_tx_; TextureFromPool postfx_tx_; @@ -69,7 +71,7 @@ class ShadingView { public: ShadingView(Instance &inst, const char *name, const float (*face_matrix)[4]) - : inst_(inst), name_(name), face_matrix_(face_matrix){}; + : inst_(inst), name_(name), face_matrix_(face_matrix), velocity_(inst, name){}; ~ShadingView(){}; @@ -154,4 +156,4 @@ class MainView { /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_world.cc b/source/blender/draw/engines/eevee_next/eevee_world.cc index 939f6087137..b9cb24fe30a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_world.cc +++ b/source/blender/draw/engines/eevee_next/eevee_world.cc @@ -64,19 +64,17 @@ void World::sync() // } ::World *bl_world = inst_.scene->world; - if (bl_world == nullptr) { // bl_world = BKE_world_default(); return; } - else { - WorldHandle &wo_handle = inst_.sync.sync_world(bl_world); - if (wo_handle.recalc != 0) { - // inst_.lightprobes.set_world_dirty(); - } - wo_handle.reset_recalc_flag(); + WorldHandle &wo_handle = inst_.sync.sync_world(bl_world); + + if (wo_handle.recalc != 0) { + // inst_.lightprobes.set_world_dirty(); } + wo_handle.reset_recalc_flag(); /* TODO(fclem) This should be detected to scene level. */ ::World *orig_world = (::World *)DEG_get_original_id(&bl_world->id); diff --git a/source/blender/draw/engines/eevee_next/eevee_world.hh b/source/blender/draw/engines/eevee_next/eevee_world.hh index 56554051eea..05177928436 100644 --- a/source/blender/draw/engines/eevee_next/eevee_world.hh +++ b/source/blender/draw/engines/eevee_next/eevee_world.hh @@ -18,7 +18,7 @@ namespace blender::eevee { class Instance; /* -------------------------------------------------------------------- */ -/** \name Default World Nodetree +/** \name Default World Node-Tree * * In order to support worlds without nodetree we reuse and configure a standalone nodetree that * we pass for shader generation. The GPUMaterial is still stored inside the World even if diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl index 3c5acf62e30..1b113e529b6 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl @@ -112,6 +112,7 @@ float attr_load_float(float attr) /** \name Curve * * Curve objects loads attributes from buffers through sampler buffers. + * Per attribute scope follows loading order. * \{ */ # ifdef OBINFO_LIB @@ -122,6 +123,22 @@ vec3 attr_load_orco(vec4 orco) return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz; } # endif + +int g_curves_attr_id = 0; + +/* Return the index to use for looking up the attribute value in the sampler + * based on the attribute scope (point or spline). */ +int curves_attribute_element_id() +{ + int id = interp.curves_strand_id; + if (drw_curves.is_point_attribute[g_curves_attr_id] != 0) { + id = hair_get_base_id(); + } + + g_curves_attr_id += 1; + return id; +} + vec4 attr_load_tangent(samplerBuffer cd_buf) { /* Not supported for the moment. */ @@ -137,19 +154,19 @@ vec4 attr_load_color(samplerBuffer cd_buf) } vec4 attr_load_vec4(samplerBuffer cd_buf) { - return texelFetch(cd_buf, interp.curves_strand_id).rgba; + return texelFetch(cd_buf, curves_attribute_element_id()).rgba; } vec3 attr_load_vec3(samplerBuffer cd_buf) { - return texelFetch(cd_buf, interp.curves_strand_id).rgb; + return texelFetch(cd_buf, curves_attribute_element_id()).rgb; } vec2 attr_load_vec2(samplerBuffer cd_buf) { - return texelFetch(cd_buf, interp.curves_strand_id).rg; + return texelFetch(cd_buf, curves_attribute_element_id()).rg; } float attr_load_float(samplerBuffer cd_buf) { - return texelFetch(cd_buf, interp.curves_strand_id).r; + return texelFetch(cd_buf, curves_attribute_element_id()).r; } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl new file mode 100644 index 00000000000..f79e9102d76 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl @@ -0,0 +1,166 @@ + +/** + * Camera projection / uv functions and utils. + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +/* -------------------------------------------------------------------- */ +/** \name Panoramic Projections + * + * Adapted from Cycles to match EEVEE's coordinate system. + * \{ */ + +vec2 camera_equirectangular_from_direction(CameraData cam, vec3 dir) +{ + float phi = atan(-dir.z, dir.x); + float theta = acos(dir.y / length(dir)); + return (vec2(phi, theta) - cam.equirect_bias) * cam.equirect_scale_inv; +} + +vec3 camera_equirectangular_to_direction(CameraData cam, vec2 uv) +{ + uv = uv * cam.equirect_scale + cam.equirect_bias; + float phi = uv.x; + float theta = uv.y; + float sin_theta = sin(theta); + return vec3(sin_theta * cos(phi), cos(theta), -sin_theta * sin(phi)); +} + +vec2 camera_fisheye_from_direction(CameraData cam, vec3 dir) +{ + float r = atan(length(dir.xy), -dir.z) / cam.fisheye_fov; + float phi = atan(dir.y, dir.x); + vec2 uv = r * vec2(cos(phi), sin(phi)) + 0.5; + return (uv - cam.uv_bias) / cam.uv_scale; +} + +vec3 camera_fisheye_to_direction(CameraData cam, vec2 uv) +{ + uv = uv * cam.uv_scale + cam.uv_bias; + uv = (uv - 0.5) * 2.0; + float r = length(uv); + if (r > 1.0) { + return vec3(0.0); + } + float phi = safe_acos(uv.x * safe_rcp(r)); + float theta = r * cam.fisheye_fov * 0.5; + if (uv.y < 0.0) { + phi = -phi; + } + return vec3(cos(phi) * sin(theta), sin(phi) * sin(theta), -cos(theta)); +} + +vec2 camera_mirror_ball_from_direction(CameraData cam, vec3 dir) +{ + dir = normalize(dir); + dir.z -= 1.0; + dir *= safe_rcp(2.0 * safe_sqrt(-0.5 * dir.z)); + vec2 uv = 0.5 * dir.xy + 0.5; + return (uv - cam.uv_bias) / cam.uv_scale; +} + +vec3 camera_mirror_ball_to_direction(CameraData cam, vec2 uv) +{ + uv = uv * cam.uv_scale + cam.uv_bias; + vec3 dir; + dir.xy = uv * 2.0 - 1.0; + if (len_squared(dir.xy) > 1.0) { + return vec3(0.0); + } + dir.z = -safe_sqrt(1.0 - sqr(dir.x) - sqr(dir.y)); + const vec3 I = vec3(0.0, 0.0, 1.0); + return reflect(I, dir); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Regular projections + * \{ */ + +vec3 camera_view_from_uv(mat4 projmat, vec2 uv) +{ + return project_point(projmat, vec3(uv * 2.0 - 1.0, 0.0)); +} + +vec2 camera_uv_from_view(mat4 projmat, bool is_persp, vec3 vV) +{ + vec4 tmp = projmat * vec4(vV, 1.0); + if (is_persp && tmp.w <= 0.0) { + /* Return invalid coordinates for points behind the camera. + * This can happen with panoramic projections. */ + return vec2(-1.0); + } + return (tmp.xy / tmp.w) * 0.5 + 0.5; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name General functions handling all projections + * \{ */ + +vec3 camera_view_from_uv(CameraData cam, vec2 uv) +{ + vec3 vV; + switch (cam.type) { + default: + case CAMERA_ORTHO: + case CAMERA_PERSP: + return camera_view_from_uv(cam.wininv, uv); + case CAMERA_PANO_EQUIRECT: + vV = camera_equirectangular_to_direction(cam, uv); + break; + case CAMERA_PANO_EQUIDISTANT: + /* ATTR_FALLTHROUGH; */ + case CAMERA_PANO_EQUISOLID: + vV = camera_fisheye_to_direction(cam, uv); + break; + case CAMERA_PANO_MIRROR: + vV = camera_mirror_ball_to_direction(cam, uv); + break; + } + return vV; +} + +vec2 camera_uv_from_view(CameraData cam, vec3 vV) +{ + switch (cam.type) { + default: + case CAMERA_ORTHO: + return camera_uv_from_view(cam.winmat, false, vV); + case CAMERA_PERSP: + return camera_uv_from_view(cam.winmat, true, vV); + case CAMERA_PANO_EQUIRECT: + return camera_equirectangular_from_direction(cam, vV); + case CAMERA_PANO_EQUISOLID: + /* ATTR_FALLTHROUGH; */ + case CAMERA_PANO_EQUIDISTANT: + return camera_fisheye_from_direction(cam, vV); + case CAMERA_PANO_MIRROR: + return camera_mirror_ball_from_direction(cam, vV); + } +} + +vec2 camera_uv_from_world(CameraData cam, vec3 V) +{ + vec3 vV = transform_point(cam.viewmat, V); + switch (cam.type) { + default: + case CAMERA_ORTHO: + return camera_uv_from_view(cam.persmat, false, V); + case CAMERA_PERSP: + return camera_uv_from_view(cam.persmat, true, V); + case CAMERA_PANO_EQUIRECT: + return camera_equirectangular_from_direction(cam, vV); + case CAMERA_PANO_EQUISOLID: + /* ATTR_FALLTHROUGH; */ + case CAMERA_PANO_EQUIDISTANT: + return camera_fisheye_from_direction(cam, vV); + case CAMERA_PANO_MIRROR: + return camera_mirror_ball_from_direction(cam, vV); + } +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl index 708bd153e84..87154ba6db1 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl @@ -5,6 +5,7 @@ #pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl) #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) void main() { @@ -27,6 +28,19 @@ void main() interp.N = cross(T, interp.curves_binormal); interp.curves_strand_id = hair_get_strand_id(); interp.barycentric_coords = hair_get_barycentric(); +#ifdef MAT_VELOCITY + /* Due to the screen space nature of the vertex positioning, we compute only the motion of curve + * strand, not its cylinder. Otherwise we would add the rotation velocity. */ + int vert_idx = hair_get_base_id(); + vec3 prv, nxt, pos = texelFetch(hairPointBuffer, vert_idx).point_position; + velocity_local_pos_get(pos, vert_idx, prv, nxt); + /* FIXME(fclem): Evaluating before displacement avoid displacement being treated as motion but + * ignores motion from animated displacement. Supporting animated displacement motion vectors + * would require evaluating the nodetree multiple time with different nodetree UBOs evaluated at + * different times, but also with different attributes (maybe we could assume static attribute at + * least). */ + velocity_vertex(prv, pos, nxt, motion.prev, motion.next); +#endif init_globals(); attrib_load(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl index 5b404ec5237..c60527162f7 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl @@ -3,6 +3,7 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) void main() { @@ -38,6 +39,16 @@ void main() aspect, thickness, hardness); +#ifdef MAT_VELOCITY + /* GPencil do not support deformation motion blur. */ + vec3 lP_curr = transform_point(ModelMatrixInverse, interp.P); + /* FIXME(fclem): Evaluating before displacement avoid displacement being treated as motion but + * ignores motion from animated displacement. Supporting animated displacement motion vectors + * would require evaluating the nodetree multiple time with different nodetree UBOs evaluated at + * different times, but also with different attributes (maybe we could assume static attribute at + * least). */ + velocity_vertex(lP_curr, lP_curr, lP_curr, motion.prev, motion.next); +#endif init_globals(); attrib_load(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl index 7b38057f41a..c07a8ae0eea 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl @@ -3,6 +3,7 @@ #pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl) #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) void main() { @@ -10,6 +11,16 @@ void main() interp.P = point_object_to_world(pos); interp.N = normal_object_to_world(nor); +#ifdef MAT_VELOCITY + vec3 prv, nxt; + velocity_local_pos_get(pos, gl_VertexID, prv, nxt); + /* FIXME(fclem): Evaluating before displacement avoid displacement being treated as motion but + * ignores motion from animated displacement. Supporting animated displacement motion vectors + * would require evaluating the nodetree multiple time with different nodetree UBOs evaluated at + * different times, but also with different attributes (maybe we could assume static attribute at + * least). */ + velocity_vertex(prv, pos, nxt, motion.prev, motion.next); +#endif init_globals(); attrib_load(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl index 002eed91130..7ddf941df7c 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl @@ -8,6 +8,7 @@ #pragma BLENDER_REQUIRE(common_hair_lib.glsl) #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) /* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire. */ float hash(vec2 a) @@ -69,4 +70,16 @@ void main() discard; } #endif + +#ifdef MAT_VELOCITY + vec4 out_velocity_camera; /* TODO(fclem): Panoramic cameras. */ + velocity_camera(interp.P + motion.prev, + interp.P, + interp.P - motion.next, + out_velocity_camera, + out_velocity_view); + + /* For testing in viewport. */ + out_velocity_view.zw = vec2(0.0); +#endif } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl new file mode 100644 index 00000000000..435ae6658c9 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl @@ -0,0 +1,101 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_camera_lib.glsl) + +#ifdef VELOCITY_CAMERA + +/** + * Given a triple of position, compute the previous and next motion vectors. + * Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy) + */ +vec4 velocity_view(vec3 P_prev, vec3 P, vec3 P_next) +{ + vec2 prev_uv, curr_uv, next_uv; + + prev_uv = transform_point(ProjectionMatrix, transform_point(camera_prev.viewmat, P_prev)).xy; + curr_uv = transform_point(ViewProjectionMatrix, P).xy; + next_uv = transform_point(ProjectionMatrix, transform_point(camera_next.viewmat, P_next)).xy; + + vec4 motion; + motion.xy = prev_uv - curr_uv; + motion.zw = curr_uv - next_uv; + /* Convert NDC velocity to UV velocity */ + motion *= 0.5; + + return motion; +} + +/** + * Given a triple of position, compute the previous and next motion vectors. + * Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy) + * \a velocity_camera is the motion in film UV space after camera projection. + * \a velocity_view is the motion in ShadingView UV space. It is different + * from velocity_camera for multi-view rendering. + */ +void velocity_camera(vec3 P_prev, vec3 P, vec3 P_next, out vec4 vel_camera, out vec4 vel_view) +{ + vec2 prev_uv, curr_uv, next_uv; + prev_uv = camera_uv_from_world(camera_prev, P_prev); + curr_uv = camera_uv_from_world(camera_curr, P); + next_uv = camera_uv_from_world(camera_next, P_next); + + vel_camera.xy = prev_uv - curr_uv; + vel_camera.zw = curr_uv - next_uv; + + if (is_panoramic(camera_curr.type)) { + /* This path is only used if using using panoramic projections. Since the views always have + * the same 45° aperture angle, we can safely reuse the projection matrix. */ + prev_uv = transform_point(ProjectionMatrix, transform_point(camera_prev.viewmat, P_prev)).xy; + curr_uv = transform_point(ViewProjectionMatrix, P).xy; + next_uv = transform_point(ProjectionMatrix, transform_point(camera_next.viewmat, P_next)).xy; + + vel_view.xy = prev_uv - curr_uv; + vel_view.zw = curr_uv - next_uv; + /* Convert NDC velocity to UV velocity */ + vel_view *= 0.5; + } + else { + vel_view = vel_camera; + } +} + +#endif + +#ifdef MAT_VELOCITY + +/** + * Given a triple of position, compute the previous and next motion vectors. + * Returns a tuple of world space motion deltas. + */ +void velocity_local_pos_get(vec3 lP, int vert_id, out vec3 lP_prev, out vec3 lP_next) +{ + VelocityIndex vel = velocity_indirection_buf[resource_id]; + lP_next = lP_prev = lP; + if (vel.geo.do_deform) { + if (vel.geo.ofs[STEP_PREVIOUS] != -1) { + lP_prev = velocity_geo_prev_buf[vel.geo.ofs[STEP_PREVIOUS] + vert_id].xyz; + } + if (vel.geo.ofs[STEP_NEXT] != -1) { + lP_next = velocity_geo_next_buf[vel.geo.ofs[STEP_NEXT] + vert_id].xyz; + } + } +} + +/** + * Given a triple of position, compute the previous and next motion vectors. + * Returns a tuple of world space motion deltas. + */ +void velocity_vertex( + vec3 lP_prev, vec3 lP, vec3 lP_next, out vec3 motion_prev, out vec3 motion_next) +{ + VelocityIndex vel = velocity_indirection_buf[resource_id]; + mat4 obmat_prev = velocity_obj_prev_buf[vel.obj.ofs[STEP_PREVIOUS]]; + mat4 obmat_next = velocity_obj_next_buf[vel.obj.ofs[STEP_NEXT]]; + vec3 P_prev = transform_point(obmat_prev, lP_prev); + vec3 P_next = transform_point(obmat_next, lP_next); + vec3 P = transform_point(ModelMatrix, lP); + motion_prev = P_prev - P; + motion_next = P_next - P; +} + +#endif diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl new file mode 100644 index 00000000000..b68b2eaf117 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl @@ -0,0 +1,58 @@ + +/** + * Fullscreen pass that compute motion vector for static geometry. + * Animated geometry has already written correct motion vectors. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) + +#define is_valid_output(img_) (imageSize(img_).x > 1) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + vec4 motion = imageLoad(velocity_view_img, texel); + + bool pixel_has_valid_motion = (motion.x != VELOCITY_INVALID); + float depth = texelFetch(depth_tx, texel, 0).r; + bool is_background = (depth == 1.0f); + + vec2 uv = vec2(texel) * drw_view.viewport_size_inverse; + vec3 P_next, P_prev, P_curr; + + if (pixel_has_valid_motion) { + /* Animated geometry. View motion already computed during prepass. Convert only to camera. */ + // P_prev = get_world_space_from_depth(uv + motion.xy, 0.5); + // P_curr = get_world_space_from_depth(uv, 0.5); + // P_next = get_world_space_from_depth(uv + motion.zw, 0.5); + return; + } + else if (is_background) { + /* NOTE: Use viewCameraVec to avoid imprecision if camera is far from origin. */ + vec3 vV = viewCameraVec(get_view_space_from_depth(uv, 1.0)); + vec3 V = transform_direction(ViewMatrixInverse, vV); + /* Background has no motion under camera translation. Translate view vector with the camera. */ + /* WATCH(fclem): Might create precision issues. */ + P_next = camera_next.viewinv[3].xyz + V; + P_curr = camera_curr.viewinv[3].xyz + V; + P_prev = camera_prev.viewinv[3].xyz + V; + } + else { + /* Static geometry. No translation in world space. */ + P_curr = get_world_space_from_depth(uv, depth); + P_prev = P_curr; + P_next = P_curr; + } + + vec4 vel_camera, vel_view; + velocity_camera(P_prev, P_curr, P_next, vel_camera, vel_view); + + if (in_texture_range(texel, depth_tx)) { + imageStore(velocity_view_img, texel, vel_view); + + if (is_valid_output(velocity_camera_img)) { + imageStore(velocity_camera_img, texel, vel_camera); + } + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index 4d6895bcde0..49250b5741e 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -22,6 +22,7 @@ GPU_SHADER_CREATE_INFO(eevee_sampling_data) * \{ */ GPU_SHADER_CREATE_INFO(eevee_geom_mesh) + .additional_info("eevee_shared") .define("MAT_GEOM_MESH") .vertex_in(0, Type::VEC3, "pos") .vertex_in(1, Type::VEC3, "nor") @@ -29,16 +30,19 @@ GPU_SHADER_CREATE_INFO(eevee_geom_mesh) .additional_info("draw_mesh", "draw_resource_id_varying", "draw_resource_handle"); GPU_SHADER_CREATE_INFO(eevee_geom_gpencil) + .additional_info("eevee_shared") .define("MAT_GEOM_GPENCIL") .vertex_source("eevee_geom_gpencil_vert.glsl") .additional_info("draw_gpencil", "draw_resource_id_varying", "draw_resource_handle"); GPU_SHADER_CREATE_INFO(eevee_geom_curves) + .additional_info("eevee_shared") .define("MAT_GEOM_CURVES") .vertex_source("eevee_geom_curves_vert.glsl") .additional_info("draw_hair", "draw_resource_id_varying", "draw_resource_handle"); GPU_SHADER_CREATE_INFO(eevee_geom_world) + .additional_info("eevee_shared") .define("MAT_GEOM_WORLD") .builtins(BuiltinBits::VERTEX_ID) .vertex_source("eevee_geom_world_vert.glsl") @@ -66,7 +70,7 @@ GPU_SHADER_INTERFACE_INFO(eevee_surf_iface, "interp") GPU_SHADER_CREATE_INFO(eevee_surf_deferred) .vertex_out(eevee_surf_iface) - /* Note: This removes the possibility of using gl_FragDepth. */ + /* NOTE: This removes the possibility of using gl_FragDepth. */ // .early_fragment_test(true) /* Direct output. */ .fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0) @@ -95,7 +99,7 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward) .fragment_source("eevee_surf_forward_frag.glsl") // .additional_info("eevee_sampling_data", // "eevee_lightprobe_data", - /* Optionnally added depending on the material. */ + /* Optionally added depending on the material. */ // "eevee_raytrace_data", // "eevee_transmittance_data", // "eevee_utility_texture", diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh new file mode 100644 index 00000000000..a5f16363466 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh @@ -0,0 +1,55 @@ + +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name Surface Velocity + * + * Combined with the depth prepass shader. + * Outputs the view motion vectors for animated objects. + * \{ */ + +/* Pass world space deltas to the fragment shader. + * This is to make sure that the resulting motion vectors are valid even with displacement. */ +GPU_SHADER_INTERFACE_INFO(eevee_velocity_surface_iface, "motion") + .smooth(Type::VEC3, "prev") + .smooth(Type::VEC3, "next"); + +GPU_SHADER_CREATE_INFO(eevee_velocity_camera) + .define("VELOCITY_CAMERA") + .uniform_buf(1, "CameraData", "camera_prev") + .uniform_buf(2, "CameraData", "camera_curr") + .uniform_buf(3, "CameraData", "camera_next"); + +GPU_SHADER_CREATE_INFO(eevee_velocity_geom) + .define("MAT_VELOCITY") + .auto_resource_location(true) + .storage_buf(4, Qualifier::READ, "mat4", "velocity_obj_prev_buf[]", Frequency::PASS) + .storage_buf(5, Qualifier::READ, "mat4", "velocity_obj_next_buf[]", Frequency::PASS) + .storage_buf(6, Qualifier::READ, "vec4", "velocity_geo_prev_buf[]", Frequency::PASS) + .storage_buf(7, Qualifier::READ, "vec4", "velocity_geo_next_buf[]", Frequency::PASS) + .storage_buf( + 7, Qualifier::READ, "VelocityIndex", "velocity_indirection_buf[]", Frequency::PASS) + .vertex_out(eevee_velocity_surface_iface) + .fragment_out(0, Type::VEC4, "out_velocity_view") + .additional_info("eevee_velocity_camera"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Velocity Resolve + * + * Computes velocity for static objects. + * Also converts motion to camera space (as opposed to view space) if needed. + * \{ */ + +GPU_SHADER_CREATE_INFO(eevee_velocity_resolve) + .do_static_compilation(true) + .local_group_size(8, 8) + .sampler(0, ImageType::DEPTH_2D, "depth_tx") + .image(0, GPU_RG16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "velocity_view_img") + .image(1, GPU_RG16F, Qualifier::WRITE, ImageType::FLOAT_2D, "velocity_camera_img") + .additional_info("eevee_shared") + .compute_source("eevee_velocity_resolve_comp.glsl") + .additional_info("draw_view", "eevee_velocity_camera"); + +/** \} */ diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index 40ebd262df5..ec44fdf42d5 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -290,7 +290,7 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, /* Masking: Go through mask list and extract valid masks in a bitmap. */ if (is_masked) { bool valid_mask = false; - /* Warning: only GP_MAX_MASKBITS amount of bits. + /* WARNING: only #GP_MAX_MASKBITS amount of bits. * TODO(fclem): Find a better system without any limitation. */ tgp_layer->mask_bits = BLI_memblock_alloc(pd->gp_maskbit_pool); tgp_layer->mask_invert_bits = BLI_memblock_alloc(pd->gp_maskbit_pool); diff --git a/source/blender/draw/engines/gpencil/gpencil_render.c b/source/blender/draw/engines/gpencil/gpencil_render.c index 19afdb3de5a..c7ef8677336 100644 --- a/source/blender/draw/engines/gpencil/gpencil_render.c +++ b/source/blender/draw/engines/gpencil/gpencil_render.c @@ -61,10 +61,10 @@ void GPENCIL_render_init(GPENCIL_Data *vedata, /* Depth need to be remapped to [0..1] range. */ pix_z = MEM_dupallocN(pix_z); - int pix_ct = rpass_z_src->rectx * rpass_z_src->recty; + int pix_num = rpass_z_src->rectx * rpass_z_src->recty; if (DRW_view_is_persp_get(view)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { pix_z[i] = (-winmat[3][2] / -pix_z[i]) - winmat[2][2]; pix_z[i] = clamp_f(pix_z[i] * 0.5f + 0.5f, 0.0f, 1.0f); } @@ -74,7 +74,7 @@ void GPENCIL_render_init(GPENCIL_Data *vedata, float near = DRW_view_near_distance_get(view); float far = DRW_view_far_distance_get(view); float range_inv = 1.0f / fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { pix_z[i] = (pix_z[i] + near) * range_inv; pix_z[i] = clamp_f(pix_z[i], 0.0f, 1.0f); } @@ -172,11 +172,11 @@ static void GPENCIL_render_result_z(struct RenderLayer *rl, float winmat[4][4]; DRW_view_winmat_get(NULL, winmat, false); - int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); /* Convert GPU depth [0..1] to view Z [near..far] */ if (DRW_view_is_persp_get(NULL)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } @@ -192,7 +192,7 @@ static void GPENCIL_render_result_z(struct RenderLayer *rl, float far = DRW_view_far_distance_get(NULL); float range = fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c index 5c5226bfe65..9531b0dd983 100644 --- a/source/blender/draw/engines/overlay/overlay_gpencil.c +++ b/source/blender/draw/engines/overlay/overlay_gpencil.c @@ -124,7 +124,7 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata) } } - /* Handles and curve point for Curve Edit submode. */ + /* Handles and curve point for Curve Edit sub-mode. */ if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) { DRWState state = DRW_STATE_WRITE_COLOR; DRW_PASS_CREATE(psl->edit_gpencil_curve_ps, state | pd->clipping_state); @@ -297,7 +297,7 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) } const int gridlines = (gpd->grid.lines <= 0) ? 1 : gpd->grid.lines; - int line_ct = gridlines * 4 + 2; + const int line_count = gridlines * 4 + 2; DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA; state |= (grid_xray) ? DRW_STATE_DEPTH_ALWAYS : DRW_STATE_DEPTH_LESS_EQUAL; @@ -311,8 +311,8 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) DRW_shgroup_uniform_vec3_copy(grp, "xAxis", mat[0]); DRW_shgroup_uniform_vec3_copy(grp, "yAxis", mat[1]); DRW_shgroup_uniform_vec3_copy(grp, "origin", mat[3]); - DRW_shgroup_uniform_int_copy(grp, "halfLineCount", line_ct / 2); - DRW_shgroup_call_procedural_lines(grp, NULL, line_ct); + DRW_shgroup_uniform_int_copy(grp, "halfLineCount", line_count / 2); + DRW_shgroup_call_procedural_lines(grp, NULL, line_count); } } diff --git a/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh b/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh index a765d881682..b9b1b73dbd4 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh @@ -284,7 +284,7 @@ GPU_SHADER_INTERFACE_INFO(overlay_particle_iface, "").flat(Type::VEC4, "finalCol GPU_SHADER_CREATE_INFO(overlay_particle) .sampler(0, ImageType::FLOAT_1D, "weightTex") - .push_constant(Type::VEC4, "color") /* Drawsize packed in alpha */ + .push_constant(Type::VEC4, "color") /* Draw-size packed in alpha. */ .vertex_in(0, Type::VEC3, "part_pos") .vertex_in(1, Type::VEC4, "part_rot") .vertex_in(2, Type::FLOAT, "part_val") diff --git a/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh b/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh index 713c8c2dc4b..5853e974eeb 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh @@ -48,7 +48,7 @@ GPU_SHADER_CREATE_INFO(overlay_volume_velocity_needle) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Volume Gridlines +/** \name Volume Grid-Lines * \{ */ GPU_SHADER_INTERFACE_INFO(overlay_volume_gridlines_iface, "").flat(Type::VEC4, "finalColor"); diff --git a/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh b/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh index 43367121d6a..16b59f6bb7d 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh @@ -21,7 +21,7 @@ GPU_SHADER_CREATE_INFO(overlay_wireframe) .sampler(0, ImageType::DEPTH_2D, "depthTex") .vertex_in(0, Type::VEC3, "pos") .vertex_in(1, Type::VEC3, "nor") - .vertex_in(2, Type::FLOAT, "wd") /* wiredata */ + .vertex_in(2, Type::FLOAT, "wd") /* wire-data. */ .vertex_out(overlay_wireframe_iface) .vertex_source("wireframe_vert.glsl") .fragment_source("wireframe_frag.glsl") diff --git a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl index c33bb474d96..3e3c0e4e89f 100644 --- a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl @@ -347,7 +347,7 @@ void main() line_end = vec2(0.0, 0.5); break; default: - /* Ensure values are assigned to, avoids undefined behaviour for + /* Ensure values are assigned to, avoids undefined behavior for * divergent control-flow. This can occur if discard is called * as discard is not treated as a return in Metal 2.2. So * side-effects can still cause problems. */ diff --git a/source/blender/draw/engines/select/shaders/infos/select_id_info.hh b/source/blender/draw/engines/select/shaders/infos/select_id_info.hh index ad0de61ffc3..e3166582197 100644 --- a/source/blender/draw/engines/select/shaders/infos/select_id_info.hh +++ b/source/blender/draw/engines/select/shaders/infos/select_id_info.hh @@ -3,7 +3,7 @@ #include "gpu_shader_create_info.hh" /* -------------------------------------------------------------------- */ -/** \name Select ID fo Edit Mesh selection +/** \name Select ID for Edit Mesh Selection * \{ */ GPU_SHADER_INTERFACE_INFO(select_id_iface, "").flat(Type::INT, "id"); diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh index e2a54788c72..735e7b6d867 100644 --- a/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh @@ -91,7 +91,7 @@ GPU_SHADER_CREATE_INFO(workbench_material) * \{ */ GPU_SHADER_CREATE_INFO(workbench_transparent_accum) - /* Note: Blending will be skipped on objectId because output is a + /* NOTE: Blending will be skipped on objectId because output is a * non-normalized integer buffer. */ .fragment_out(0, Type::VEC4, "transparentAccum") .fragment_out(1, Type::VEC4, "revealageAccum") diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl index 36059b6076f..49e26cd3e0c 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl @@ -218,13 +218,13 @@ void main() /* Manual depth test. TODO: remove. */ float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; if (gl_FragCoord.z >= depth) { - /* Note: In the Metal API, prior to Metal 2.3, Discard is not an explicit return and can - * produce undefined behaviour. This is especially prominent with derivatives if control-flow + /* NOTE: In the Metal API, prior to Metal 2.3, Discard is not an explicit return and can + * produce undefined behavior. This is especially prominent with derivatives if control-flow * divergence is present. * - * Adding a return call eliminates undefined behaviour and a later out-of-bounds read causing + * Adding a return call eliminates undefined behavior and a later out-of-bounds read causing * a crash on AMD platforms. - * This behaviour can also affect OpenGL on certain devices. */ + * This behavior can also affect OpenGL on certain devices. */ discard; return; } diff --git a/source/blender/draw/engines/workbench/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c index 1279682e899..e5dcf6c5624 100644 --- a/source/blender/draw/engines/workbench/workbench_render.c +++ b/source/blender/draw/engines/workbench/workbench_render.c @@ -115,11 +115,11 @@ static void workbench_render_result_z(struct RenderLayer *rl, float winmat[4][4]; DRW_view_winmat_get(NULL, winmat, false); - int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); /* Convert ogl depth [0..1] to view Z [near..far] */ if (DRW_view_is_persp_get(NULL)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } @@ -135,7 +135,7 @@ static void workbench_render_result_z(struct RenderLayer *rl, float far = DRW_view_far_distance_get(NULL); float range = fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c index 2c902e9b627..ce7773e7439 100644 --- a/source/blender/draw/engines/workbench/workbench_volume.c +++ b/source/blender/draw/engines/workbench/workbench_volume.c @@ -124,12 +124,12 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata, double noise_ofs; BLI_halton_1d(3, 0.0, wpd->taa_sample, &noise_ofs); float dim[3], step_length, max_slice; - float slice_ct[3] = {fds->res[0], fds->res[1], fds->res[2]}; - mul_v3_fl(slice_ct, max_ff(0.001f, fds->slice_per_voxel)); - max_slice = max_fff(slice_ct[0], slice_ct[1], slice_ct[2]); + float slice_count[3] = {fds->res[0], fds->res[1], fds->res[2]}; + mul_v3_fl(slice_count, max_ff(0.001f, fds->slice_per_voxel)); + max_slice = max_fff(slice_count[0], slice_count[1], slice_count[2]); BKE_object_dimensions_get(ob, dim); - invert_v3(slice_ct); - mul_v3_v3(dim, slice_ct); + invert_v3(slice_count); + mul_v3_v3(dim, slice_count); step_length = len_v3(dim); grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); @@ -273,12 +273,12 @@ static void workbench_volume_object_cache_populate(WORKBENCH_Data *vedata, float step_length, max_slice; int resolution[3]; GPU_texture_get_mipmap_size(grid->texture, 0, resolution); - float slice_ct[3] = {resolution[0], resolution[1], resolution[2]}; - mul_v3_fl(slice_ct, max_ff(0.001f, 5.0f)); - max_slice = max_fff(slice_ct[0], slice_ct[1], slice_ct[2]); - invert_v3(slice_ct); - mul_v3_v3(slice_ct, world_size); - step_length = len_v3(slice_ct); + float slice_count[3] = {resolution[0], resolution[1], resolution[2]}; + mul_v3_fl(slice_count, max_ff(0.001f, 5.0f)); + max_slice = max_fff(slice_count[0], slice_count[1], slice_count[2]); + invert_v3(slice_count); + mul_v3_v3(slice_count, world_size); + step_length = len_v3(slice_count); /* Set uniforms. */ grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index d7e752a43f4..257f01a5562 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -102,7 +102,7 @@ class DataBuffer { { BLI_STATIC_ASSERT(!device_only, ""); BLI_assert(index >= 0); - BLI_assert(index < len); + BLI_assert(index < len_); return data_[index]; } @@ -110,7 +110,7 @@ class DataBuffer { { BLI_STATIC_ASSERT(!device_only, ""); BLI_assert(index >= 0); - BLI_assert(index < len); + BLI_assert(index < len_); return data_[index]; } @@ -139,7 +139,7 @@ class DataBuffer { const T *end() const { BLI_STATIC_ASSERT(!device_only, ""); - return data_ + len; + return data_ + len_; } T *begin() @@ -150,13 +150,13 @@ class DataBuffer { T *end() { BLI_STATIC_ASSERT(!device_only, ""); - return data_ + len; + return data_ + len_; } operator Span<T>() const { BLI_STATIC_ASSERT(!device_only, ""); - return Span<T>(data_, len); + return Span<T>(data_, len_); } }; @@ -217,7 +217,9 @@ class StorageCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable if (name) { name_ = name; } - init(len); + this->len_ = len; + constexpr GPUUsageType usage = device_only ? GPU_USAGE_DEVICE_ONLY : GPU_USAGE_DYNAMIC; + ssbo_ = GPU_storagebuf_create_ex(sizeof(T) * this->len_, nullptr, usage, this->name_); } ~StorageCommon() @@ -225,15 +227,6 @@ class StorageCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable GPU_storagebuf_free(ssbo_); } - void resize(int64_t new_size) - { - BLI_assert(new_size > 0); - if (new_size != this->len_) { - GPU_storagebuf_free(ssbo_); - this->init(new_size); - } - } - void push_update(void) { BLI_assert(device_only == false); @@ -249,14 +242,6 @@ class StorageCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable { return &ssbo_; } - - private: - void init(int64_t new_size) - { - this->len_ = new_size; - GPUUsageType usage = device_only ? GPU_USAGE_DEVICE_ONLY : GPU_USAGE_DYNAMIC; - ssbo_ = GPU_storagebuf_create_ex(sizeof(T) * this->len_, nullptr, usage, this->name_); - } }; } // namespace detail @@ -333,6 +318,34 @@ class StorageArrayBuffer : public detail::StorageCommon<T, len, device_only> { { MEM_freeN(this->data_); } + + void resize(int64_t new_size) + { + BLI_assert(new_size > 0); + if (new_size != this->len_) { + /* Manual realloc since MEM_reallocN_aligned does not exists. */ + T *new_data_ = (T *)MEM_mallocN_aligned(new_size * sizeof(T), 16, this->name_); + memcpy(new_data_, this->data_, min_uu(this->len_, new_size) * sizeof(T)); + MEM_freeN(this->data_); + this->data_ = new_data_; + GPU_storagebuf_free(this->ssbo_); + + this->len_ = new_size; + constexpr GPUUsageType usage = device_only ? GPU_USAGE_DEVICE_ONLY : GPU_USAGE_DYNAMIC; + this->ssbo_ = GPU_storagebuf_create_ex(sizeof(T) * this->len_, nullptr, usage, this->name_); + } + } + + /* Resize on access. */ + T &get_or_resize(int64_t index) + { + BLI_assert(index >= 0); + if (index >= this->len_) { + size_t size = power_of_2_max_u(index + 1); + this->resize(size); + } + return this->data_[index]; + } }; template< @@ -880,4 +893,56 @@ class Framebuffer : NonCopyable { /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Double & Triple buffering util + * + * This is not strictly related to a GPU type and could be moved elsewhere. + * \{ */ + +template<typename T, int64_t len> class SwapChain { + private: + std::array<T, len> chain_; + int64_t index_ = 0; + + public: + void swap() + { + index_ = (index_ + 1) % len; + } + + T ¤t() + { + return chain_[index_]; + } + + T &previous() + { + /* Avoid modulo operation with negative numbers. */ + return chain_[(index_ + len - 1) % len]; + } + + T &next() + { + return chain_[(index_ + 1) % len]; + } + + const T ¤t() const + { + return chain_[index_]; + } + + const T &previous() const + { + /* Avoid modulo operation with negative numbers. */ + return chain_[(index_ + len - 1) % len]; + } + + const T &next() const + { + return chain_[(index_ + 1) % len]; + } +}; + +/** \} */ + } // namespace blender::draw diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 712118e8282..07105757b2c 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -296,6 +296,8 @@ void DRW_shader_library_free(DRWShaderLibrary *lib); * therefore they aren't ordered as a bit mask. */ typedef enum { + /** To be used for compute passes. */ + DRW_STATE_NO_DRAW = 0, /** Write mask */ DRW_STATE_WRITE_DEPTH = (1 << 0), DRW_STATE_WRITE_COLOR = (1 << 1), @@ -430,12 +432,12 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup, DRW_shgroup_call_ex(shgroup, ob, NULL, geom, true, NULL) void DRW_shgroup_call_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_ct); + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_num); /** * A count of 0 instance will use the default number of instance in the batch. */ void DRW_shgroup_call_instance_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct); + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_num); void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, int groups_x_len, @@ -621,6 +623,12 @@ void DRW_shgroup_vertex_buffer_ex(DRWShadingGroup *shgroup, void DRW_shgroup_vertex_buffer_ref_ex(DRWShadingGroup *shgroup, const char *name, struct GPUVertBuf **vertex_buffer DRW_DEBUG_FILE_LINE_ARGS); +void DRW_shgroup_buffer_texture(DRWShadingGroup *shgroup, + const char *name, + struct GPUVertBuf *vertex_buffer); +void DRW_shgroup_buffer_texture_ref(DRWShadingGroup *shgroup, + const char *name, + struct GPUVertBuf **vertex_buffer); #ifdef DRW_UNUSED_RESOURCE_TRACKING # define DRW_shgroup_vertex_buffer(shgroup, name, vert) \ @@ -738,6 +746,7 @@ const DRWView *DRW_view_get_active(void); */ void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len); void DRW_view_camtexco_set(DRWView *view, float texco[4]); +void DRW_view_camtexco_get(const DRWView *view, float r_texco[4]); /* For all getters, if view is NULL, default view is assumed. */ diff --git a/source/blender/draw/intern/draw_attributes.cc b/source/blender/draw/intern/draw_attributes.cc new file mode 100644 index 00000000000..714f1dbb3d1 --- /dev/null +++ b/source/blender/draw/intern/draw_attributes.cc @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +#include "draw_attributes.h" + +/* Return true if the given DRW_AttributeRequest is already in the requests. */ +static bool drw_attributes_has_request(const DRW_Attributes *requests, DRW_AttributeRequest req) +{ + for (int i = 0; i < requests->num_requests; i++) { + const DRW_AttributeRequest src_req = requests->requests[i]; + if (src_req.domain != req.domain) { + continue; + } + if (src_req.layer_index != req.layer_index) { + continue; + } + if (src_req.cd_type != req.cd_type) { + continue; + } + return true; + } + return false; +} + +static void drw_attributes_merge_requests(const DRW_Attributes *src_requests, + DRW_Attributes *dst_requests) +{ + for (int i = 0; i < src_requests->num_requests; i++) { + if (dst_requests->num_requests == GPU_MAX_ATTR) { + return; + } + + if (drw_attributes_has_request(dst_requests, src_requests->requests[i])) { + continue; + } + + dst_requests->requests[dst_requests->num_requests] = src_requests->requests[i]; + dst_requests->num_requests += 1; + } +} + +void drw_attributes_clear(DRW_Attributes *attributes) +{ + memset(attributes, 0, sizeof(DRW_Attributes)); +} + +void drw_attributes_merge(DRW_Attributes *dst, + const DRW_Attributes *src, + ThreadMutex *render_mutex) +{ + BLI_mutex_lock(render_mutex); + drw_attributes_merge_requests(src, dst); + BLI_mutex_unlock(render_mutex); +} + +bool drw_attributes_overlap(const DRW_Attributes *a, const DRW_Attributes *b) +{ + for (int i = 0; i < b->num_requests; i++) { + if (!drw_attributes_has_request(a, b->requests[i])) { + return false; + } + } + + return true; +} + +DRW_AttributeRequest *drw_attributes_add_request(DRW_Attributes *attrs, + CustomDataType type, + int layer, + AttributeDomain domain) +{ + if (attrs->num_requests >= GPU_MAX_ATTR) { + return nullptr; + } + + DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests]; + req->cd_type = type; + req->layer_index = layer; + req->domain = domain; + attrs->num_requests += 1; + return req; +} + +bool drw_custom_data_match_attribute(const CustomData *custom_data, + const char *name, + int *r_layer_index, + int *r_type) +{ + const int possible_attribute_types[7] = { + CD_PROP_BOOL, + CD_PROP_INT8, + CD_PROP_INT32, + CD_PROP_FLOAT, + CD_PROP_FLOAT2, + CD_PROP_FLOAT3, + CD_PROP_COLOR, + }; + + for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) { + const int attr_type = possible_attribute_types[i]; + int layer_index = CustomData_get_named_layer(custom_data, attr_type, name); + if (layer_index == -1) { + continue; + } + + *r_layer_index = layer_index; + *r_type = attr_type; + return true; + } + + return false; +} diff --git a/source/blender/draw/intern/draw_attributes.h b/source/blender/draw/intern/draw_attributes.h new file mode 100644 index 00000000000..192ffa43337 --- /dev/null +++ b/source/blender/draw/intern/draw_attributes.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup draw + * + * \brief Utilities for rendering attributes. + */ + +#pragma once + +#include "DNA_customdata_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute.h" + +#include "BLI_sys_types.h" +#include "BLI_threads.h" + +#include "GPU_shader.h" +#include "GPU_vertex_format.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DRW_AttributeRequest { + CustomDataType cd_type; + int layer_index; + AttributeDomain domain; + char attribute_name[64]; +} DRW_AttributeRequest; + +typedef struct DRW_Attributes { + DRW_AttributeRequest requests[GPU_MAX_ATTR]; + int num_requests; +} DRW_Attributes; + +void drw_attributes_clear(DRW_Attributes *attributes); + +void drw_attributes_merge(DRW_Attributes *dst, + const DRW_Attributes *src, + ThreadMutex *render_mutex); + +/* Return true if all requests in b are in a. */ +bool drw_attributes_overlap(const DRW_Attributes *a, const DRW_Attributes *b); + +DRW_AttributeRequest *drw_attributes_add_request(DRW_Attributes *attrs, + CustomDataType type, + int layer, + AttributeDomain domain); + +bool drw_custom_data_match_attribute(const CustomData *custom_data, + const char *name, + int *r_layer_index, + int *r_type); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index 67700a4274f..fb074cc728e 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -3400,6 +3400,9 @@ void DRW_batch_cache_free_old(Object *ob, int ctime) case OB_MESH: DRW_mesh_batch_cache_free_old((Mesh *)ob->data, ctime); break; + case OB_CURVES: + DRW_curves_batch_cache_free_old((Curves *)ob->data, ctime); + break; /* TODO: all cases. */ default: break; diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index cb6006e303a..ce3ad9923da 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -20,6 +20,8 @@ struct TaskGraph; #include "GPU_index_buffer.h" #include "GPU_vertex_buffer.h" +#include "draw_attributes.h" + /* Vertex Group Selection and display options */ typedef struct DRW_MeshWeightState { int defgroup_active; @@ -67,17 +69,6 @@ typedef enum eMRIterType { } eMRIterType; ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT) -typedef struct DRW_AttributeRequest { - CustomDataType cd_type; - int layer_index; - AttributeDomain domain; -} DRW_AttributeRequest; - -typedef struct DRW_MeshAttributes { - DRW_AttributeRequest requests[GPU_MAX_ATTR]; - int num_requests; -} DRW_MeshAttributes; - typedef enum eMRDataType { MR_DATA_NONE = 0, MR_DATA_POLY_NOR = 1 << 1, @@ -294,7 +285,7 @@ typedef struct MeshBatchCache { DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time; - DRW_MeshAttributes attr_used, attr_needed, attr_used_over_time; + DRW_Attributes attr_used, attr_needed, attr_used_over_time; int lastmatch; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index ec544d8e786..3d44d3d1b3f 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -793,7 +793,12 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache, /* The order in which extractors are added to the list matters somewhat, as some buffers are * reused when building others. */ EXTRACT_ADD_REQUESTED(ibo, tris); - EXTRACT_ADD_REQUESTED(vbo, pos_nor); + + /* Orcos are extracted at the same time as positions. */ + if (DRW_vbo_requested(mbuflist->vbo.pos_nor) || DRW_vbo_requested(mbuflist->vbo.orco)) { + extractors.append(&extract_pos_nor); + } + EXTRACT_ADD_REQUESTED(vbo, lnor); for (int i = 0; i < GPU_MAX_ATTR; i++) { EXTRACT_ADD_REQUESTED(vbo, attr[i]); @@ -843,7 +848,6 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache, EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle); EXTRACT_ADD_REQUESTED(ibo, lines_paint_mask); EXTRACT_ADD_REQUESTED(ibo, lines_adjacency); - EXTRACT_ADD_REQUESTED(vbo, orco); EXTRACT_ADD_REQUESTED(vbo, vcol); EXTRACT_ADD_REQUESTED(vbo, weights); EXTRACT_ADD_REQUESTED(vbo, sculpt_data); diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index f877c94208f..0755d5967d5 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -83,6 +83,7 @@ void DRW_batch_cache_free_old(struct Object *ob, int ctime); * \note For now this only free the shading batches / VBO if any cd layers is not needed anymore. */ void DRW_mesh_batch_cache_free_old(struct Mesh *me, int ctime); +void DRW_curves_batch_cache_free_old(struct Curves *curves, int ctime); /** \} */ diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 7b8f34b999c..ebcdabe4942 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -108,7 +108,7 @@ static void curve_eval_render_wire_verts_edges_len_get(const blender::bke::Curve const blender::VArray<bool> cyclic = curves.cyclic(); for (const int i : curves.curves_range()) { const IndexRange points = curves.evaluated_points_for_curve(i); - *r_edge_len += blender::bke::curves::curve_segment_size(points.size(), cyclic[i]); + *r_edge_len += blender::bke::curves::curve_segment_num(points.size(), cyclic[i]); } } diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index f2742f3bcc7..f9cf0021fcd 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -24,6 +24,7 @@ #include "DNA_scene_types.h" #include "BKE_curves.hh" +#include "BKE_geometry_set.hh" #include "GPU_batch.h" #include "GPU_material.h" @@ -31,12 +32,16 @@ #include "DRW_render.h" +#include "draw_attributes.h" #include "draw_cache_impl.h" /* own include */ #include "draw_cache_inline.h" #include "draw_curves_private.h" /* own include */ +#include "draw_shader.h" +using blender::ColorGeometry4f; using blender::float3; using blender::IndexRange; +using blender::MutableSpan; using blender::Span; /* ---------------------------------------------------------------------- */ @@ -49,6 +54,10 @@ struct CurvesBatchCache { /* To determine if cache is invalid. */ bool is_dirty; + + /** Needed when updating material data (e.g. attributes) as the same curves might be used for + * multiple objects with different materials. */ + ThreadMutex render_mutex; }; static bool curves_batch_cache_valid(const Curves &curves) @@ -63,6 +72,7 @@ static void curves_batch_cache_init(Curves &curves) if (!cache) { cache = MEM_cnew<CurvesBatchCache>(__func__); + BLI_mutex_init(&cache->render_mutex); curves.batch_cache = cache; } else { @@ -72,6 +82,23 @@ static void curves_batch_cache_init(Curves &curves) cache->is_dirty = false; } +static void curves_discard_attributes(CurvesEvalCache &curves_cache) +{ + for (int i = 0; i < GPU_MAX_ATTR; i++) { + GPU_VERTBUF_DISCARD_SAFE(curves_cache.proc_attributes_buf[i]); + DRW_TEXTURE_FREE_SAFE(curves_cache.proc_attributes_tex[i]); + } + + for (int i = 0; i < MAX_HAIR_SUBDIV; i++) { + for (int j = 0; j < GPU_MAX_ATTR; j++) { + GPU_VERTBUF_DISCARD_SAFE(curves_cache.final[i].attributes_buf[j]); + DRW_TEXTURE_FREE_SAFE(curves_cache.final[i].attributes_tex[j]); + } + + drw_attributes_clear(&curves_cache.final[i].attr_used); + } +} + static void curves_batch_cache_clear_data(CurvesEvalCache &curves_cache) { /* TODO: more granular update tagging. */ @@ -92,6 +119,8 @@ static void curves_batch_cache_clear_data(CurvesEvalCache &curves_cache) GPU_BATCH_DISCARD_SAFE(curves_cache.final[i].proc_hairs[j]); } } + + curves_discard_attributes(curves_cache); } static void curves_batch_cache_clear(Curves &curves) @@ -138,55 +167,89 @@ void DRW_curves_batch_cache_dirty_tag(Curves *curves, int mode) void DRW_curves_batch_cache_free(Curves *curves) { curves_batch_cache_clear(*curves); + CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves->batch_cache); + BLI_mutex_end(&cache->render_mutex); MEM_SAFE_FREE(curves->batch_cache); } +void DRW_curves_batch_cache_free_old(Curves *curves, int ctime) +{ + CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves->batch_cache); + if (cache == nullptr) { + return; + } + + bool do_discard = false; + + for (int i = 0; i < MAX_HAIR_SUBDIV; i++) { + CurvesEvalFinalCache &final_cache = cache->curves_cache.final[i]; + + if (drw_attributes_overlap(&final_cache.attr_used_over_time, &final_cache.attr_used)) { + final_cache.last_attr_matching_time = ctime; + } + + if (ctime - final_cache.last_attr_matching_time > U.vbotimeout) { + do_discard = true; + } + + drw_attributes_clear(&final_cache.attr_used_over_time); + } + + if (do_discard) { + curves_discard_attributes(cache->curves_cache); + } +} + static void ensure_seg_pt_count(const Curves &curves, CurvesEvalCache &curves_cache) { if (curves_cache.proc_point_buf != nullptr) { return; } - curves_cache.strands_len = curves.geometry.curve_size; - curves_cache.elems_len = curves.geometry.point_size + curves.geometry.curve_size; - curves_cache.point_len = curves.geometry.point_size; + curves_cache.strands_len = curves.geometry.curve_num; + curves_cache.elems_len = curves.geometry.point_num + curves.geometry.curve_num; + curves_cache.point_len = curves.geometry.point_num; } -static void curves_batch_cache_fill_segments_proc_pos(const Curves &curves_id, - GPUVertBufRaw &attr_step, - GPUVertBufRaw &length_step) +struct PositionAndParameter { + float3 position; + float parameter; +}; + +static void curves_batch_cache_fill_segments_proc_pos( + const Curves &curves_id, + MutableSpan<PositionAndParameter> posTime_data, + MutableSpan<float> hairLength_data) { /* TODO: use hair radius layer if available. */ - const int curve_size = curves_id.geometry.curve_size; + const int curve_num = curves_id.geometry.curve_num; const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( curves_id.geometry); Span<float3> positions = curves.positions(); - for (const int i : IndexRange(curve_size)) { - const IndexRange curve_range = curves.points_for_curve(i); + for (const int i_curve : IndexRange(curve_num)) { + const IndexRange points = curves.points_for_curve(i_curve); + + Span<float3> curve_positions = positions.slice(points); + MutableSpan<PositionAndParameter> curve_posTime_data = posTime_data.slice(points); - Span<float3> curve_positions = positions.slice(curve_range); float total_len = 0.0f; - float *seg_data_first; - for (const int i_curve : curve_positions.index_range()) { - float *seg_data = (float *)GPU_vertbuf_raw_step(&attr_step); - copy_v3_v3(seg_data, curve_positions[i_curve]); - if (i_curve == 0) { - seg_data_first = seg_data; - } - else { - total_len += blender::math::distance(curve_positions[i_curve - 1], - curve_positions[i_curve]); + for (const int i_point : curve_positions.index_range()) { + if (i_point > 0) { + total_len += blender::math::distance(curve_positions[i_point - 1], + curve_positions[i_point]); } - seg_data[3] = total_len; + curve_posTime_data[i_point].position = curve_positions[i_point]; + curve_posTime_data[i_point].parameter = total_len; } + hairLength_data[i_curve] = total_len; + /* Assign length value. */ - *(float *)GPU_vertbuf_raw_step(&length_step) = total_len; if (total_len > 0.0f) { + const float factor = 1.0f / total_len; /* Divide by total length to have a [0-1] number. */ - for ([[maybe_unused]] const int i_curve : curve_positions.index_range()) { - seg_data_first[3] /= total_len; - seg_data_first += 4; + for (const int i_point : curve_positions.index_range()) { + curve_posTime_data[i_point].parameter *= factor; } } } @@ -199,26 +262,26 @@ static void curves_batch_cache_ensure_procedural_pos(Curves &curves, if (cache.proc_point_buf == nullptr || DRW_vbo_requested(cache.proc_point_buf)) { /* Initialize vertex format. */ GPUVertFormat format = {0}; - uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); GPU_vertformat_alias_add(&format, "pos"); cache.proc_point_buf = GPU_vertbuf_create_with_format(&format); GPU_vertbuf_data_alloc(cache.proc_point_buf, cache.point_len); - GPUVertBufRaw point_step; - GPU_vertbuf_attr_get_raw_data(cache.proc_point_buf, pos_id, &point_step); + MutableSpan posTime_data{ + reinterpret_cast<PositionAndParameter *>(GPU_vertbuf_get_data(cache.proc_point_buf)), + cache.point_len}; GPUVertFormat length_format = {0}; - uint length_id = GPU_vertformat_attr_add( - &length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); cache.proc_length_buf = GPU_vertbuf_create_with_format(&length_format); GPU_vertbuf_data_alloc(cache.proc_length_buf, cache.strands_len); - GPUVertBufRaw length_step; - GPU_vertbuf_attr_get_raw_data(cache.proc_length_buf, length_id, &length_step); + MutableSpan hairLength_data{ + reinterpret_cast<float *>(GPU_vertbuf_get_data(cache.proc_length_buf)), cache.strands_len}; - curves_batch_cache_fill_segments_proc_pos(curves, point_step, length_step); + curves_batch_cache_fill_segments_proc_pos(curves, posTime_data, hairLength_data); /* Create vbo immediately to bind to texture buffer. */ GPU_vertbuf_use(cache.proc_point_buf); @@ -237,6 +300,88 @@ static void curves_batch_cache_ensure_procedural_pos(Curves &curves, } } +void drw_curves_get_attribute_sampler_name(const char *layer_name, char r_sampler_name[32]) +{ + char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + /* Attributes use auto-name. */ + BLI_snprintf(r_sampler_name, 32, "a%s", attr_safe_name); +} + +static void curves_batch_cache_ensure_procedural_final_attr( + CurvesEvalCache &cache, GPUVertFormat *format, int subdiv, int index, const char *name) +{ + CurvesEvalFinalCache &final_cache = cache.final[subdiv]; + final_cache.attributes_buf[index] = GPU_vertbuf_create_with_format_ex(format, + GPU_USAGE_DEVICE_ONLY); + + /* Create a destination buffer for the transform feedback. Sized appropriately */ + /* Those are points! not line segments. */ + GPU_vertbuf_data_alloc(final_cache.attributes_buf[index], + final_cache.strands_res * cache.strands_len); + + /* Create vbo immediately to bind to texture buffer. */ + GPU_vertbuf_use(final_cache.attributes_buf[index]); + + final_cache.attributes_tex[index] = GPU_texture_create_from_vertbuf( + name, final_cache.attributes_buf[index]); +} + +static void curves_batch_ensure_attribute(const Curves &curves, + CurvesEvalCache &cache, + const DRW_AttributeRequest &request, + int subdiv, + int index) +{ + GPU_VERTBUF_DISCARD_SAFE(cache.proc_attributes_buf[index]); + DRW_TEXTURE_FREE_SAFE(cache.proc_attributes_tex[index]); + + char sampler_name[32]; + drw_curves_get_attribute_sampler_name(request.attribute_name, sampler_name); + + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + /* All attributes use vec4, see comment below. */ + GPU_vertformat_attr_add(&format, sampler_name, GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + cache.proc_attributes_buf[index] = GPU_vertbuf_create_with_format(&format); + GPUVertBuf *attr_vbo = cache.proc_attributes_buf[index]; + + GPU_vertbuf_data_alloc(attr_vbo, + request.domain == ATTR_DOMAIN_POINT ? curves.geometry.point_num : + curves.geometry.curve_num); + + CurveComponent component; + component.replace(const_cast<Curves *>(&curves), GeometryOwnershipType::ReadOnly); + + /* TODO(@kevindietrich): float4 is used for scalar attributes as the implicit conversion done + * by OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following + * the Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a + * similar texture state swizzle to map the attribute correctly as for volume attributes, so we + * can control the conversion ourselves. */ + blender::VArray<ColorGeometry4f> attribute = component.attribute_get_for_read<ColorGeometry4f>( + request.attribute_name, request.domain, {0.0f, 0.0f, 0.0f, 1.0f}); + + MutableSpan<ColorGeometry4f> vbo_span{ + static_cast<ColorGeometry4f *>(GPU_vertbuf_get_data(attr_vbo)), + component.attribute_domain_num(request.domain)}; + + attribute.materialize(vbo_span); + + GPU_vertbuf_use(attr_vbo); + cache.proc_attributes_tex[index] = GPU_texture_create_from_vertbuf(sampler_name, attr_vbo); + + /* Existing final data may have been for a different attribute (with a different name or domain), + * free the data. */ + GPU_VERTBUF_DISCARD_SAFE(cache.final[subdiv].attributes_buf[index]); + DRW_TEXTURE_FREE_SAFE(cache.final[subdiv].attributes_tex[index]); + + /* Ensure final data for points. */ + if (request.domain == ATTR_DOMAIN_POINT) { + curves_batch_cache_ensure_procedural_final_attr(cache, &format, subdiv, index, sampler_name); + } +} + static void curves_batch_cache_fill_strands_data(const Curves &curves_id, GPUVertBufRaw &data_step, GPUVertBufRaw &seg_step) @@ -307,7 +452,7 @@ static void curves_batch_cache_fill_segments_indices(const Curves &curves, const int res, GPUIndexBufBuilder &elb) { - const int curves_num = curves.geometry.curve_size; + const int curves_num = curves.geometry.curve_num; uint curr_point = 0; @@ -353,6 +498,88 @@ static void curves_batch_cache_ensure_procedural_indices(Curves &curves, prim_type, vbo, GPU_indexbuf_build(&elb), GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX); } +static bool curves_ensure_attributes(const Curves &curves, + CurvesBatchCache &cache, + GPUMaterial *gpu_material, + int subdiv) +{ + ThreadMutex *render_mutex = &cache.render_mutex; + const CustomData *cd_curve = &curves.geometry.curve_data; + const CustomData *cd_point = &curves.geometry.point_data; + + DRW_Attributes attrs_needed; + drw_attributes_clear(&attrs_needed); + ListBase gpu_attrs = GPU_material_attributes(gpu_material); + LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) { + const char *name = gpu_attr->name; + int type = gpu_attr->type; + int layer = -1; + AttributeDomain domain; + + if (drw_custom_data_match_attribute(cd_curve, name, &layer, &type)) { + domain = ATTR_DOMAIN_CURVE; + } + else if (drw_custom_data_match_attribute(cd_point, name, &layer, &type)) { + domain = ATTR_DOMAIN_POINT; + } + else { + continue; + } + + switch (type) { + default: + break; + case CD_PROP_BOOL: + case CD_PROP_INT8: + case CD_PROP_INT32: + case CD_PROP_FLOAT: + case CD_PROP_FLOAT2: + case CD_PROP_FLOAT3: + case CD_PROP_COLOR: { + if (layer != -1) { + DRW_AttributeRequest *req = drw_attributes_add_request( + &attrs_needed, (CustomDataType)type, layer, domain); + if (req) { + BLI_strncpy(req->attribute_name, name, sizeof(req->attribute_name)); + } + } + break; + } + } + } + + CurvesEvalFinalCache &final_cache = cache.curves_cache.final[subdiv]; + + const bool attr_overlap = drw_attributes_overlap(&final_cache.attr_used, &attrs_needed); + if (attr_overlap == false) { + /* Some new attributes have been added, free all and start over. */ + for (int i = 0; i < GPU_MAX_ATTR; i++) { + GPU_VERTBUF_DISCARD_SAFE(cache.curves_cache.proc_attributes_buf[i]); + DRW_TEXTURE_FREE_SAFE(cache.curves_cache.proc_attributes_tex[i]); + } + drw_attributes_merge(&final_cache.attr_used, &attrs_needed, render_mutex); + } + drw_attributes_merge(&final_cache.attr_used_over_time, &attrs_needed, render_mutex); + + bool need_tf_update = false; + + for (int i = 0; i < final_cache.attr_used.num_requests; i++) { + const DRW_AttributeRequest &request = final_cache.attr_used.requests[i]; + + if (cache.curves_cache.proc_attributes_buf[i] != nullptr) { + continue; + } + + if (request.domain == ATTR_DOMAIN_POINT) { + need_tf_update = true; + } + + curves_batch_ensure_attribute(curves, cache.curves_cache, request, subdiv, i); + } + + return need_tf_update; +} + bool curves_ensure_procedural_data(Object *object, CurvesEvalCache **r_hair_cache, GPUMaterial *gpu_material, @@ -390,6 +617,10 @@ bool curves_ensure_procedural_data(Object *object, curves, cache.curves_cache, thickness_res, subdiv); } + if (gpu_material) { + need_ft_update |= curves_ensure_attributes(curves, cache, gpu_material, subdiv); + } + return need_ft_update; } diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index dac4b7488be..a6ab2176d16 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -281,91 +281,6 @@ static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *c cd_used->edit_uv = 1; } -/** \name DRW_MeshAttributes - * - * Utilities for handling requested attributes. - * \{ */ - -/* Return true if the given DRW_AttributeRequest is already in the requests. */ -static bool has_request(const DRW_MeshAttributes *requests, DRW_AttributeRequest req) -{ - for (int i = 0; i < requests->num_requests; i++) { - const DRW_AttributeRequest src_req = requests->requests[i]; - if (src_req.domain != req.domain) { - continue; - } - if (src_req.layer_index != req.layer_index) { - continue; - } - if (src_req.cd_type != req.cd_type) { - continue; - } - return true; - } - return false; -} - -static void mesh_attrs_merge_requests(const DRW_MeshAttributes *src_requests, - DRW_MeshAttributes *dst_requests) -{ - for (int i = 0; i < src_requests->num_requests; i++) { - if (dst_requests->num_requests == GPU_MAX_ATTR) { - return; - } - - if (has_request(dst_requests, src_requests->requests[i])) { - continue; - } - - dst_requests->requests[dst_requests->num_requests] = src_requests->requests[i]; - dst_requests->num_requests += 1; - } -} - -static void drw_mesh_attributes_clear(DRW_MeshAttributes *attributes) -{ - memset(attributes, 0, sizeof(DRW_MeshAttributes)); -} - -static void drw_mesh_attributes_merge(DRW_MeshAttributes *dst, - const DRW_MeshAttributes *src, - ThreadMutex *mesh_render_mutex) -{ - BLI_mutex_lock(mesh_render_mutex); - mesh_attrs_merge_requests(src, dst); - BLI_mutex_unlock(mesh_render_mutex); -} - -/* Return true if all requests in b are in a. */ -static bool drw_mesh_attributes_overlap(DRW_MeshAttributes *a, DRW_MeshAttributes *b) -{ - for (int i = 0; i < b->num_requests; i++) { - if (!has_request(a, b->requests[i])) { - return false; - } - } - - return true; -} - -static void drw_mesh_attributes_add_request(DRW_MeshAttributes *attrs, - CustomDataType type, - int layer, - AttributeDomain domain) -{ - if (attrs->num_requests >= GPU_MAX_ATTR) { - return; - } - - DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests]; - req->cd_type = type; - req->layer_index = layer; - req->domain = domain; - attrs->num_requests += 1; -} - -/** \} */ - BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) { switch ((eMeshWrapperType)me->runtime.wrapper_type) { @@ -475,36 +390,6 @@ static void mesh_cd_calc_active_mloopcol_layer(const Object *object, } } -static bool custom_data_match_attribute(const CustomData *custom_data, - const char *name, - int *r_layer_index, - int *r_type) -{ - const int possible_attribute_types[7] = { - CD_PROP_BOOL, - CD_PROP_INT8, - CD_PROP_INT32, - CD_PROP_FLOAT, - CD_PROP_FLOAT2, - CD_PROP_FLOAT3, - CD_PROP_COLOR, - }; - - for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) { - const int attr_type = possible_attribute_types[i]; - int layer_index = CustomData_get_named_layer(custom_data, attr_type, name); - if (layer_index == -1) { - continue; - } - - *r_layer_index = layer_index; - *r_type = attr_type; - return true; - } - - return false; -} - static uint mesh_cd_calc_gpu_layers_vcol_used(const Mesh *me_query, const CustomData *cd_vdata, const CustomData *cd_ldata, @@ -531,7 +416,7 @@ static uint mesh_cd_calc_gpu_layers_vcol_used(const Mesh *me_query, layer_i; } - /* Note: this is not the same as the layer_i below. */ + /* NOTE: this is not the same as the layer_i below. */ if (layer_i != -1) { layer = (domain == ATTR_DOMAIN_POINT ? cd_vdata : cd_ldata)->layers + layer_i; } @@ -544,7 +429,7 @@ static uint mesh_cd_calc_gpu_layers_vcol_used(const Mesh *me_query, return -1; } - /* Note: this is the logical index into the color attribute list, + /* NOTE: this is the logical index into the color attribute list, * not the customdata index. */ int vcol_i = BKE_id_attribute_to_index( (ID *)me_query, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); @@ -556,7 +441,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, const Mesh *me, struct GPUMaterial **gpumat_array, int gpumat_array_len, - DRW_MeshAttributes *attributes) + DRW_Attributes *attributes) { const Mesh *me_final = editmesh_final_or_this(object, me); const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); @@ -636,16 +521,16 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, if (layer == -1) { /* Try to match a generic attribute, we use the first attribute domain with a * matching name. */ - if (custom_data_match_attribute(cd_vdata, name, &layer, &type)) { + if (drw_custom_data_match_attribute(cd_vdata, name, &layer, &type)) { domain = ATTR_DOMAIN_POINT; } - else if (custom_data_match_attribute(cd_ldata, name, &layer, &type)) { + else if (drw_custom_data_match_attribute(cd_ldata, name, &layer, &type)) { domain = ATTR_DOMAIN_CORNER; } - else if (custom_data_match_attribute(cd_pdata, name, &layer, &type)) { + else if (drw_custom_data_match_attribute(cd_pdata, name, &layer, &type)) { domain = ATTR_DOMAIN_FACE; } - else if (custom_data_match_attribute(cd_edata, name, &layer, &type)) { + else if (drw_custom_data_match_attribute(cd_edata, name, &layer, &type)) { domain = ATTR_DOMAIN_EDGE; } else { @@ -701,7 +586,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, break; } - /* Note: attr->type will always be CD_PROP_COLOR even for + /* NOTE: attr->type will always be CD_PROP_COLOR even for * CD_PROP_BYTE_COLOR layers, see node_shader_gpu_vertex_color in * node_shader_vertex_color.cc. */ @@ -718,7 +603,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, } if (layer != -1 && domain != ATTR_DOMAIN_NUM) { - drw_mesh_attributes_add_request(attributes, type, layer, domain); + drw_attributes_add_request(attributes, type, layer, domain); } break; } @@ -729,7 +614,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, case CD_PROP_FLOAT: case CD_PROP_FLOAT2: { if (layer != -1 && domain != ATTR_DOMAIN_NUM) { - drw_mesh_attributes_add_request(attributes, type, layer, domain); + drw_attributes_add_request(attributes, type, layer, domain); } break; } @@ -1317,8 +1202,8 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Object *object, uint gpumat_array_len) { MeshBatchCache *cache = mesh_batch_cache_get(me); - DRW_MeshAttributes attrs_needed; - drw_mesh_attributes_clear(&attrs_needed); + DRW_Attributes attrs_needed; + drw_attributes_clear(&attrs_needed); DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers( object, me, gpumat_array, gpumat_array_len, &attrs_needed); @@ -1326,7 +1211,7 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Object *object, mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; - drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex); + drw_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex); mesh_batch_cache_request_surface_batches(cache); return cache->surface_per_mat; } @@ -1596,7 +1481,7 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime) cache->lastmatch = ctime; } - if (drw_mesh_attributes_overlap(&cache->attr_used_over_time, &cache->attr_used)) { + if (drw_attributes_overlap(&cache->attr_used_over_time, &cache->attr_used)) { cache->lastmatch = ctime; } @@ -1605,12 +1490,12 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime) } mesh_cd_layers_type_clear(&cache->cd_used_over_time); - drw_mesh_attributes_clear(&cache->attr_used_over_time); + drw_attributes_clear(&cache->attr_used_over_time); } static void drw_add_attributes_vbo(GPUBatch *batch, MeshBufferList *mbuflist, - DRW_MeshAttributes *attr_used) + DRW_Attributes *attr_used) { for (int i = 0; i < attr_used->num_requests; i++) { DRW_vbo_request(batch, &mbuflist->vbo.attr[i]); @@ -1721,7 +1606,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, /* TODO(fclem): We could be a bit smarter here and only do it per * material. */ bool cd_overlap = mesh_cd_layers_type_overlap(cache->cd_used, cache->cd_needed); - bool attr_overlap = drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed); + bool attr_overlap = drw_attributes_overlap(&cache->attr_used, &cache->attr_needed); if (cd_overlap == false || attr_overlap == false) { FOREACH_MESH_BUFFER_CACHE (cache, mbc) { if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv) { @@ -1741,7 +1626,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol); } - if (!drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed)) { + if (!drw_attributes_overlap(&cache->attr_used, &cache->attr_needed)) { for (int i = 0; i < GPU_MAX_ATTR; i++) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.attr[i]); } @@ -1756,13 +1641,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, cache->batch_ready &= ~(MBC_SURFACE); mesh_cd_layers_type_merge(&cache->cd_used, cache->cd_needed); - drw_mesh_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex); + drw_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex); } mesh_cd_layers_type_merge(&cache->cd_used_over_time, cache->cd_needed); mesh_cd_layers_type_clear(&cache->cd_needed); - drw_mesh_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex); - drw_mesh_attributes_clear(&cache->attr_needed); + drw_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex); + drw_attributes_clear(&cache->attr_needed); } if (batch_requested & MBC_EDITUV) { diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c index a1c0a42ba6f..c1d609bf648 100644 --- a/source/blender/draw/intern/draw_cache_impl_particles.c +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -277,7 +277,7 @@ static void particle_calculate_parent_uvs(ParticleSystem *psys, ParticleSystemModifierData *psmd, const int num_uv_layers, const int parent_index, - /*const*/ MTFace **mtfaces, + const MTFace **mtfaces, float (*r_uv)[2]) { if (psmd == NULL) { @@ -306,7 +306,7 @@ static void particle_calculate_parent_mcol(ParticleSystem *psys, ParticleSystemModifierData *psmd, const int num_col_layers, const int parent_index, - /*const*/ MCol **mcols, + const MCol **mcols, MCol *r_mcol) { if (psmd == NULL) { @@ -337,7 +337,7 @@ static void particle_interpolate_children_uvs(ParticleSystem *psys, ParticleSystemModifierData *psmd, const int num_uv_layers, const int child_index, - /*const*/ MTFace **mtfaces, + const MTFace **mtfaces, float (*r_uv)[2]) { if (psmd == NULL) { @@ -361,7 +361,7 @@ static void particle_interpolate_children_mcol(ParticleSystem *psys, ParticleSystemModifierData *psmd, const int num_col_layers, const int child_index, - /*const*/ MCol **mcols, + const MCol **mcols, MCol *r_mcol) { if (psmd == NULL) { @@ -388,7 +388,7 @@ static void particle_calculate_uvs(ParticleSystem *psys, const int num_uv_layers, const int parent_index, const int child_index, - /*const*/ MTFace **mtfaces, + const MTFace **mtfaces, float (**r_parent_uvs)[2], float (**r_uv)[2]) { @@ -431,7 +431,7 @@ static void particle_calculate_mcol(ParticleSystem *psys, const int num_col_layers, const int parent_index, const int child_index, - /*const*/ MCol **mcols, + const MCol **mcols, MCol **r_parent_mcol, MCol **r_mcol) { @@ -482,8 +482,8 @@ static int particle_batch_cache_fill_segments(ParticleSystem *psys, const int num_path_keys, const int num_uv_layers, const int num_col_layers, - /*const*/ MTFace **mtfaces, - /*const*/ MCol **mcols, + const MTFace **mtfaces, + const MCol **mcols, uint *uv_id, uint *col_id, float (***r_parent_uvs)[2], @@ -713,11 +713,11 @@ static int particle_batch_cache_fill_strands_data(ParticleSystem *psys, GPUVertBufRaw *seg_step, float (***r_parent_uvs)[2], GPUVertBufRaw *uv_step, - MTFace **mtfaces, + const MTFace **mtfaces, int num_uv_layers, MCol ***r_parent_mcol, GPUVertBufRaw *col_step, - MCol **mcols, + const MCol **mcols, int num_col_layers) { const bool is_simple = (psys->part->childtype == PART_CHILD_PARTICLES); @@ -834,8 +834,8 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit GPUVertBufRaw uv_step[MAX_MTFACE]; GPUVertBufRaw col_step[MAX_MCOL]; - MTFace *mtfaces[MAX_MTFACE] = {NULL}; - MCol *mcols[MAX_MCOL] = {NULL}; + const MTFace *mtfaces[MAX_MTFACE] = {NULL}; + const MCol *mcols[MAX_MCOL] = {NULL}; float(**parent_uvs)[2] = NULL; MCol **parent_mcol = NULL; @@ -909,12 +909,13 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit BKE_mesh_tessface_ensure(psmd->mesh_final); if (cache->num_uv_layers) { for (int j = 0; j < cache->num_uv_layers; j++) { - mtfaces[j] = (MTFace *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MTFACE, j); + mtfaces[j] = (const MTFace *)CustomData_get_layer_n( + &psmd->mesh_final->fdata, CD_MTFACE, j); } } if (cache->num_col_layers) { for (int j = 0; j < cache->num_col_layers; j++) { - mcols[j] = (MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, j); + mcols[j] = (const MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, j); } } } @@ -930,11 +931,11 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit &seg_step, &parent_uvs, uv_step, - (MTFace **)mtfaces, + mtfaces, cache->num_uv_layers, &parent_mcol, col_step, - (MCol **)mcols, + mcols, cache->num_col_layers); } else { @@ -951,11 +952,11 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit &seg_step, &parent_uvs, uv_step, - (MTFace **)mtfaces, + mtfaces, cache->num_uv_layers, &parent_mcol, col_step, - (MCol **)mcols, + mcols, cache->num_col_layers); } if (psys->childcache) { @@ -970,11 +971,11 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit &seg_step, &parent_uvs, uv_step, - (MTFace **)mtfaces, + mtfaces, cache->num_uv_layers, &parent_mcol, col_step, - (MCol **)mcols, + mcols, cache->num_col_layers); } } @@ -1147,8 +1148,8 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, int num_col_layers = 0; int active_uv = 0; int active_col = 0; - MTFace **mtfaces = NULL; - MCol **mcols = NULL; + const MTFace **mtfaces = NULL; + const MCol **mcols = NULL; float(**parent_uvs)[2] = NULL; MCol **parent_mcol = NULL; @@ -1214,13 +1215,14 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, if (num_uv_layers) { mtfaces = MEM_mallocN(sizeof(*mtfaces) * num_uv_layers, "Faces UV layers"); for (int i = 0; i < num_uv_layers; i++) { - mtfaces[i] = (MTFace *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MTFACE, i); + mtfaces[i] = (const MTFace *)CustomData_get_layer_n( + &psmd->mesh_final->fdata, CD_MTFACE, i); } } if (num_col_layers) { mcols = MEM_mallocN(sizeof(*mcols) * num_col_layers, "Color layers"); for (int i = 0; i < num_col_layers; i++) { - mcols[i] = (MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, i); + mcols[i] = (const MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, i); } } } @@ -1304,10 +1306,10 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, MEM_freeN(parent_mcol); } if (num_uv_layers) { - MEM_freeN(mtfaces); + MEM_freeN((void *)mtfaces); } if (num_col_layers) { - MEM_freeN(mcols); + MEM_freeN((void *)mcols); } if (psmd != NULL) { MEM_freeN(uv_id); diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 8eb0509d615..dc40e15a450 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -69,6 +69,7 @@ enum { SHADER_PATCH_EVALUATION, SHADER_PATCH_EVALUATION_FVAR, SHADER_PATCH_EVALUATION_FACE_DOTS, + SHADER_PATCH_EVALUATION_ORCO, SHADER_COMP_CUSTOM_DATA_INTERP_1D, SHADER_COMP_CUSTOM_DATA_INTERP_2D, SHADER_COMP_CUSTOM_DATA_INTERP_3D, @@ -107,7 +108,8 @@ static const char *get_shader_code(int shader_type) } case SHADER_PATCH_EVALUATION: case SHADER_PATCH_EVALUATION_FVAR: - case SHADER_PATCH_EVALUATION_FACE_DOTS: { + case SHADER_PATCH_EVALUATION_FACE_DOTS: + case SHADER_PATCH_EVALUATION_ORCO: { return datatoc_common_subdiv_patch_evaluation_comp_glsl; } case SHADER_COMP_CUSTOM_DATA_INTERP_1D: @@ -163,6 +165,9 @@ static const char *get_shader_name(int shader_type) case SHADER_PATCH_EVALUATION_FACE_DOTS: { return "subdiv patch evaluation face dots"; } + case SHADER_PATCH_EVALUATION_ORCO: { + return "subdiv patch evaluation orco"; + } case SHADER_COMP_CUSTOM_DATA_INTERP_1D: { return "subdiv custom data interp 1D"; } @@ -206,6 +211,12 @@ static GPUShader *get_patch_evaluation_shader(int shader_type) "#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n" "#define FDOTS_EVALUATION\n"; } + else if (shader_type == SHADER_PATCH_EVALUATION_ORCO) { + defines = + "#define OSD_PATCH_BASIS_GLSL\n" + "#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n" + "#define ORCO_EVALUATION\n"; + } else { defines = "#define OSD_PATCH_BASIS_GLSL\n" @@ -236,7 +247,8 @@ static GPUShader *get_subdiv_shader(int shader_type, const char *defines) if (ELEM(shader_type, SHADER_PATCH_EVALUATION, SHADER_PATCH_EVALUATION_FVAR, - SHADER_PATCH_EVALUATION_FACE_DOTS)) { + SHADER_PATCH_EVALUATION_FACE_DOTS, + SHADER_PATCH_EVALUATION_ORCO)) { return get_patch_evaluation_shader(shader_type); } if (g_subdiv_shaders[shader_type] == nullptr) { @@ -710,6 +722,23 @@ static DRWSubdivCache *mesh_batch_cache_ensure_subdiv_cache(MeshBatchCache *mbc) return subdiv_cache; } +static void draw_subdiv_invalidate_evaluator_for_orco(Subdiv *subdiv, Mesh *mesh) +{ + const bool has_orco = CustomData_has_layer(&mesh->vdata, CD_ORCO); + if (has_orco && subdiv->evaluator && !subdiv->evaluator->hasVertexData(subdiv->evaluator)) { + /* If we suddenly have/need original coordinates, recreate the evaluator if the extra + * source was not created yet. The refiner also has to be recreated as refinement for source + * and vertex data is done only once. */ + openSubdiv_deleteEvaluator(subdiv->evaluator); + subdiv->evaluator = nullptr; + + if (subdiv->topology_refiner != nullptr) { + openSubdiv_deleteTopologyRefiner(subdiv->topology_refiner); + subdiv->topology_refiner = nullptr; + } + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -745,8 +774,8 @@ struct DRWCacheBuildingContext { /* Origindex layers from the mesh to directly look up during traversal the origindex from the * base mesh for edit data so that we do not have to handle yet another GPU buffer and do this in * the shaders. */ - int *v_origindex; - int *e_origindex; + const int *v_origindex; + const int *e_origindex; }; static bool draw_subdiv_topology_info_cb(const SubdivForeachContext *foreach_context, @@ -1230,7 +1259,9 @@ static void drw_subdiv_compute_dispatch(const DRWSubdivCache *cache, GPU_compute_dispatch(shader, dispatch_rx, dispatch_ry, 1); } -void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_nor) +void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, + GPUVertBuf *pos_nor, + GPUVertBuf *orco) { if (!draw_subdiv_cache_need_polygon_data(cache)) { /* Happens on meshes with only loose geometry. */ @@ -1245,6 +1276,14 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no get_subdiv_vertex_format()); evaluator->wrapSrcBuffer(evaluator, &src_buffer_interface); + GPUVertBuf *src_extra_buffer = nullptr; + if (orco) { + OpenSubdiv_Buffer src_extra_buffer_interface; + src_extra_buffer = create_buffer_and_interface(&src_extra_buffer_interface, + get_subdiv_vertex_format()); + evaluator->wrapSrcVertexDataBuffer(evaluator, &src_extra_buffer_interface); + } + OpenSubdiv_Buffer patch_arrays_buffer_interface; GPUVertBuf *patch_arrays_buffer = create_buffer_and_interface(&patch_arrays_buffer_interface, get_patch_array_format()); @@ -1260,7 +1299,8 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no get_patch_param_format()); evaluator->wrapPatchParamBuffer(evaluator, &patch_param_buffer_interface); - GPUShader *shader = get_patch_evaluation_shader(SHADER_PATCH_EVALUATION); + GPUShader *shader = get_patch_evaluation_shader(orco ? SHADER_PATCH_EVALUATION_ORCO : + SHADER_PATCH_EVALUATION); GPU_shader_bind(shader); int binding_point = 0; @@ -1273,6 +1313,10 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no GPU_vertbuf_bind_as_ssbo(patch_index_buffer, binding_point++); GPU_vertbuf_bind_as_ssbo(patch_param_buffer, binding_point++); GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++); + if (orco) { + GPU_vertbuf_bind_as_ssbo(src_extra_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(orco, binding_point++); + } BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_quads); @@ -1289,6 +1333,7 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no GPU_vertbuf_discard(patch_param_buffer); GPU_vertbuf_discard(patch_arrays_buffer); GPU_vertbuf_discard(src_buffer); + GPU_VERTBUF_DISCARD_SAFE(src_extra_buffer); } void draw_subdiv_extract_uvs(const DRWSubdivCache *cache, @@ -1935,6 +1980,8 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, return false; } + draw_subdiv_invalidate_evaluator_for_orco(subdiv, mesh_eval); + if (!BKE_subdiv_eval_begin_from_mesh( subdiv, mesh_eval, nullptr, SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE, evaluator_cache)) { /* This could happen in two situations: diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 779ac43178c..b6b0c94f4bf 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -13,6 +13,7 @@ extern "C" { #endif +struct CurvesUniformBufPool; struct DRWShadingGroup; struct FluidModifierData; struct GPUMaterial; @@ -44,7 +45,7 @@ float *DRW_color_background_blend_get(int theme_id); bool DRW_object_is_flat(struct Object *ob, int *r_axis); bool DRW_object_axis_orthogonal_to_view(struct Object *ob, int axis); -/* draw_hair.c */ +/* draw_hair.cc */ /** * This creates a shading group with display hairs. @@ -82,7 +83,8 @@ struct DRWShadingGroup *DRW_shgroup_curves_create_sub(struct Object *object, struct DRWShadingGroup *shgrp, struct GPUMaterial *gpu_material); -void DRW_curves_init(void); +void DRW_curves_init(struct DRWData *drw_data); +void DRW_curves_ubos_pool_free(struct CurvesUniformBufPool *pool); void DRW_curves_update(void); void DRW_curves_free(void); diff --git a/source/blender/draw/intern/draw_curves.cc b/source/blender/draw/intern/draw_curves.cc index 88118361115..d90c63b680e 100644 --- a/source/blender/draw/intern/draw_curves.cc +++ b/source/blender/draw/intern/draw_curves.cc @@ -10,6 +10,7 @@ #include "BLI_string_utils.h" #include "BLI_utildefines.h" +#include "DNA_curves_types.h" #include "DNA_customdata_types.h" #include "GPU_batch.h" @@ -20,9 +21,13 @@ #include "GPU_texture.h" #include "GPU_vertex_buffer.h" +#include "DRW_gpu_wrapper.hh" #include "DRW_render.h" +#include "draw_cache_impl.h" +#include "draw_curves_private.h" #include "draw_hair_private.h" +#include "draw_manager.h" #include "draw_shader.h" #ifndef __APPLE__ @@ -61,16 +66,43 @@ static GPUVertBuf *g_dummy_vbo = nullptr; static GPUTexture *g_dummy_texture = nullptr; static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in the future */ +using CurvesInfosBuf = blender::draw::UniformBuffer<CurvesInfos>; + +struct CurvesUniformBufPool { + blender::Vector<std::unique_ptr<CurvesInfosBuf>> ubos; + int used = 0; + + void reset() + { + used = 0; + } + + CurvesInfosBuf &alloc() + { + if (used >= ubos.size()) { + ubos.append(std::make_unique<CurvesInfosBuf>()); + return *ubos.last().get(); + } + return *ubos[used++].get(); + } +}; + static GPUShader *curves_eval_shader_get(CurvesEvalShader type) { return DRW_shader_curves_refine_get(type, drw_curves_shader_type_get()); } -void DRW_curves_init(void) +void DRW_curves_init(DRWData *drw_data) { /* Initialize legacy hair too, to avoid verbosity in callers. */ DRW_hair_init(); + if (drw_data->curves_ubos == nullptr) { + drw_data->curves_ubos = MEM_new<CurvesUniformBufPool>("CurvesUniformBufPool"); + } + CurvesUniformBufPool *pool = drw_data->curves_ubos; + pool->reset(); + #if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS) g_tf_pass = DRW_pass_create("Update Curves Pass", (DRWState)0); #else @@ -94,63 +126,120 @@ void DRW_curves_init(void) } } +void DRW_curves_ubos_pool_free(CurvesUniformBufPool *pool) +{ + MEM_delete(pool); +} + static void drw_curves_cache_shgrp_attach_resources(DRWShadingGroup *shgrp, CurvesEvalCache *cache, + GPUTexture *tex, const int subdiv) { - DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", cache->point_tex); + DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", tex); DRW_shgroup_uniform_texture(shgrp, "hairStrandBuffer", cache->strand_tex); DRW_shgroup_uniform_texture(shgrp, "hairStrandSegBuffer", cache->strand_seg_tex); DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1); } +static void drw_curves_cache_update_compute(CurvesEvalCache *cache, + const int subdiv, + const int strands_len, + GPUVertBuf *buffer, + GPUTexture *tex) +{ + GPUShader *shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM); + DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass); + drw_curves_cache_shgrp_attach_resources(shgrp, cache, tex, subdiv); + DRW_shgroup_vertex_buffer(shgrp, "posTime", buffer); + + const int max_strands_per_call = GPU_max_work_group_count(0); + int strands_start = 0; + while (strands_start < strands_len) { + int batch_strands_len = MIN2(strands_len - strands_start, max_strands_per_call); + DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp); + DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start); + DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1); + strands_start += batch_strands_len; + } +} + static void drw_curves_cache_update_compute(CurvesEvalCache *cache, const int subdiv) { const int strands_len = cache->strands_len; const int final_points_len = cache->final[subdiv].strands_res * strands_len; - if (final_points_len > 0) { - GPUShader *shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM); - DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass); - drw_curves_cache_shgrp_attach_resources(shgrp, cache, subdiv); - DRW_shgroup_vertex_buffer(shgrp, "posTime", cache->final[subdiv].proc_buf); - - const int max_strands_per_call = GPU_max_work_group_count(0); - int strands_start = 0; - while (strands_start < strands_len) { - int batch_strands_len = MIN2(strands_len - strands_start, max_strands_per_call); - DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp); - DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start); - DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1); - strands_start += batch_strands_len; + if (final_points_len == 0) { + return; + } + + drw_curves_cache_update_compute( + cache, subdiv, strands_len, cache->final[subdiv].proc_buf, cache->point_tex); + + const DRW_Attributes &attrs = cache->final[subdiv].attr_used; + for (int i = 0; i < attrs.num_requests; i++) { + /* Only refine point attributes. */ + if (attrs.requests[i].domain == ATTR_DOMAIN_CURVE) { + continue; } + + drw_curves_cache_update_compute(cache, + subdiv, + strands_len, + cache->final[subdiv].attributes_buf[i], + cache->proc_attributes_tex[i]); } } -static void drw_curves_cache_update_transform_feedback(CurvesEvalCache *cache, const int subdiv) +static void drw_curves_cache_update_transform_feedback(CurvesEvalCache *cache, + GPUVertBuf *vbo, + GPUTexture *tex, + const int subdiv, + const int final_points_len) { - const int final_points_len = cache->final[subdiv].strands_res * cache->strands_len; - if (final_points_len > 0) { - GPUShader *tf_shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM); + GPUShader *tf_shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM); #ifdef USE_TRANSFORM_FEEDBACK - DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create( - tf_shader, g_tf_pass, cache->final[subdiv].proc_buf); + DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(tf_shader, g_tf_pass, vbo); #else - DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); - - CurvesEvalCall *pr_call = MEM_new<CurvesEvalCall>(__func__); - pr_call->next = g_tf_calls; - pr_call->vbo = cache->final[subdiv].proc_buf; - pr_call->shgrp = tf_shgrp; - pr_call->vert_len = final_points_len; - g_tf_calls = pr_call; - DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1); - DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1); - DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1); + DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); + + CurvesEvalCall *pr_call = MEM_new<CurvesEvalCall>(__func__); + pr_call->next = g_tf_calls; + pr_call->vbo = vbo; + pr_call->shgrp = tf_shgrp; + pr_call->vert_len = final_points_len; + g_tf_calls = pr_call; + DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1); + DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1); + DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1); #endif - drw_curves_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv); - DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len); + drw_curves_cache_shgrp_attach_resources(tf_shgrp, cache, tex, subdiv); + DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len); +} + +static void drw_curves_cache_update_transform_feedback(CurvesEvalCache *cache, const int subdiv) +{ + const int final_points_len = cache->final[subdiv].strands_res * cache->strands_len; + if (final_points_len == 0) { + return; + } + + drw_curves_cache_update_transform_feedback( + cache, cache->final[subdiv].proc_buf, cache->point_tex, subdiv, final_points_len); + + const DRW_Attributes &attrs = cache->final[subdiv].attr_used; + for (int i = 0; i < attrs.num_requests; i++) { + /* Only refine point attributes. */ + if (attrs.requests[i].domain == ATTR_DOMAIN_CURVE) { + continue; + } + + drw_curves_cache_update_transform_feedback(cache, + cache->final[subdiv].attributes_buf[i], + cache->proc_attributes_tex[i], + subdiv, + final_points_len); } } @@ -186,12 +275,34 @@ GPUVertBuf *DRW_curves_pos_buffer_get(Object *object) return cache->final[subdiv].proc_buf; } +static int attribute_index_in_material(GPUMaterial *gpu_material, const char *name) +{ + if (!gpu_material) { + return -1; + } + + int index = 0; + + ListBase gpu_attrs = GPU_material_attributes(gpu_material); + LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) { + if (STREQ(gpu_attr->name, name)) { + return index; + } + + index++; + } + + return -1; +} + DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, DRWShadingGroup *shgrp_parent, GPUMaterial *gpu_material) { const DRWContextState *draw_ctx = DRW_context_state_get(); Scene *scene = draw_ctx->scene; + CurvesUniformBufPool *pool = DST.vmempool->curves_ubos; + CurvesInfosBuf &curves_infos = pool->alloc(); int subdiv = scene->r.hair_subdiv; int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; @@ -209,7 +320,7 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture); /* TODO: Generalize radius implementation for curves data type. */ - float hair_rad_shape = 1.0f; + float hair_rad_shape = 0.0f; float hair_rad_root = 0.005f; float hair_rad_tip = 0.0f; bool hair_close_tip = true; @@ -218,6 +329,43 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, if (curves_cache->length_tex) { DRW_shgroup_uniform_texture(shgrp, "hairLen", curves_cache->length_tex); } + + const DRW_Attributes &attrs = curves_cache->final[subdiv].attr_used; + for (int i = 0; i < attrs.num_requests; i++) { + const DRW_AttributeRequest &request = attrs.requests[i]; + + char sampler_name[32]; + drw_curves_get_attribute_sampler_name(request.attribute_name, sampler_name); + + if (request.domain == ATTR_DOMAIN_CURVE) { + if (!curves_cache->proc_attributes_tex[i]) { + continue; + } + + DRW_shgroup_uniform_texture(shgrp, sampler_name, curves_cache->proc_attributes_tex[i]); + } + else { + if (!curves_cache->final[subdiv].attributes_tex[i]) { + continue; + } + DRW_shgroup_uniform_texture( + shgrp, sampler_name, curves_cache->final[subdiv].attributes_tex[i]); + } + + /* Some attributes may not be used in the shader anymore and were not garbage collected yet, so + * we need to find the right index for this attribute as uniforms defining the scope of the + * attributes are based on attribute loading order, which is itself based on the material's + * attributes. */ + const int index = attribute_index_in_material(gpu_material, request.attribute_name); + if (index != -1) { + curves_infos.is_point_attribute[index] = request.domain == ATTR_DOMAIN_POINT; + } + } + + curves_infos.push_update(); + + DRW_shgroup_uniform_block(shgrp, "drw_curves", curves_infos); + DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &curves_cache->final[subdiv].strands_res, 1); DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res); DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape); diff --git a/source/blender/draw/intern/draw_curves_private.h b/source/blender/draw/intern/draw_curves_private.h index 76d5f15319d..ed4dd50dfbe 100644 --- a/source/blender/draw/intern/draw_curves_private.h +++ b/source/blender/draw/intern/draw_curves_private.h @@ -7,6 +7,11 @@ #pragma once +#include "BKE_attribute.h" +#include "GPU_shader.h" + +#include "draw_attributes.h" + #ifdef __cplusplus extern "C" { #endif @@ -35,6 +40,23 @@ typedef struct CurvesEvalFinalCache { /* Points per curve, at least 2. */ int strands_res; + + /* Attributes currently being or about to be drawn. */ + DRW_Attributes attr_used; + + /* Attributes which were used at some point. This is used for garbage collection, to remove + * attributes which are not used in shaders anymore due to user edits. */ + DRW_Attributes attr_used_over_time; + + /* Last time, in seconds, the `attr_used` and `attr_used_over_time` were exactly the same. + * If the delta between this time and the current scene time is greater than the timeout set in + * user preferences (`U.vbotimeout`) then garbage collection is performed. */ + int last_attr_matching_time; + + /* Output of the subdivision stage: vertex buffers sized to subdiv level. This is only attributes + * on point domain. */ + GPUVertBuf *attributes_buf[GPU_MAX_ATTR]; + GPUTexture *attributes_tex[GPU_MAX_ATTR]; } CurvesEvalFinalCache; /* Curves procedural display: Evaluation is done on the GPU. */ @@ -56,6 +78,11 @@ typedef struct CurvesEvalCache { CurvesEvalFinalCache final[MAX_HAIR_SUBDIV]; + /* For point attributes, which need subdivision, these are the input data. + * For spline attributes, which need not subdivision, these are the final data. */ + GPUVertBuf *proc_attributes_buf[GPU_MAX_ATTR]; + GPUTexture *proc_attributes_tex[GPU_MAX_ATTR]; + int strands_len; int elems_len; int point_len; @@ -70,6 +97,8 @@ bool curves_ensure_procedural_data(struct Object *object, int subdiv, int thickness_res); +void drw_curves_get_attribute_sampler_name(const char *layer_name, char r_sampler_name[32]); + #ifdef __cplusplus } #endif diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.cc index 8351452769d..0a3c16e0d71 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.cc @@ -35,7 +35,7 @@ # define USE_COMPUTE_SHADERS #endif -BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get(void) +BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get() { #ifdef USE_COMPUTE_SHADERS if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support()) { @@ -49,21 +49,21 @@ BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get(void) } #ifndef USE_TRANSFORM_FEEDBACK -typedef struct ParticleRefineCall { +struct ParticleRefineCall { struct ParticleRefineCall *next; GPUVertBuf *vbo; DRWShadingGroup *shgrp; uint vert_len; -} ParticleRefineCall; +}; -static ParticleRefineCall *g_tf_calls = NULL; +static ParticleRefineCall *g_tf_calls = nullptr; static int g_tf_id_offset; static int g_tf_target_width; static int g_tf_target_height; #endif -static GPUVertBuf *g_dummy_vbo = NULL; -static GPUTexture *g_dummy_texture = NULL; +static GPUVertBuf *g_dummy_vbo = nullptr; +static GPUTexture *g_dummy_texture = nullptr; static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in the future */ static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement) @@ -74,12 +74,12 @@ static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement) void DRW_hair_init(void) { #if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS) - g_tf_pass = DRW_pass_create("Update Hair Pass", 0); + g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_NO_DRAW); #else g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_WRITE_COLOR); #endif - if (g_dummy_vbo == NULL) { + if (g_dummy_vbo == nullptr) { /* initialize vertex format */ GPUVertFormat format = {0}; uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); @@ -141,7 +141,7 @@ static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache #else DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); - ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__); + ParticleRefineCall *pr_call = (ParticleRefineCall *)MEM_mallocN(sizeof(*pr_call), __func__); pr_call->next = g_tf_calls; pr_call->vbo = cache->final[subdiv].proc_buf; pr_call->shgrp = tf_shgrp; @@ -153,7 +153,7 @@ static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache #endif drw_hair_particle_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv); - DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len); + DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len); } } @@ -188,7 +188,7 @@ GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, Modifi int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; ParticleHairCache *cache = drw_hair_particle_cache_get( - object, psys, md, NULL, subdiv, thickness_res); + object, psys, md, nullptr, subdiv, thickness_res); return cache->final[subdiv].proc_buf; } @@ -201,11 +201,11 @@ void DRW_hair_duplimat_get(Object *object, Object *dupli_parent = DRW_object_get_dupli_parent(object); DupliObject *dupli_object = DRW_object_get_dupli(object); - if ((dupli_parent != NULL) && (dupli_object != NULL)) { + if ((dupli_parent != nullptr) && (dupli_object != nullptr)) { if (dupli_object->type & OB_DUPLICOLLECTION) { unit_m4(dupli_mat); Collection *collection = dupli_parent->instance_collection; - if (collection != NULL) { + if (collection != nullptr) { sub_v3_v3(dupli_mat[3], collection->instance_offset); } mul_m4_m4m4(dupli_mat, dupli_parent->obmat, dupli_mat); @@ -291,7 +291,7 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, return shgrp; } -void DRW_hair_update(void) +void DRW_hair_update() { #ifndef USE_TRANSFORM_FEEDBACK /** @@ -304,7 +304,7 @@ void DRW_hair_update(void) * and the most local workaround that still uses the power of the GPU. */ - if (g_tf_calls == NULL) { + if (g_tf_calls == nullptr) { return; } @@ -319,21 +319,22 @@ void DRW_hair_update(void) * Do chunks of maximum 2048 * 2048 hair points. */ int width = 2048; int height = min_ii(width, 1 + max_size / width); - GPUTexture *tex = DRW_texture_pool_query_2d(width, height, GPU_RGBA32F, (void *)DRW_hair_update); + GPUTexture *tex = DRW_texture_pool_query_2d( + width, height, GPU_RGBA32F, (DrawEngineType *)DRW_hair_update); g_tf_target_height = height; g_tf_target_width = width; - GPUFrameBuffer *fb = NULL; + GPUFrameBuffer *fb = nullptr; GPU_framebuffer_ensure_config(&fb, { GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(tex), }); - float *data = MEM_mallocN(sizeof(float[4]) * width * height, "tf fallback buffer"); + float *data = (float *)MEM_mallocN(sizeof(float[4]) * width * height, "tf fallback buffer"); GPU_framebuffer_bind(fb); - while (g_tf_calls != NULL) { + while (g_tf_calls != nullptr) { ParticleRefineCall *pr_call = g_tf_calls; g_tf_calls = g_tf_calls->next; @@ -342,7 +343,7 @@ void DRW_hair_update(void) int max_read_px_len = min_ii(width * height, pr_call->vert_len); DRW_draw_pass_subset(g_tf_pass, pr_call->shgrp, pr_call->shgrp); - /* Readback result to main memory. */ + /* Read back result to main memory. */ GPU_framebuffer_read_color(fb, 0, 0, width, height, 4, 0, GPU_DATA_FLOAT, data); /* Upload back to VBO. */ GPU_vertbuf_use(pr_call->vbo); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 4bbcf6eaf42..1372b843442 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -479,6 +479,7 @@ void DRW_viewport_data_free(DRWData *drw_data) MEM_freeN(drw_data->obinfos_ubo); } DRW_volume_ubos_pool_free(drw_data->volume_grids_ubos); + DRW_curves_ubos_pool_free(drw_data->curves_ubos); MEM_freeN(drw_data); } @@ -501,7 +502,7 @@ static DRWData *drw_viewport_data_ensure(GPUViewport *viewport) * - size can be NULL to get it from viewport. * - if viewport and size are NULL, size is set to (1, 1). * - * Important: drw_manager_init can be called multiple times before drw_manager_exit. + * IMPORTANT: #drw_manager_init can be called multiple times before #drw_manager_exit. */ static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int size[2]) { @@ -529,7 +530,7 @@ static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int s dst->view_data_active = dst->vmempool->view_data[view]; dst->resource_handle = 0; dst->pass_handle = 0; - dst->primary_view_ct = 0; + dst->primary_view_num = 0; drw_viewport_data_reset(dst->vmempool); @@ -1650,7 +1651,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, DRW_globals_update(); drw_debug_init(); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2022,7 +2023,7 @@ void DRW_render_object_iter( void (*callback)(void *vedata, Object *ob, RenderEngine *engine, struct Depsgraph *depsgraph)) { const DRWContextState *draw_ctx = DRW_context_state_get(); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2079,7 +2080,7 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type, drw_manager_init(&DST, NULL, NULL); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2114,7 +2115,7 @@ void DRW_cache_restart(void) DST.buffer_finish_called = false; - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); } @@ -2433,7 +2434,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, /* Init engines */ drw_engines_init(); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2607,7 +2608,7 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, /* Init engines */ drw_engines_init(); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 7a9585262ff..6d384c599d8 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -319,6 +319,8 @@ typedef enum { DRW_UNIFORM_STORAGE_BLOCK, DRW_UNIFORM_STORAGE_BLOCK_REF, DRW_UNIFORM_TFEEDBACK_TARGET, + DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE, + DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE_REF, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF, /** Per drawcall uniforms/UBO */ @@ -536,6 +538,8 @@ typedef struct DRWData { struct DRWTexturePool *texture_pool; /** Per stereo view data. Contains engine data and default framebuffers. */ struct DRWViewData *view_data[2]; + /** Per draw-call curves object data. */ + struct CurvesUniformBufPool *curves_ubos; } DRWData; /* ------------- DRAW MANAGER ------------ */ @@ -617,7 +621,7 @@ typedef struct DRWManager { DRWView *view_default; DRWView *view_active; DRWView *view_previous; - uint primary_view_ct; + uint primary_view_num; /** TODO(@fclem): Remove this. Only here to support * shaders without common_view_lib.glsl */ ViewInfos view_storage_cpy; diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index b5432da0957..f0960c5324b 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -547,6 +547,29 @@ void DRW_shgroup_vertex_buffer_ref_ex(DRWShadingGroup *shgroup, shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF, vertex_buffer, 0, 0, 1); } +void DRW_shgroup_buffer_texture(DRWShadingGroup *shgroup, + const char *name, + GPUVertBuf *vertex_buffer) +{ + int location = GPU_shader_get_ssbo(shgroup->shader, name); + if (location == -1) { + return; + } + drw_shgroup_uniform_create_ex( + shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE, vertex_buffer, 0, 0, 1); +} + +void DRW_shgroup_buffer_texture_ref(DRWShadingGroup *shgroup, + const char *name, + GPUVertBuf **vertex_buffer) +{ + int location = GPU_shader_get_ssbo(shgroup->shader, name); + if (location == -1) { + return; + } + drw_shgroup_uniform_create_ex( + shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE_REF, vertex_buffer, 0, 0, 1); +} /** \} */ /* -------------------------------------------------------------------- */ @@ -937,25 +960,25 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup, } void DRW_shgroup_call_range( - DRWShadingGroup *shgroup, struct Object *ob, GPUBatch *geom, uint v_sta, uint v_ct) + DRWShadingGroup *shgroup, struct Object *ob, GPUBatch *geom, uint v_sta, uint v_num) { BLI_assert(geom != NULL); if (G.f & G_FLAG_PICKSEL) { drw_command_set_select_id(shgroup, NULL, DST.select_id); } DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); - drw_command_draw_range(shgroup, geom, handle, v_sta, v_ct); + drw_command_draw_range(shgroup, geom, handle, v_sta, v_num); } void DRW_shgroup_call_instance_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct) + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_num) { BLI_assert(geom != NULL); if (G.f & G_FLAG_PICKSEL) { drw_command_set_select_id(shgroup, NULL, DST.select_id); } DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); - drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_ct); + drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_num); } void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, @@ -1905,8 +1928,8 @@ DRWView *DRW_view_create(const float viewmat[4][4], { DRWView *view = BLI_memblock_alloc(DST.vmempool->views); - if (DST.primary_view_ct < MAX_CULLED_VIEWS) { - view->culling_mask = 1u << DST.primary_view_ct++; + if (DST.primary_view_num < MAX_CULLED_VIEWS) { + view->culling_mask = 1u << DST.primary_view_num++; } else { BLI_assert(0); @@ -2058,6 +2081,11 @@ void DRW_view_camtexco_set(DRWView *view, float texco[4]) copy_v4_v4(view->storage.viewcamtexcofac, texco); } +void DRW_view_camtexco_get(const DRWView *view, float r_texco[4]) +{ + copy_v4_v4(r_texco, view->storage.viewcamtexcofac); +} + void DRW_view_frustum_corners_get(const DRWView *view, BoundBox *corners) { memcpy(corners, &view->frustum_corners, sizeof(view->frustum_corners)); diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 2c5b02f88a9..e7e0e0ce41f 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -693,6 +693,12 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, *use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader, ((GPUVertBuf *)uni->pvalue)); break; + case DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE_REF: + GPU_vertbuf_bind_as_texture(*uni->vertbuf_ref, uni->location); + break; + case DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE: + GPU_vertbuf_bind_as_texture(uni->vertbuf, uni->location); + break; case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF: GPU_vertbuf_bind_as_ssbo(*uni->vertbuf_ref, uni->location); break; diff --git a/source/blender/draw/intern/draw_shader.c b/source/blender/draw/intern/draw_shader.cc index 063aec24b94..001ceb0ae8d 100644 --- a/source/blender/draw/intern/draw_shader.c +++ b/source/blender/draw/intern/draw_shader.cc @@ -16,15 +16,15 @@ #include "draw_shader.h" -extern char datatoc_common_hair_lib_glsl[]; +extern "C" char datatoc_common_hair_lib_glsl[]; -extern char datatoc_common_hair_refine_vert_glsl[]; -extern char datatoc_common_hair_refine_comp_glsl[]; -extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; +extern "C" char datatoc_common_hair_refine_vert_glsl[]; +extern "C" char datatoc_common_hair_refine_comp_glsl[]; +extern "C" char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; static struct { struct GPUShader *hair_refine_sh[PART_REFINE_MAX_SHADER]; -} e_data = {{NULL}}; +} e_data = {{nullptr}}; /* -------------------------------------------------------------------- */ /** \name Hair refinement @@ -38,19 +38,19 @@ static GPUShader *hair_refine_shader_compute_create(ParticleRefineShader UNUSED( static GPUShader *hair_refine_shader_transform_feedback_create( ParticleRefineShader UNUSED(refinement)) { - GPUShader *sh = NULL; + GPUShader *sh = nullptr; + + std::string shader_src = std::string(datatoc_common_hair_lib_glsl) + + std::string(datatoc_common_hair_refine_vert_glsl); - char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, - datatoc_common_hair_refine_vert_glsl); const char *var_names[1] = {"finalColor"}; - sh = DRW_shader_create_with_transform_feedback(shader_src, - NULL, + sh = DRW_shader_create_with_transform_feedback(shader_src.c_str(), + nullptr, "#define HAIR_PHASE_SUBDIV\n" "#define USE_TF\n", GPU_SHADER_TFB_POINTS, var_names, 1); - MEM_freeN(shader_src); return sh; } @@ -64,8 +64,8 @@ static GPUShader *hair_refine_shader_transform_feedback_workaround_create( GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, eParticleRefineShaderType sh_type) { - if (e_data.hair_refine_sh[refinement] == NULL) { - GPUShader *sh = NULL; + if (e_data.hair_refine_sh[refinement] == nullptr) { + GPUShader *sh = nullptr; switch (sh_type) { case PART_REFINE_SHADER_COMPUTE: sh = hair_refine_shader_compute_create(refinement); @@ -88,8 +88,8 @@ GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineShaderType sh_type) { /* TODO: Implement curves evaluation types (Bezier and Catmull Rom). */ - if (e_data.hair_refine_sh[type] == NULL) { - GPUShader *sh = NULL; + if (e_data.hair_refine_sh[type] == nullptr) { + GPUShader *sh = nullptr; switch (sh_type) { case PART_REFINE_SHADER_COMPUTE: sh = hair_refine_shader_compute_create(PART_REFINE_CATMULL_ROM); @@ -111,7 +111,7 @@ GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineSh /** \} */ -void DRW_shaders_free(void) +void DRW_shaders_free() { for (int i = 0; i < PART_REFINE_MAX_SHADER; i++) { DRW_SHADER_FREE_SAFE(e_data.hair_refine_sh[i]); diff --git a/source/blender/draw/intern/draw_shader.h b/source/blender/draw/intern/draw_shader.h index 650e78c9362..63d755cc334 100644 --- a/source/blender/draw/intern/draw_shader.h +++ b/source/blender/draw/intern/draw_shader.h @@ -22,7 +22,7 @@ typedef enum eParticleRefineShaderType { PART_REFINE_SHADER_COMPUTE, } eParticleRefineShaderType; -/* draw_shader.c */ +/* draw_shader.cc */ struct GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, eParticleRefineShaderType sh_type); diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index db128fffde7..94c0c53dab7 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -1,12 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef GPU_SHADER +# include "GPU_shader.h" # include "GPU_shader_shared_utils.h" typedef struct ViewInfos ViewInfos; typedef struct ObjectMatrices ObjectMatrices; typedef struct ObjectInfos ObjectInfos; typedef struct VolumeInfos VolumeInfos; +typedef struct CurvesInfos CurvesInfos; #endif #define DRW_SHADER_SHARED_H @@ -16,6 +18,10 @@ typedef struct VolumeInfos VolumeInfos; /* Define the maximum number of grid we allow in a volume UBO. */ #define DRW_GRID_PER_VOLUME_MAX 16 +/* Define the maximum number of attribute we allow in a curves UBO. + * This should be kept in sync with `GPU_ATTR_MAX` */ +#define DRW_ATTRIBUTE_PER_CURVES_MAX 15 + struct ViewInfos { /* View matrices */ float4x4 persmat; @@ -79,6 +85,14 @@ struct VolumeInfos { }; BLI_STATIC_ASSERT_ALIGN(VolumeInfos, 16) +struct CurvesInfos { + /* Per attribute scope, follows loading order. + * NOTE: uint as bool in GLSL is 4 bytes. */ + uint is_point_attribute[DRW_ATTRIBUTE_PER_CURVES_MAX]; + int _pad; +}; +BLI_STATIC_ASSERT_ALIGN(CurvesInfos, 16) + #define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors) #define ObjectInfo (drw_infos[resource_id].drw_Infos) #define ObjectColor (drw_infos[resource_id].drw_ObjectColor) diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h index b7cd520f54f..7bddd000ecb 100644 --- a/source/blender/draw/intern/draw_subdivision.h +++ b/source/blender/draw/intern/draw_subdivision.h @@ -160,8 +160,8 @@ typedef struct DRWSubdivCache { /* Contains the start loop index and the smooth flag for each coarse polygon. */ struct GPUVertBuf *extra_coarse_face_data; - /* Computed for ibo.points, one value per subdivided vertex, mapping coarse vertices -> - * subdivided loop */ + /* Computed for `ibo.points`, one value per subdivided vertex, + * mapping coarse vertices -> subdivided loop. */ int *point_indices; /* Material offsets. */ @@ -235,7 +235,9 @@ void draw_subdiv_finalize_custom_normals(const DRWSubdivCache *cache, GPUVertBuf *src_custom_normals, GPUVertBuf *pos_nor); -void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, struct GPUVertBuf *pos_nor); +void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, + struct GPUVertBuf *pos_nor, + struct GPUVertBuf *orco); void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache, struct GPUVertBuf *src_data, diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h index d55386dfd7d..b88cd9e77d2 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.h +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h @@ -67,7 +67,7 @@ typedef struct MeshRenderData { const float (*bm_poly_normals)[3]; const float (*bm_poly_centers)[3]; - int *v_origindex, *e_origindex, *p_origindex; + const int *v_origindex, *e_origindex, *p_origindex; int edge_crease_ofs; int vert_crease_ofs; int bweight_ofs; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc index f9e58709c6e..e08aa1da420 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -14,6 +14,7 @@ #include "BKE_attribute.h" +#include "draw_attributes.h" #include "draw_subdivision.h" #include "extract_mesh.h" @@ -180,7 +181,7 @@ static void fill_vertbuf_with_attribute(const MeshRenderData *mr, const MPoly *mpoly = mr->mpoly; const MLoop *mloop = mr->mloop; - const AttributeType *attr_data = static_cast<AttributeType *>( + const AttributeType *attr_data = static_cast<const AttributeType *>( CustomData_get_layer_n(custom_data, request.cd_type, layer_index)); using converter = attribute_type_converter<AttributeType, VBOType>; @@ -284,7 +285,7 @@ static void extract_attr_init(const MeshRenderData *mr, void *UNUSED(tls_data), int index) { - const DRW_MeshAttributes *attrs_used = &cache->attr_used; + const DRW_Attributes *attrs_used = &cache->attr_used; const DRW_AttributeRequest &request = attrs_used->requests[index]; GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf); @@ -337,7 +338,7 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache, void *UNUSED(tls_data), int index) { - const DRW_MeshAttributes *attrs_used = &cache->attr_used; + const DRW_Attributes *attrs_used = &cache->attr_used; const DRW_AttributeRequest &request = attrs_used->requests[index]; Mesh *coarse_mesh = subdiv_cache->mesh; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc index 4ced14ab11a..fb4b95885fc 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc @@ -26,7 +26,7 @@ struct UVStretchAngle { struct MeshExtract_StretchAngle_Data { UVStretchAngle *vbo_data; - MLoopUV *luv; + const MLoopUV *luv; float auv[2][2], last_auv[2]; float av[2][3], last_av[3]; int cd_ofs; @@ -98,7 +98,7 @@ static void extract_edituv_stretch_angle_init(const MeshRenderData *mr, } else { BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); - data->luv = (MLoopUV *)CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); + data->luv = (const MLoopUV *)CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); } } @@ -236,7 +236,7 @@ static void extract_edituv_stretch_angle_init_subdiv(const DRWSubdivCache *subdi draw_subdiv_get_pos_nor_format(), subdiv_cache->num_subdiv_loops + loose_geom.loop_len); - draw_subdiv_extract_pos_nor(subdiv_cache, pos_nor); + draw_subdiv_extract_pos_nor(subdiv_cache, pos_nor, nullptr); } /* UVs are stored contiguously so we need to compute the offset in the UVs buffer for the active diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc index 26f0b07f676..e7a3cb03903 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc @@ -17,7 +17,7 @@ namespace blender::draw { struct MeshExtract_FdotUV_Data { float (*vbo_data)[2]; - MLoopUV *uv_data; + const MLoopUV *uv_data; int cd_ofs; }; @@ -49,7 +49,7 @@ static void extract_fdots_uv_init(const MeshRenderData *mr, data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); } else { - data->uv_data = (MLoopUV *)CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); + data->uv_data = (const MLoopUV *)CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); } } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc index ed1a0ccd178..94674a54f12 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc @@ -7,8 +7,6 @@ #include "extract_mesh.h" -#include "draw_subdivision.h" - namespace blender::draw { /* ---------------------------------------------------------------------- */ @@ -17,7 +15,7 @@ namespace blender::draw { struct MeshExtract_Orco_Data { float (*vbo_data)[4]; - float (*orco)[3]; + const float (*orco)[3]; }; static void extract_orco_init(const MeshRenderData *mr, @@ -42,7 +40,7 @@ static void extract_orco_init(const MeshRenderData *mr, MeshExtract_Orco_Data *data = static_cast<MeshExtract_Orco_Data *>(tls_data); data->vbo_data = (float(*)[4])GPU_vertbuf_get_data(vbo); - data->orco = static_cast<float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO)); + data->orco = static_cast<const float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO)); /* Make sure `orco` layer was requested only if needed! */ BLI_assert(data->orco); } @@ -79,77 +77,12 @@ static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, } } -static void extract_orco_init_subdiv(const DRWSubdivCache *subdiv_cache, - const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buffer, - void *UNUSED(data)) -{ - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex - * attributes. This is a substantial waste of video-ram and should be done another way. - * Unfortunately, at the time of writing, I did not found any other "non disruptive" - * alternative. */ - GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - } - - GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer); - GPU_vertbuf_init_build_on_device(dst_buffer, &format, subdiv_cache->num_subdiv_loops); - - GPUVertBuf *coarse_vbo = GPU_vertbuf_calloc(); - /* Dynamic as we upload and interpolate layers one at a time. */ - GPU_vertbuf_init_with_format_ex(coarse_vbo, &format, GPU_USAGE_DYNAMIC); - GPU_vertbuf_data_alloc(coarse_vbo, mr->loop_len); - - float(*coarse_vbo_data)[4] = static_cast<float(*)[4]>(GPU_vertbuf_get_data(coarse_vbo)); - - CustomData *cd_vdata = &mr->me->vdata; - float(*orco)[3] = static_cast<float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO)); - - if (mr->extract_type == MR_EXTRACT_MESH) { - const MLoop *mloop = mr->mloop; - const MPoly *mp = mr->mpoly; - - int ml_index = 0; - for (int i = 0; i < mr->poly_len; i++, mp++) { - const MLoop *ml = &mloop[mp->loopstart]; - - for (int j = 0; j < mp->totloop; j++, ml++, ml_index++) { - float *loop_orco = coarse_vbo_data[ml_index]; - copy_v3_v3(loop_orco, orco[ml->v]); - loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ - } - } - } - else { - BMIter iter; - BMFace *f; - BM_ITER_MESH (f, &iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter; - BMLoop *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - float *loop_orco = coarse_vbo_data[l_index]; - copy_v3_v3(loop_orco, orco[BM_elem_index_get(l_iter->v)]); - loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ - } while ((l_iter = l_iter->next) != l_first); - } - } - - draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, 0, false); - - GPU_vertbuf_discard(coarse_vbo); -} - constexpr MeshExtract create_extractor_orco() { MeshExtract extractor = {nullptr}; extractor.init = extract_orco_init; extractor.iter_poly_bm = extract_orco_iter_poly_bm; extractor.iter_poly_mesh = extract_orco_iter_poly_mesh; - extractor.init_subdiv = extract_orco_init_subdiv; extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_Orco_Data); extractor.use_threading = true; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc index ea46d9c4caa..f80b33e28f2 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc @@ -201,7 +201,7 @@ static GPUVertFormat *get_custom_normals_format() static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache, const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), + struct MeshBatchCache *cache, void *buffer, void *UNUSED(data)) { @@ -216,11 +216,25 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache, return; } - draw_subdiv_extract_pos_nor(subdiv_cache, vbo); + GPUVertBuf *orco_vbo = cache->final.buff.vbo.orco; + + if (orco_vbo) { + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex + * attributes. This is a substantial waste of video-ram and should be done another way. + * Unfortunately, at the time of writing, I did not found any other "non disruptive" + * alternative. */ + GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + } + GPU_vertbuf_init_build_on_device(orco_vbo, &format, subdiv_cache->num_subdiv_loops); + } + + draw_subdiv_extract_pos_nor(subdiv_cache, vbo, orco_vbo); if (subdiv_cache->use_custom_loop_normals) { Mesh *coarse_mesh = subdiv_cache->mesh; - float(*lnors)[3] = static_cast<float(*)[3]>( + const float(*lnors)[3] = static_cast<float(*)[3]>( CustomData_get_layer(&coarse_mesh->ldata, CD_NORMAL)); BLI_assert(lnors != nullptr); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc index 96595df9276..5658ed85c8b 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc @@ -42,8 +42,8 @@ static void extract_sculpt_data_init(const MeshRenderData *mr, CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; CustomData *cd_pdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata; - float *cd_mask = (float *)CustomData_get_layer(cd_vdata, CD_PAINT_MASK); - int *cd_face_set = (int *)CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); + const float *cd_mask = (const float *)CustomData_get_layer(cd_vdata, CD_PAINT_MASK); + const int *cd_face_set = (const int *)CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); GPU_vertbuf_init_with_format(vbo, format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -54,7 +54,7 @@ static void extract_sculpt_data_init(const MeshRenderData *mr, }; gpuSculptData *vbo_data = (gpuSculptData *)GPU_vertbuf_get_data(vbo); - MLoop *loops = (MLoop *)CustomData_get_layer(cd_ldata, CD_MLOOP); + const MLoop *loops = (const MLoop *)CustomData_get_layer(cd_ldata, CD_MLOOP); if (mr->extract_type == MR_EXTRACT_BMESH) { int cd_mask_ofs = CustomData_get_offset(cd_vdata, CD_PAINT_MASK); @@ -126,7 +126,7 @@ static void extract_sculpt_data_init_subdiv(const DRWSubdivCache *subdiv_cache, /* First, interpolate mask if available. */ GPUVertBuf *mask_vbo = nullptr; GPUVertBuf *subdiv_mask_vbo = nullptr; - float *cd_mask = (float *)CustomData_get_layer(cd_vdata, CD_PAINT_MASK); + const float *cd_mask = (const float *)CustomData_get_layer(cd_vdata, CD_PAINT_MASK); if (cd_mask) { GPUVertFormat mask_format = {0}; @@ -167,7 +167,7 @@ static void extract_sculpt_data_init_subdiv(const DRWSubdivCache *subdiv_cache, }; gpuFaceSet *face_sets = (gpuFaceSet *)GPU_vertbuf_get_data(face_set_vbo); - int *cd_face_set = (int *)CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); + const int *cd_face_set = (const int *)CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); GPUVertFormat *format = get_sculpt_data_format(); GPU_vertbuf_init_build_on_device(vbo, format, subdiv_cache->num_subdiv_loops); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc index 2a4a6a186be..f4c54b2f881 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc @@ -188,7 +188,7 @@ static void extract_vert_idx_init_subdiv(const DRWSubdivCache *subdiv_cache, { GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf); const DRWSubdivLooseGeom &loose_geom = subdiv_cache->loose_geom; - /* Each element points to an element in the ibo.points. */ + /* Each element points to an element in the `ibo.points`. */ draw_subdiv_init_origindex_buffer( vbo, (int32_t *)GPU_vertbuf_get_data(subdiv_cache->verts_orig_index), diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc index 25f78d68914..91cd675d32f 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc @@ -291,7 +291,8 @@ static void extract_tan_init_subdiv(const DRWSubdivCache *subdiv_cache, for (int i = 0; i < tan_len; i++) { float(*tan_data)[4] = (float(*)[4])GPU_vertbuf_get_data(coarse_vbo); const char *name = tangent_names[i]; - float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(&loop_data, CD_TANGENT, name); + const float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named( + &loop_data, CD_TANGENT, name); for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { copy_v3_v3(*tan_data, layer_data[ml_index]); (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? 1.0f : -1.0f; @@ -306,7 +307,7 @@ static void extract_tan_init_subdiv(const DRWSubdivCache *subdiv_cache, } if (use_orco_tan) { float(*tan_data)[4] = (float(*)[4])GPU_vertbuf_get_data(coarse_vbo); - float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0); + const float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0); for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { copy_v3_v3(*tan_data, layer_data[ml_index]); (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? 1.0f : -1.0f; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc index 5fb4b401ae3..2808a0a3a71 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc @@ -108,7 +108,8 @@ static void extract_uv_init(const MeshRenderData *mr, } } else { - MLoopUV *layer_data = (MLoopUV *)CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i); + const MLoopUV *layer_data = (const MLoopUV *)CustomData_get_layer_n( + cd_ldata, CD_MLOOPUV, i); for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, uv_data++, layer_data++) { memcpy(uv_data, layer_data->uv, sizeof(*uv_data)); } diff --git a/source/blender/draw/intern/shaders/common_globals_lib.glsl b/source/blender/draw/intern/shaders/common_globals_lib.glsl index 0460ba56e4c..a8931292064 100644 --- a/source/blender/draw/intern/shaders/common_globals_lib.glsl +++ b/source/blender/draw/intern/shaders/common_globals_lib.glsl @@ -110,7 +110,7 @@ layout(std140) uniform globalsBlock vec4 screenVecs[2]; vec4 sizeViewport; /* Inverted size in zw. */ - float sizePixel; /* This one is for dpi scaling */ + float sizePixel; /* This one is for DPI scaling. */ float pixelFac; /* To use with mul_project_m4_v3_zfac() */ float sizeObjectCenter; float sizeLightCenter; diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index ff52b483d77..dfa2f307800 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -212,8 +212,8 @@ void hair_get_pos_tan_binor_time(bool is_persp, wpos += wbinor * thick_time * scale; } else { - /* Note: Ensures 'hairThickTime' is initialised - - * avoids undefined behaviour on certain macOS configurations. */ + /* NOTE: Ensures 'hairThickTime' is initialized - + * avoids undefined behavior on certain macOS configurations. */ thick_time = 0.0; } } diff --git a/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl index 65cf4ebb90f..bf1f0f95787 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl @@ -89,6 +89,16 @@ layout(std430, binding = 8) writeonly buffer outputVertexData { PosNorLoop output_verts[]; }; +# if defined(ORCO_EVALUATION) +layout(std430, binding = 9) buffer src_extra_buffer +{ + float srcExtraVertexBuffer[]; +}; +layout(std430, binding = 10) writeonly buffer outputOrcoData +{ + vec4 output_orcos[]; +}; +# endif #endif vec2 read_vec2(int index) @@ -108,6 +118,17 @@ vec3 read_vec3(int index) return result; } +#if defined(ORCO_EVALUATION) +vec3 read_vec3_extra(int index) +{ + vec3 result; + result.x = srcExtraVertexBuffer[index * 3]; + result.y = srcExtraVertexBuffer[index * 3 + 1]; + result.z = srcExtraVertexBuffer[index * 3 + 2]; + return result; +} +#endif + OsdPatchArray GetPatchArray(int arrayIndex) { return patchArrayBuffer[arrayIndex]; @@ -290,6 +311,31 @@ void evaluate_patches_limits( dv += src_vertex * wDv[cv]; } } + +# if defined(ORCO_EVALUATION) +/* Evaluate the patches limits from the extra source vertex buffer. */ +void evaluate_patches_limits_extra(int patch_index, float u, float v, inout vec3 dst) +{ + OsdPatchCoord coord = GetPatchCoord(patch_index, u, v); + OsdPatchArray array = GetPatchArray(coord.arrayIndex); + OsdPatchParam param = GetPatchParam(coord.patchIndex); + + int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc; + + float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20]; + int nPoints = OsdEvaluatePatchBasis( + patchType, param, coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv); + + int indexBase = array.indexBase + array.stride * (coord.patchIndex - array.primitiveIdBase); + + for (int cv = 0; cv < nPoints; ++cv) { + int index = patchIndexBuffer[indexBase + cv]; + vec3 src_vertex = read_vec3_extra(index); + + dst += src_vertex * wP[cv]; + } +} +# endif #endif /* ------------------------------------------------------------------------------ @@ -407,6 +453,16 @@ void main() set_vertex_pos(vertex_data, pos); set_vertex_nor(vertex_data, nor, flag); output_verts[loop_index] = vertex_data; + +# if defined(ORCO_EVALUATION) + pos = vec3(0.0); + evaluate_patches_limits_extra(patch_co.patch_index, uv.x, uv.y, pos); + + /* Set w = 0.0 to indicate that this is not a generic attribute. + * See comments in `extract_mesh_vbo_orco.cc`. */ + vec4 orco_data = vec4(pos, 0.0); + output_orcos[loop_index] = orco_data; +# endif } } #endif diff --git a/source/blender/draw/intern/shaders/draw_object_infos_info.hh b/source/blender/draw/intern/shaders/draw_object_infos_info.hh index c74a043ec97..8fd55ea351f 100644 --- a/source/blender/draw/intern/shaders/draw_object_infos_info.hh +++ b/source/blender/draw/intern/shaders/draw_object_infos_info.hh @@ -10,3 +10,7 @@ GPU_SHADER_CREATE_INFO(draw_object_infos) GPU_SHADER_CREATE_INFO(draw_volume_infos) .typedef_source("draw_shader_shared.h") .uniform_buf(2, "VolumeInfos", "drw_volume", Frequency::BATCH); + +GPU_SHADER_CREATE_INFO(draw_curves_infos) + .typedef_source("draw_shader_shared.h") + .uniform_buf(2, "CurvesInfos", "drw_curves", Frequency::BATCH); |