diff options
author | Hans Goudey <h.goudey@me.com> | 2022-09-04 08:04:10 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-09-04 08:04:10 +0300 |
commit | 86e01811a5e97767f6112daa76efc380177486a0 (patch) | |
tree | f709405682cf65c0501d33158434e58a90481372 | |
parent | f31e0670bd4a260417c70d8ad9c16b57a514a252 (diff) | |
parent | 0ff920b777791d6dcc002257f437f86e2d14df01 (diff) |
Merge branch 'master' into refactor-mesh-remove-pointers
226 files changed, 8916 insertions, 4122 deletions
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index eb89e76dc75..ee284dd899a 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -296,7 +296,6 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): row.prop(cscene, "use_animated_seed", text="", icon='TIME') col = layout.column(align=True) - col.active = not (cscene.use_adaptive_sampling and cscene.use_preview_adaptive_sampling) col.prop(cscene, "sampling_pattern", text="Pattern") col = layout.column(align=True) @@ -305,7 +304,7 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): layout.separator() heading = layout.column(align=True, heading="Scrambling Distance") - heading.active = cscene.sampling_pattern != 'SOBOL_BURLEY' + heading.active = cscene.sampling_pattern != 'SOBOL' heading.prop(cscene, "auto_scrambling_distance", text="Automatic") heading.prop(cscene, "preview_scrambling_distance", text="Viewport") heading.prop(cscene, "scrambling_distance", text="Multiplier") diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index f32a810786d..6d84357b699 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -225,15 +225,18 @@ set(SRC_KERNEL_CAMERA_HEADERS ) set(SRC_KERNEL_FILM_HEADERS - film/accumulate.h film/adaptive_sampling.h - film/id_passes.h - film/passes.h + film/aov_passes.h + film/data_passes.h + film/denoising_passes.h + film/cryptomatte_passes.h + film/light_passes.h film/read.h - film/write_passes.h + film/write.h ) set(SRC_KERNEL_INTEGRATOR_HEADERS + integrator/displacement_shader.h integrator/init_from_bake.h integrator/init_from_camera.h integrator/intersect_closest.h @@ -245,7 +248,6 @@ set(SRC_KERNEL_INTEGRATOR_HEADERS integrator/path_state.h integrator/shade_background.h integrator/shade_light.h - integrator/shader_eval.h integrator/shade_shadow.h integrator/shade_surface.h integrator/shade_volume.h @@ -258,6 +260,8 @@ set(SRC_KERNEL_INTEGRATOR_HEADERS integrator/subsurface_disk.h integrator/subsurface.h integrator/subsurface_random_walk.h + integrator/surface_shader.h + integrator/volume_shader.h integrator/volume_stack.h ) diff --git a/intern/cycles/kernel/bake/bake.h b/intern/cycles/kernel/bake/bake.h index 9d53d71b431..384ca9168f0 100644 --- a/intern/cycles/kernel/bake/bake.h +++ b/intern/cycles/kernel/bake/bake.h @@ -4,7 +4,8 @@ #pragma once #include "kernel/camera/projection.h" -#include "kernel/integrator/shader_eval.h" +#include "kernel/integrator/displacement_shader.h" +#include "kernel/integrator/surface_shader.h" #include "kernel/geom/geom.h" @@ -25,7 +26,7 @@ ccl_device void kernel_displace_evaluate(KernelGlobals kg, /* Evaluate displacement shader. */ const float3 P = sd.P; - shader_eval_displacement(kg, INTEGRATOR_STATE_NULL, &sd); + displacement_shader_eval(kg, INTEGRATOR_STATE_NULL, &sd); float3 D = sd.P - P; object_inverse_dir_transform(kg, &sd, &D); @@ -64,10 +65,10 @@ ccl_device void kernel_background_evaluate(KernelGlobals kg, /* Evaluate shader. * This is being evaluated for all BSDFs, so path flag does not contain a specific type. */ const uint32_t path_flag = PATH_RAY_EMISSION; - shader_eval_surface<KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT & + surface_shader_eval<KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT & ~(KERNEL_FEATURE_NODE_RAYTRACE | KERNEL_FEATURE_NODE_LIGHT_PATH)>( kg, INTEGRATOR_STATE_NULL, &sd, NULL, path_flag); - Spectrum color = shader_background_eval(&sd); + Spectrum color = surface_shader_background(&sd); #ifdef __KERNEL_DEBUG_NAN__ if (!isfinite_safe(color)) { @@ -99,12 +100,12 @@ ccl_device void kernel_curve_shadow_transparency_evaluate( shader_setup_from_curve(kg, &sd, in.object, in.prim, __float_as_int(in.v), in.u); /* Evaluate transparency. */ - shader_eval_surface<KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW & + surface_shader_eval<KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW & ~(KERNEL_FEATURE_NODE_RAYTRACE | KERNEL_FEATURE_NODE_LIGHT_PATH)>( kg, INTEGRATOR_STATE_NULL, &sd, NULL, PATH_RAY_SHADOW); /* Write output. */ - output[offset] = clamp(average(shader_bsdf_transparency(kg, &sd)), 0.0f, 1.0f); + output[offset] = clamp(average(surface_shader_transparency(kg, &sd)), 0.0f, 1.0f); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/camera/camera.h b/intern/cycles/kernel/camera/camera.h index 926ccf7b86f..27876677281 100644 --- a/intern/cycles/kernel/camera/camera.h +++ b/intern/cycles/kernel/camera/camera.h @@ -45,7 +45,6 @@ ccl_device void camera_sample_perspective(KernelGlobals kg, float3 raster = make_float3(raster_x, raster_y, 0.0f); float3 Pcamera = transform_perspective(&rastertocamera, raster); -#ifdef __CAMERA_MOTION__ if (kernel_data.cam.have_perspective_motion) { /* TODO(sergey): Currently we interpolate projected coordinate which * gives nice looking result and which is simple, but is in fact a bit @@ -63,7 +62,6 @@ ccl_device void camera_sample_perspective(KernelGlobals kg, Pcamera = interp(Pcamera, Pcamera_post, (ray->time - 0.5f) * 2.0f); } } -#endif float3 P = zero_float3(); float3 D = Pcamera; @@ -87,14 +85,12 @@ ccl_device void camera_sample_perspective(KernelGlobals kg, /* transform ray from camera to world */ Transform cameratoworld = kernel_data.cam.cameratoworld; -#ifdef __CAMERA_MOTION__ if (kernel_data.cam.num_motion_steps) { transform_motion_array_interpolate(&cameratoworld, kernel_data_array(camera_motion), kernel_data.cam.num_motion_steps, ray->time); } -#endif P = transform_point(&cameratoworld, P); D = normalize(transform_direction(&cameratoworld, D)); @@ -159,7 +155,6 @@ ccl_device void camera_sample_perspective(KernelGlobals kg, #endif } -#ifdef __CAMERA_CLIPPING__ /* clipping */ float z_inv = 1.0f / normalize(Pcamera).z; float nearclip = kernel_data.cam.nearclip * z_inv; @@ -167,10 +162,6 @@ ccl_device void camera_sample_perspective(KernelGlobals kg, ray->dP += nearclip * ray->dD; ray->tmin = 0.0f; ray->tmax = kernel_data.cam.cliplength * z_inv; -#else - ray->tmin = 0.0f; - ray->tmax = FLT_MAX; -#endif } /* Orthographic Camera */ @@ -209,14 +200,12 @@ ccl_device void camera_sample_orthographic(KernelGlobals kg, /* transform ray from camera to world */ Transform cameratoworld = kernel_data.cam.cameratoworld; -#ifdef __CAMERA_MOTION__ if (kernel_data.cam.num_motion_steps) { transform_motion_array_interpolate(&cameratoworld, kernel_data_array(camera_motion), kernel_data.cam.num_motion_steps, ray->time); } -#endif ray->P = transform_point(&cameratoworld, P); ray->D = normalize(transform_direction(&cameratoworld, D)); @@ -231,22 +220,15 @@ ccl_device void camera_sample_orthographic(KernelGlobals kg, ray->dD = differential_zero_compact(); #endif -#ifdef __CAMERA_CLIPPING__ /* clipping */ ray->tmin = 0.0f; ray->tmax = kernel_data.cam.cliplength; -#else - ray->tmin = 0.0f; - ray->tmax = FLT_MAX; -#endif } /* Panorama Camera */ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, -#ifdef __CAMERA_MOTION__ ccl_global const DecomposedTransform *cam_motion, -#endif float raster_x, float raster_y, float lens_u, @@ -290,12 +272,10 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, /* transform ray from camera to world */ Transform cameratoworld = cam->cameratoworld; -#ifdef __CAMERA_MOTION__ if (cam->num_motion_steps) { transform_motion_array_interpolate( &cameratoworld, cam_motion, cam->num_motion_steps, ray->time); } -#endif /* Stereo transform */ bool use_stereo = cam->interocular_offset != 0.0f; @@ -348,17 +328,12 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, ray->dP = differential_make_compact(dP); #endif -#ifdef __CAMERA_CLIPPING__ /* clipping */ float nearclip = cam->nearclip; ray->P += nearclip * ray->D; ray->dP += nearclip * ray->dD; ray->tmin = 0.0f; ray->tmax = cam->cliplength; -#else - ray->tmin = 0.0f; - ray->tmax = FLT_MAX; -#endif } /* Common */ @@ -378,7 +353,6 @@ ccl_device_inline void camera_sample(KernelGlobals kg, float raster_x = x + lookup_table_read(kg, filter_u, filter_table_offset, FILTER_TABLE_SIZE); float raster_y = y + lookup_table_read(kg, filter_v, filter_table_offset, FILTER_TABLE_SIZE); -#ifdef __CAMERA_MOTION__ /* motion blur */ if (kernel_data.cam.shuttertime == -1.0f) { ray->time = 0.5f; @@ -416,7 +390,6 @@ ccl_device_inline void camera_sample(KernelGlobals kg, } } } -#endif /* sample */ if (kernel_data.cam.type == CAMERA_PERSPECTIVE) { @@ -426,12 +399,8 @@ ccl_device_inline void camera_sample(KernelGlobals kg, camera_sample_orthographic(kg, raster_x, raster_y, lens_u, lens_v, ray); } else { -#ifdef __CAMERA_MOTION__ ccl_global const DecomposedTransform *cam_motion = kernel_data_array(camera_motion); camera_sample_panorama(&kernel_data.cam, cam_motion, raster_x, raster_y, lens_u, lens_v, ray); -#else - camera_sample_panorama(&kernel_data.cam, raster_x, raster_y, lens_u, lens_v, ray); -#endif } } diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h index d6b7e7bfa88..02cf8bfe3e2 100644 --- a/intern/cycles/kernel/closure/bsdf.h +++ b/intern/cycles/kernel/closure/bsdf.h @@ -182,14 +182,12 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg, case CLOSURE_BSDF_HAIR_PRINCIPLED_ID: label = bsdf_principled_hair_sample(kg, sc, sd, randu, randv, eval, omega_in, pdf); break; -# ifdef __PRINCIPLED__ case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: label = bsdf_principled_diffuse_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID: label = bsdf_principled_sheen_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; -# endif /* __PRINCIPLED__ */ #endif default: label = LABEL_NONE; @@ -312,14 +310,12 @@ ccl_device_inline case CLOSURE_BSDF_HAIR_TRANSMISSION_ID: eval = bsdf_hair_transmission_eval_reflect(sc, sd->I, omega_in, pdf); break; -# ifdef __PRINCIPLED__ case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: eval = bsdf_principled_diffuse_eval_reflect(sc, sd->I, omega_in, pdf); break; case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID: eval = bsdf_principled_sheen_eval_reflect(sc, sd->I, omega_in, pdf); break; -# endif /* __PRINCIPLED__ */ #endif default: break; @@ -397,14 +393,12 @@ ccl_device_inline case CLOSURE_BSDF_HAIR_TRANSMISSION_ID: eval = bsdf_hair_transmission_eval_transmit(sc, sd->I, omega_in, pdf); break; -# ifdef __PRINCIPLED__ case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: eval = bsdf_principled_diffuse_eval_transmit(sc, sd->I, omega_in, pdf); break; case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID: eval = bsdf_principled_sheen_eval_transmit(sc, sd->I, omega_in, pdf); break; -# endif /* __PRINCIPLED__ */ #endif default: break; diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h index cdd4d128c1f..7131d9d8f38 100644 --- a/intern/cycles/kernel/closure/bssrdf.h +++ b/intern/cycles/kernel/closure/bssrdf.h @@ -312,7 +312,6 @@ ccl_device int bssrdf_setup(ccl_private ShaderData *sd, if (bssrdf_channels < SPECTRUM_CHANNELS) { /* Add diffuse BSDF if any radius too small. */ -#ifdef __PRINCIPLED__ if (bssrdf->roughness != FLT_MAX) { ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *)bsdf_alloc( sd, sizeof(PrincipledDiffuseBsdf), diffuse_weight); @@ -323,9 +322,7 @@ ccl_device int bssrdf_setup(ccl_private ShaderData *sd, flag |= bsdf_principled_diffuse_setup(bsdf, PRINCIPLED_DIFFUSE_LAMBERT); } } - else -#endif /* __PRINCIPLED__ */ - { + else { ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( sd, sizeof(DiffuseBsdf), diffuse_weight); diff --git a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h index 0e5f7b4a2fd..0d7c06f4fc6 100644 --- a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h +++ b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h @@ -34,7 +34,7 @@ # include "kernel/integrator/megakernel.h" # include "kernel/film/adaptive_sampling.h" -# include "kernel/film/id_passes.h" +# include "kernel/film/cryptomatte_passes.h" # include "kernel/film/read.h" # include "kernel/bake/bake.h" @@ -169,7 +169,7 @@ bool KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_convergence_check)( STUB_ASSERT(KERNEL_ARCH, adaptive_sampling_convergence_check); return false; #else - return kernel_adaptive_sampling_convergence_check( + return film_adaptive_sampling_convergence_check( kg, render_buffer, x, y, threshold, reset, offset, stride); #endif } @@ -185,7 +185,7 @@ void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_x)(const KernelGlobalsCP #ifdef KERNEL_STUB STUB_ASSERT(KERNEL_ARCH, adaptive_sampling_filter_x); #else - kernel_adaptive_sampling_filter_x(kg, render_buffer, y, start_x, width, offset, stride); + film_adaptive_sampling_filter_x(kg, render_buffer, y, start_x, width, offset, stride); #endif } @@ -200,7 +200,7 @@ void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_y)(const KernelGlobalsCP #ifdef KERNEL_STUB STUB_ASSERT(KERNEL_ARCH, adaptive_sampling_filter_y); #else - kernel_adaptive_sampling_filter_y(kg, render_buffer, x, start_y, height, offset, stride); + film_adaptive_sampling_filter_y(kg, render_buffer, x, start_y, height, offset, stride); #endif } @@ -215,7 +215,7 @@ void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const KernelGlobalsCPU * #ifdef KERNEL_STUB STUB_ASSERT(KERNEL_ARCH, cryptomatte_postprocess); #else - kernel_cryptomatte_post(kg, render_buffer, pixel_index); + film_cryptomatte_post(kg, render_buffer, pixel_index); #endif } diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index e1ab802aa80..d7d2000775f 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -526,7 +526,7 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) bool converged = true; if (x < sw && y < sh) { - converged = ccl_gpu_kernel_call(kernel_adaptive_sampling_convergence_check( + converged = ccl_gpu_kernel_call(film_adaptive_sampling_convergence_check( nullptr, render_buffer, sx + x, sy + y, threshold, reset, offset, stride)); } @@ -553,7 +553,7 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) if (y < sh) { ccl_gpu_kernel_call( - kernel_adaptive_sampling_filter_x(NULL, render_buffer, sy + y, sx, sw, offset, stride)); + film_adaptive_sampling_filter_x(NULL, render_buffer, sy + y, sx, sw, offset, stride)); } } ccl_gpu_kernel_postfix @@ -572,7 +572,7 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) if (x < sw) { ccl_gpu_kernel_call( - kernel_adaptive_sampling_filter_y(NULL, render_buffer, sx + x, sy, sh, offset, stride)); + film_adaptive_sampling_filter_y(NULL, render_buffer, sx + x, sy, sh, offset, stride)); } } ccl_gpu_kernel_postfix @@ -589,7 +589,7 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) const int pixel_index = ccl_gpu_global_id_x(); if (pixel_index < num_pixels) { - ccl_gpu_kernel_call(kernel_cryptomatte_post(nullptr, render_buffer, pixel_index)); + ccl_gpu_kernel_call(film_cryptomatte_post(nullptr, render_buffer, pixel_index)); } } ccl_gpu_kernel_postfix diff --git a/intern/cycles/kernel/film/adaptive_sampling.h b/intern/cycles/kernel/film/adaptive_sampling.h index 16867c39d99..d28c87747c3 100644 --- a/intern/cycles/kernel/film/adaptive_sampling.h +++ b/intern/cycles/kernel/film/adaptive_sampling.h @@ -3,15 +3,15 @@ #pragma once -#include "kernel/film/write_passes.h" +#include "kernel/film/write.h" CCL_NAMESPACE_BEGIN /* Check whether the pixel has converged and should not be sampled anymore. */ -ccl_device_forceinline bool kernel_need_sample_pixel(KernelGlobals kg, - ConstIntegratorState state, - ccl_global float *render_buffer) +ccl_device_forceinline bool film_need_sample_pixel(KernelGlobals kg, + ConstIntegratorState state, + ccl_global float *render_buffer) { if (kernel_data.film.pass_adaptive_aux_buffer == PASS_UNUSED) { return true; @@ -28,14 +28,14 @@ ccl_device_forceinline bool kernel_need_sample_pixel(KernelGlobals kg, /* Determines whether to continue sampling a given pixel or if it has sufficiently converged. */ -ccl_device bool kernel_adaptive_sampling_convergence_check(KernelGlobals kg, - ccl_global float *render_buffer, - int x, - int y, - float threshold, - bool reset, - int offset, - int stride) +ccl_device bool film_adaptive_sampling_convergence_check(KernelGlobals kg, + ccl_global float *render_buffer, + int x, + int y, + float threshold, + bool reset, + int offset, + int stride) { kernel_assert(kernel_data.film.pass_adaptive_aux_buffer != PASS_UNUSED); kernel_assert(kernel_data.film.pass_sample_count != PASS_UNUSED); @@ -78,13 +78,13 @@ ccl_device bool kernel_adaptive_sampling_convergence_check(KernelGlobals kg, /* This is a simple box filter in two passes. * When a pixel demands more adaptive samples, let its neighboring pixels draw more samples too. */ -ccl_device void kernel_adaptive_sampling_filter_x(KernelGlobals kg, - ccl_global float *render_buffer, - int y, - int start_x, - int width, - int offset, - int stride) +ccl_device void film_adaptive_sampling_filter_x(KernelGlobals kg, + ccl_global float *render_buffer, + int y, + int start_x, + int width, + int offset, + int stride) { kernel_assert(kernel_data.film.pass_adaptive_aux_buffer != PASS_UNUSED); @@ -111,13 +111,13 @@ ccl_device void kernel_adaptive_sampling_filter_x(KernelGlobals kg, } } -ccl_device void kernel_adaptive_sampling_filter_y(KernelGlobals kg, - ccl_global float *render_buffer, - int x, - int start_y, - int height, - int offset, - int stride) +ccl_device void film_adaptive_sampling_filter_y(KernelGlobals kg, + ccl_global float *render_buffer, + int x, + int start_y, + int height, + int offset, + int stride) { kernel_assert(kernel_data.film.pass_adaptive_aux_buffer != PASS_UNUSED); diff --git a/intern/cycles/kernel/film/aov_passes.h b/intern/cycles/kernel/film/aov_passes.h new file mode 100644 index 00000000000..3fbb250340f --- /dev/null +++ b/intern/cycles/kernel/film/aov_passes.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +#include "kernel/geom/geom.h" + +#include "kernel/film/write.h" + +CCL_NAMESPACE_BEGIN + +ccl_device_inline void film_write_aov_pass_value(KernelGlobals kg, + ConstIntegratorState state, + ccl_global float *ccl_restrict render_buffer, + const int aov_id, + const float value) +{ + ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer); + film_write_pass_float(buffer + kernel_data.film.pass_aov_value + aov_id, value); +} + +ccl_device_inline void film_write_aov_pass_color(KernelGlobals kg, + ConstIntegratorState state, + ccl_global float *ccl_restrict render_buffer, + const int aov_id, + const float3 color) +{ + ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer); + film_write_pass_float4(buffer + kernel_data.film.pass_aov_color + aov_id, + make_float4(color.x, color.y, color.z, 1.0f)); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/film/id_passes.h b/intern/cycles/kernel/film/cryptomatte_passes.h index c8317512bb2..4765777e7e2 100644 --- a/intern/cycles/kernel/film/id_passes.h +++ b/intern/cycles/kernel/film/cryptomatte_passes.h @@ -8,15 +8,15 @@ CCL_NAMESPACE_BEGIN /* Element of ID pass stored in the render buffers. * It is `float2` semantically, but it must be unaligned since the offset of ID passes in the * render buffers might not meet expected by compiler alignment. */ -typedef struct IDPassBufferElement { +typedef struct CryptoPassBufferElement { float x; float y; -} IDPassBufferElement; +} CryptoPassBufferElement; -ccl_device_inline void kernel_write_id_slots(ccl_global float *buffer, - int num_slots, - float id, - float weight) +ccl_device_inline void film_write_cryptomatte_slots(ccl_global float *buffer, + int num_slots, + float id, + float weight) { kernel_assert(id != ID_NONE); if (weight == 0.0f) { @@ -24,7 +24,7 @@ ccl_device_inline void kernel_write_id_slots(ccl_global float *buffer, } for (int slot = 0; slot < num_slots; slot++) { - ccl_global IDPassBufferElement *id_buffer = (ccl_global IDPassBufferElement *)buffer; + ccl_global CryptoPassBufferElement *id_buffer = (ccl_global CryptoPassBufferElement *)buffer; #ifdef __ATOMIC_PASS_WRITE__ /* If the loop reaches an empty slot, the ID isn't in any slot yet - so add it! */ if (id_buffer[slot].x == ID_NONE) { @@ -60,9 +60,9 @@ ccl_device_inline void kernel_write_id_slots(ccl_global float *buffer, } } -ccl_device_inline void kernel_sort_id_slots(ccl_global float *buffer, int num_slots) +ccl_device_inline void film_sort_cryptomatte_slots(ccl_global float *buffer, int num_slots) { - ccl_global IDPassBufferElement *id_buffer = (ccl_global IDPassBufferElement *)buffer; + ccl_global CryptoPassBufferElement *id_buffer = (ccl_global CryptoPassBufferElement *)buffer; for (int slot = 1; slot < num_slots; ++slot) { if (id_buffer[slot].x == ID_NONE) { return; @@ -70,7 +70,7 @@ ccl_device_inline void kernel_sort_id_slots(ccl_global float *buffer, int num_sl /* Since we're dealing with a tiny number of elements, insertion sort should be fine. */ int i = slot; while (i > 0 && id_buffer[i].y > id_buffer[i - 1].y) { - const IDPassBufferElement swap = id_buffer[i]; + const CryptoPassBufferElement swap = id_buffer[i]; id_buffer[i] = id_buffer[i - 1]; id_buffer[i - 1] = swap; --i; @@ -79,15 +79,15 @@ ccl_device_inline void kernel_sort_id_slots(ccl_global float *buffer, int num_sl } /* post-sorting for Cryptomatte */ -ccl_device_inline void kernel_cryptomatte_post(KernelGlobals kg, - ccl_global float *render_buffer, - int pixel_index) +ccl_device_inline void film_cryptomatte_post(KernelGlobals kg, + ccl_global float *render_buffer, + int pixel_index) { const int pass_stride = kernel_data.film.pass_stride; const uint64_t render_buffer_offset = (uint64_t)pixel_index * pass_stride; ccl_global float *cryptomatte_buffer = render_buffer + render_buffer_offset + kernel_data.film.pass_cryptomatte; - kernel_sort_id_slots(cryptomatte_buffer, 2 * kernel_data.film.cryptomatte_depth); + film_sort_cryptomatte_slots(cryptomatte_buffer, 2 * kernel_data.film.cryptomatte_depth); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/film/data_passes.h b/intern/cycles/kernel/film/data_passes.h new file mode 100644 index 00000000000..d14b3cea989 --- /dev/null +++ b/intern/cycles/kernel/film/data_passes.h @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +#include "kernel/geom/geom.h" + +#include "kernel/film/cryptomatte_passes.h" +#include "kernel/film/write.h" + +CCL_NAMESPACE_BEGIN + +ccl_device_inline size_t film_write_cryptomatte_pass(ccl_global float *ccl_restrict buffer, + size_t depth, + float id, + float matte_weight) +{ + film_write_cryptomatte_slots(buffer, depth * 2, id, matte_weight); + return depth * 4; +} + +ccl_device_inline void film_write_data_passes(KernelGlobals kg, + IntegratorState state, + ccl_private const ShaderData *sd, + ccl_global float *ccl_restrict render_buffer) +{ +#ifdef __PASSES__ + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + + if (!(path_flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { + return; + } + + const int flag = kernel_data.film.pass_flag; + + if (!(flag & PASS_ANY)) { + return; + } + + ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer); + + if (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)) { + if (!(sd->flag & SD_TRANSPARENT) || kernel_data.film.pass_alpha_threshold == 0.0f || + average(surface_shader_alpha(kg, sd)) >= kernel_data.film.pass_alpha_threshold) { + if (INTEGRATOR_STATE(state, path, sample) == 0) { + if (flag & PASSMASK(DEPTH)) { + const float depth = camera_z_depth(kg, sd->P); + film_write_pass_float(buffer + kernel_data.film.pass_depth, depth); + } + if (flag & PASSMASK(OBJECT_ID)) { + const float id = object_pass_id(kg, sd->object); + film_write_pass_float(buffer + kernel_data.film.pass_object_id, id); + } + if (flag & PASSMASK(MATERIAL_ID)) { + const float id = shader_pass_id(kg, sd); + film_write_pass_float(buffer + kernel_data.film.pass_material_id, id); + } + if (flag & PASSMASK(POSITION)) { + const float3 position = sd->P; + film_write_pass_float3(buffer + kernel_data.film.pass_position, position); + } + } + + if (flag & PASSMASK(NORMAL)) { + const float3 normal = surface_shader_average_normal(kg, sd); + film_write_pass_float3(buffer + kernel_data.film.pass_normal, normal); + } + if (flag & PASSMASK(ROUGHNESS)) { + const float roughness = surface_shader_average_roughness(sd); + film_write_pass_float(buffer + kernel_data.film.pass_roughness, roughness); + } + if (flag & PASSMASK(UV)) { + const float3 uv = primitive_uv(kg, sd); + film_write_pass_float3(buffer + kernel_data.film.pass_uv, uv); + } + if (flag & PASSMASK(MOTION)) { + const float4 speed = primitive_motion_vector(kg, sd); + film_write_pass_float4(buffer + kernel_data.film.pass_motion, speed); + film_write_pass_float(buffer + kernel_data.film.pass_motion_weight, 1.0f); + } + + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_SINGLE_PASS_DONE; + } + } + + if (kernel_data.film.cryptomatte_passes) { + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); + const float matte_weight = average(throughput) * + (1.0f - average(surface_shader_transparency(kg, sd))); + if (matte_weight > 0.0f) { + ccl_global float *cryptomatte_buffer = buffer + kernel_data.film.pass_cryptomatte; + if (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) { + const float id = object_cryptomatte_id(kg, sd->object); + cryptomatte_buffer += film_write_cryptomatte_pass( + cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight); + } + if (kernel_data.film.cryptomatte_passes & CRYPT_MATERIAL) { + const float id = kernel_data_fetch(shaders, (sd->shader & SHADER_MASK)).cryptomatte_id; + cryptomatte_buffer += film_write_cryptomatte_pass( + cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight); + } + if (kernel_data.film.cryptomatte_passes & CRYPT_ASSET) { + const float id = object_cryptomatte_asset_id(kg, sd->object); + cryptomatte_buffer += film_write_cryptomatte_pass( + cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight); + } + } + } + + if (flag & PASSMASK(DIFFUSE_COLOR)) { + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); + film_write_pass_spectrum(buffer + kernel_data.film.pass_diffuse_color, + surface_shader_diffuse(kg, sd) * throughput); + } + if (flag & PASSMASK(GLOSSY_COLOR)) { + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); + film_write_pass_spectrum(buffer + kernel_data.film.pass_glossy_color, + surface_shader_glossy(kg, sd) * throughput); + } + if (flag & PASSMASK(TRANSMISSION_COLOR)) { + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); + film_write_pass_spectrum(buffer + kernel_data.film.pass_transmission_color, + surface_shader_transmission(kg, sd) * throughput); + } + if (flag & PASSMASK(MIST)) { + /* Bring depth into 0..1 range. */ + const float mist_start = kernel_data.film.mist_start; + const float mist_inv_depth = kernel_data.film.mist_inv_depth; + + const float depth = camera_distance(kg, sd->P); + float mist = saturatef((depth - mist_start) * mist_inv_depth); + + /* Falloff */ + const float mist_falloff = kernel_data.film.mist_falloff; + + if (mist_falloff == 1.0f) + ; + else if (mist_falloff == 2.0f) + mist = mist * mist; + else if (mist_falloff == 0.5f) + mist = sqrtf(mist); + else + mist = powf(mist, mist_falloff); + + /* Modulate by transparency */ + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); + const Spectrum alpha = surface_shader_alpha(kg, sd); + const float mist_output = (1.0f - mist) * average(throughput * alpha); + + /* Note that the final value in the render buffer we want is 1 - mist_output, + * to avoid having to tracking this in the Integrator state we do the negation + * after rendering. */ + film_write_pass_float(buffer + kernel_data.film.pass_mist, mist_output); + } +#endif +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/film/denoising_passes.h b/intern/cycles/kernel/film/denoising_passes.h new file mode 100644 index 00000000000..1517e8bad56 --- /dev/null +++ b/intern/cycles/kernel/film/denoising_passes.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +#include "kernel/geom/geom.h" + +#include "kernel/film/write.h" + +CCL_NAMESPACE_BEGIN + +#ifdef __DENOISING_FEATURES__ +ccl_device_forceinline void film_write_denoising_features_surface(KernelGlobals kg, + IntegratorState state, + ccl_private const ShaderData *sd, + ccl_global float *ccl_restrict + render_buffer) +{ + if (!(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_DENOISING_FEATURES)) { + return; + } + + /* Skip implicitly transparent surfaces. */ + if (sd->flag & SD_HAS_ONLY_VOLUME) { + return; + } + + ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer); + + if (kernel_data.film.pass_denoising_depth != PASS_UNUSED) { + const Spectrum denoising_feature_throughput = INTEGRATOR_STATE( + state, path, denoising_feature_throughput); + const float denoising_depth = ensure_finite(average(denoising_feature_throughput) * + sd->ray_length); + film_write_pass_float(buffer + kernel_data.film.pass_denoising_depth, denoising_depth); + } + + float3 normal = zero_float3(); + Spectrum diffuse_albedo = zero_spectrum(); + Spectrum specular_albedo = zero_spectrum(); + float sum_weight = 0.0f, sum_nonspecular_weight = 0.0f; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (!CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + continue; + } + + /* All closures contribute to the normal feature, but only diffuse-like ones to the albedo. */ + normal += sc->N * sc->sample_weight; + sum_weight += sc->sample_weight; + + Spectrum closure_albedo = sc->weight; + /* Closures that include a Fresnel term typically have weights close to 1 even though their + * actual contribution is significantly lower. + * To account for this, we scale their weight by the average fresnel factor (the same is also + * done for the sample weight in the BSDF setup, so we don't need to scale that here). */ + if (CLOSURE_IS_BSDF_MICROFACET_FRESNEL(sc->type)) { + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc; + closure_albedo *= bsdf->extra->fresnel_color; + } + else if (sc->type == CLOSURE_BSDF_PRINCIPLED_SHEEN_ID) { + ccl_private PrincipledSheenBsdf *bsdf = (ccl_private PrincipledSheenBsdf *)sc; + closure_albedo *= bsdf->avg_value; + } + else if (sc->type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID) { + closure_albedo *= bsdf_principled_hair_albedo(sc); + } + else if (sc->type == CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID) { + /* BSSRDF already accounts for weight, retro-reflection would double up. */ + ccl_private const PrincipledDiffuseBsdf *bsdf = (ccl_private const PrincipledDiffuseBsdf *) + sc; + if (bsdf->components == PRINCIPLED_DIFFUSE_RETRO_REFLECTION) { + continue; + } + } + + if (bsdf_get_specular_roughness_squared(sc) > sqr(0.075f)) { + diffuse_albedo += closure_albedo; + sum_nonspecular_weight += sc->sample_weight; + } + else { + specular_albedo += closure_albedo; + } + } + + /* Wait for next bounce if 75% or more sample weight belongs to specular-like closures. */ + if ((sum_weight == 0.0f) || (sum_nonspecular_weight * 4.0f > sum_weight)) { + if (sum_weight != 0.0f) { + normal /= sum_weight; + } + + if (kernel_data.film.pass_denoising_normal != PASS_UNUSED) { + /* Transform normal into camera space. */ + const Transform worldtocamera = kernel_data.cam.worldtocamera; + normal = transform_direction(&worldtocamera, normal); + + const float3 denoising_normal = ensure_finite(normal); + film_write_pass_float3(buffer + kernel_data.film.pass_denoising_normal, denoising_normal); + } + + if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { + const Spectrum denoising_feature_throughput = INTEGRATOR_STATE( + state, path, denoising_feature_throughput); + const Spectrum denoising_albedo = ensure_finite(denoising_feature_throughput * + diffuse_albedo); + film_write_pass_spectrum(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); + } + + INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_DENOISING_FEATURES; + } + else { + INTEGRATOR_STATE_WRITE(state, path, denoising_feature_throughput) *= specular_albedo; + } +} + +ccl_device_forceinline void film_write_denoising_features_volume(KernelGlobals kg, + IntegratorState state, + const Spectrum albedo, + const bool scatter, + ccl_global float *ccl_restrict + render_buffer) +{ + ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer); + const Spectrum denoising_feature_throughput = INTEGRATOR_STATE( + state, path, denoising_feature_throughput); + + if (scatter && kernel_data.film.pass_denoising_normal != PASS_UNUSED) { + /* Assume scatter is sufficiently diffuse to stop writing denoising features. */ + INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_DENOISING_FEATURES; + + /* Write view direction as normal. */ + const float3 denoising_normal = make_float3(0.0f, 0.0f, -1.0f); + film_write_pass_float3(buffer + kernel_data.film.pass_denoising_normal, denoising_normal); + } + + if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { + /* Write albedo. */ + const Spectrum denoising_albedo = ensure_finite(denoising_feature_throughput * albedo); + film_write_pass_spectrum(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); + } +} +#endif /* __DENOISING_FEATURES__ */ + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/film/accumulate.h b/intern/cycles/kernel/film/light_passes.h index 97ec915a8ad..b45b5305119 100644 --- a/intern/cycles/kernel/film/accumulate.h +++ b/intern/cycles/kernel/film/light_passes.h @@ -4,7 +4,7 @@ #pragma once #include "kernel/film/adaptive_sampling.h" -#include "kernel/film/write_passes.h" +#include "kernel/film/write.h" #include "kernel/integrator/shadow_catcher.h" @@ -95,9 +95,7 @@ ccl_device_inline Spectrum bsdf_eval_pass_glossy_weight(ccl_private const BsdfEv * to render buffers instead of using per-thread memory, and to avoid the * impact of clamping on other contributions. */ -ccl_device_forceinline void kernel_accum_clamp(KernelGlobals kg, - ccl_private Spectrum *L, - int bounce) +ccl_device_forceinline void film_clamp_light(KernelGlobals kg, ccl_private Spectrum *L, int bounce) { #ifdef __KERNEL_DEBUG_NAN__ if (!isfinite_safe(*L)) { @@ -123,59 +121,49 @@ ccl_device_forceinline void kernel_accum_clamp(KernelGlobals kg, * Pass accumulation utilities. */ -/* Get pointer to pixel in render buffer. */ -ccl_device_forceinline ccl_global float *kernel_accum_pixel_render_buffer( - KernelGlobals kg, ConstIntegratorState state, ccl_global float *ccl_restrict render_buffer) -{ - const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); - const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * - kernel_data.film.pass_stride; - return render_buffer + render_buffer_offset; -} - /* -------------------------------------------------------------------- * Adaptive sampling. */ -ccl_device_inline int kernel_accum_sample(KernelGlobals kg, - ConstIntegratorState state, - ccl_global float *ccl_restrict render_buffer, - int sample, - int sample_offset) +ccl_device_inline int film_write_sample(KernelGlobals kg, + ConstIntegratorState state, + ccl_global float *ccl_restrict render_buffer, + int sample, + int sample_offset) { if (kernel_data.film.pass_sample_count == PASS_UNUSED) { return sample; } - ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); + ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer); return atomic_fetch_and_add_uint32( (ccl_global uint *)(buffer) + kernel_data.film.pass_sample_count, 1) + sample_offset; } -ccl_device void kernel_accum_adaptive_buffer(KernelGlobals kg, - const int sample, - const Spectrum contribution, - ccl_global float *ccl_restrict buffer) +ccl_device void film_write_adaptive_buffer(KernelGlobals kg, + const int sample, + const Spectrum contribution, + ccl_global float *ccl_restrict buffer) { - /* Adaptive Sampling. Fill the additional buffer with the odd samples and calculate our stopping - * criteria. This is the heuristic from "A hierarchical automatic stopping condition for Monte - * Carlo global illumination" except that here it is applied per pixel and not in hierarchical - * tiles. */ + /* Adaptive Sampling. Fill the additional buffer with only one half of the samples and + * calculate our stopping criteria. This is the heuristic from "A hierarchical automatic + * stopping condition for Monte Carlo global illumination" except that here it is applied + * per pixel and not in hierarchical tiles. */ if (kernel_data.film.pass_adaptive_aux_buffer == PASS_UNUSED) { return; } - if (sample_is_even(kernel_data.integrator.sampling_pattern, sample)) { + if (sample_is_class_A(kernel_data.integrator.sampling_pattern, sample)) { const float3 contribution_rgb = spectrum_to_rgb(contribution); - kernel_write_pass_float4(buffer + kernel_data.film.pass_adaptive_aux_buffer, - make_float4(contribution_rgb.x * 2.0f, - contribution_rgb.y * 2.0f, - contribution_rgb.z * 2.0f, - 0.0f)); + film_write_pass_float4(buffer + kernel_data.film.pass_adaptive_aux_buffer, + make_float4(contribution_rgb.x * 2.0f, + contribution_rgb.y * 2.0f, + contribution_rgb.z * 2.0f, + 0.0f)); } } @@ -190,10 +178,10 @@ ccl_device void kernel_accum_adaptive_buffer(KernelGlobals kg, * Returns truth if the contribution is fully handled here and is not to be added to the other * passes (like combined, adaptive sampling). */ -ccl_device bool kernel_accum_shadow_catcher(KernelGlobals kg, - const uint32_t path_flag, - const Spectrum contribution, - ccl_global float *ccl_restrict buffer) +ccl_device bool film_write_shadow_catcher(KernelGlobals kg, + const uint32_t path_flag, + const Spectrum contribution, + ccl_global float *ccl_restrict buffer) { if (!kernel_data.integrator.has_shadow_catcher) { return false; @@ -204,7 +192,7 @@ ccl_device bool kernel_accum_shadow_catcher(KernelGlobals kg, /* Matte pass. */ if (kernel_shadow_catcher_is_matte_path(path_flag)) { - kernel_write_pass_spectrum(buffer + kernel_data.film.pass_shadow_catcher_matte, contribution); + film_write_pass_spectrum(buffer + kernel_data.film.pass_shadow_catcher_matte, contribution); /* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive * sampling is based on how noisy the combined pass is as if there were no catchers in the * scene. */ @@ -212,18 +200,18 @@ ccl_device bool kernel_accum_shadow_catcher(KernelGlobals kg, /* Shadow catcher pass. */ if (kernel_shadow_catcher_is_object_pass(path_flag)) { - kernel_write_pass_spectrum(buffer + kernel_data.film.pass_shadow_catcher, contribution); + film_write_pass_spectrum(buffer + kernel_data.film.pass_shadow_catcher, contribution); return true; } return false; } -ccl_device bool kernel_accum_shadow_catcher_transparent(KernelGlobals kg, - const uint32_t path_flag, - const Spectrum contribution, - const float transparent, - ccl_global float *ccl_restrict buffer) +ccl_device bool film_write_shadow_catcher_transparent(KernelGlobals kg, + const uint32_t path_flag, + const Spectrum contribution, + const float transparent, + ccl_global float *ccl_restrict buffer) { if (!kernel_data.integrator.has_shadow_catcher) { return false; @@ -240,7 +228,7 @@ ccl_device bool kernel_accum_shadow_catcher_transparent(KernelGlobals kg, if (kernel_shadow_catcher_is_matte_path(path_flag)) { const float3 contribution_rgb = spectrum_to_rgb(contribution); - kernel_write_pass_float4( + film_write_pass_float4( buffer + kernel_data.film.pass_shadow_catcher_matte, make_float4(contribution_rgb.x, contribution_rgb.y, contribution_rgb.z, transparent)); /* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive @@ -253,17 +241,17 @@ ccl_device bool kernel_accum_shadow_catcher_transparent(KernelGlobals kg, /* NOTE: The transparency of the shadow catcher pass is ignored. It is not needed for the * calculation and the alpha channel of the pass contains numbers of samples contributed to a * pixel of the pass. */ - kernel_write_pass_spectrum(buffer + kernel_data.film.pass_shadow_catcher, contribution); + film_write_pass_spectrum(buffer + kernel_data.film.pass_shadow_catcher, contribution); return true; } return false; } -ccl_device void kernel_accum_shadow_catcher_transparent_only(KernelGlobals kg, - const uint32_t path_flag, - const float transparent, - ccl_global float *ccl_restrict buffer) +ccl_device void film_write_shadow_catcher_transparent_only(KernelGlobals kg, + const uint32_t path_flag, + const float transparent, + ccl_global float *ccl_restrict buffer) { if (!kernel_data.integrator.has_shadow_catcher) { return; @@ -273,10 +261,29 @@ ccl_device void kernel_accum_shadow_catcher_transparent_only(KernelGlobals kg, /* Matte pass. */ if (kernel_shadow_catcher_is_matte_path(path_flag)) { - kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, transparent); + film_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, transparent); } } +/* Write shadow catcher passes on a bounce from the shadow catcher object. */ +ccl_device_forceinline void film_write_shadow_catcher_bounce_data( + KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer) +{ + kernel_assert(kernel_data.film.pass_shadow_catcher_sample_count != PASS_UNUSED); + kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); + + ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer); + + /* Count sample for the shadow catcher object. */ + film_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_sample_count, 1.0f); + + /* Since the split is done, the sample does not contribute to the matte, so accumulate it as + * transparency to the matte. */ + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); + film_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, + average(throughput)); +} + #endif /* __SHADOW_CATCHER__ */ /* -------------------------------------------------------------------- @@ -284,36 +291,35 @@ ccl_device void kernel_accum_shadow_catcher_transparent_only(KernelGlobals kg, */ /* Write combined pass. */ -ccl_device_inline void kernel_accum_combined_pass(KernelGlobals kg, - const uint32_t path_flag, - const int sample, - const Spectrum contribution, - ccl_global float *ccl_restrict buffer) +ccl_device_inline void film_write_combined_pass(KernelGlobals kg, + const uint32_t path_flag, + const int sample, + const Spectrum contribution, + ccl_global float *ccl_restrict buffer) { #ifdef __SHADOW_CATCHER__ - if (kernel_accum_shadow_catcher(kg, path_flag, contribution, buffer)) { + if (film_write_shadow_catcher(kg, path_flag, contribution, buffer)) { return; } #endif if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { - kernel_write_pass_spectrum(buffer + kernel_data.film.pass_combined, contribution); + film_write_pass_spectrum(buffer + kernel_data.film.pass_combined, contribution); } - kernel_accum_adaptive_buffer(kg, sample, contribution, buffer); + film_write_adaptive_buffer(kg, sample, contribution, buffer); } /* Write combined pass with transparency. */ -ccl_device_inline void kernel_accum_combined_transparent_pass(KernelGlobals kg, - const uint32_t path_flag, - const int sample, - const Spectrum contribution, - const float transparent, - ccl_global float *ccl_restrict - buffer) +ccl_device_inline void film_write_combined_transparent_pass(KernelGlobals kg, + const uint32_t path_flag, + const int sample, + const Spectrum contribution, + const float transparent, + ccl_global float *ccl_restrict buffer) { #ifdef __SHADOW_CATCHER__ - if (kernel_accum_shadow_catcher_transparent(kg, path_flag, contribution, transparent, buffer)) { + if (film_write_shadow_catcher_transparent(kg, path_flag, contribution, transparent, buffer)) { return; } #endif @@ -321,16 +327,16 @@ ccl_device_inline void kernel_accum_combined_transparent_pass(KernelGlobals kg, if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { const float3 contribution_rgb = spectrum_to_rgb(contribution); - kernel_write_pass_float4( + film_write_pass_float4( buffer + kernel_data.film.pass_combined, make_float4(contribution_rgb.x, contribution_rgb.y, contribution_rgb.z, transparent)); } - kernel_accum_adaptive_buffer(kg, sample, contribution, buffer); + film_write_adaptive_buffer(kg, sample, contribution, buffer); } /* Write background or emission to appropriate pass. */ -ccl_device_inline void kernel_accum_emission_or_background_pass( +ccl_device_inline void film_write_emission_or_background_pass( KernelGlobals kg, ConstIntegratorState state, Spectrum contribution, @@ -353,15 +359,14 @@ ccl_device_inline void kernel_accum_emission_or_background_pass( const Spectrum denoising_feature_throughput = INTEGRATOR_STATE( state, path, denoising_feature_throughput); const Spectrum denoising_albedo = denoising_feature_throughput * contribution; - kernel_write_pass_spectrum(buffer + kernel_data.film.pass_denoising_albedo, - denoising_albedo); + film_write_pass_spectrum(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); } } # endif /* __DENOISING_FEATURES__ */ if (lightgroup != LIGHTGROUP_NONE && kernel_data.film.pass_lightgroup != PASS_UNUSED) { - kernel_write_pass_spectrum(buffer + kernel_data.film.pass_lightgroup + 3 * lightgroup, - contribution); + film_write_pass_spectrum(buffer + kernel_data.film.pass_lightgroup + 3 * lightgroup, + contribution); } if (!(path_flag & PATH_RAY_ANY_PASS)) { @@ -385,7 +390,7 @@ ccl_device_inline void kernel_accum_emission_or_background_pass( kernel_data.film.pass_glossy_direct : kernel_data.film.pass_glossy_indirect); if (glossy_pass_offset != PASS_UNUSED) { - kernel_write_pass_spectrum(buffer + glossy_pass_offset, glossy_weight * contribution); + film_write_pass_spectrum(buffer + glossy_pass_offset, glossy_weight * contribution); } /* Transmission */ @@ -397,8 +402,8 @@ ccl_device_inline void kernel_accum_emission_or_background_pass( /* Transmission is what remains if not diffuse and glossy, not stored explicitly to save * GPU memory. */ const Spectrum transmission_weight = one_spectrum() - diffuse_weight - glossy_weight; - kernel_write_pass_spectrum(buffer + transmission_pass_offset, - transmission_weight * contribution); + film_write_pass_spectrum(buffer + transmission_pass_offset, + transmission_weight * contribution); } /* Reconstruct diffuse subset of throughput. */ @@ -419,19 +424,19 @@ ccl_device_inline void kernel_accum_emission_or_background_pass( /* Single write call for GPU coherence. */ if (pass_offset != PASS_UNUSED) { - kernel_write_pass_spectrum(buffer + pass_offset, contribution); + film_write_pass_spectrum(buffer + pass_offset, contribution); } #endif /* __PASSES__ */ } /* Write light contribution to render buffer. */ -ccl_device_inline void kernel_accum_light(KernelGlobals kg, - ConstIntegratorShadowState state, - ccl_global float *ccl_restrict render_buffer) +ccl_device_inline void film_write_direct_light(KernelGlobals kg, + ConstIntegratorShadowState state, + ccl_global float *ccl_restrict render_buffer) { /* The throughput for shadow paths already contains the light shader evaluation. */ Spectrum contribution = INTEGRATOR_STATE(state, shadow_path, throughput); - kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, shadow_path, bounce)); + film_clamp_light(kg, &contribution, INTEGRATOR_STATE(state, shadow_path, bounce)); const uint32_t render_pixel_index = INTEGRATOR_STATE(state, shadow_path, render_pixel_index); const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * @@ -444,17 +449,17 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, /* Ambient occlusion. */ if (path_flag & PATH_RAY_SHADOW_FOR_AO) { if ((kernel_data.kernel_features & KERNEL_FEATURE_AO_PASS) && (path_flag & PATH_RAY_CAMERA)) { - kernel_write_pass_spectrum(buffer + kernel_data.film.pass_ao, contribution); + film_write_pass_spectrum(buffer + kernel_data.film.pass_ao, contribution); } if (kernel_data.kernel_features & KERNEL_FEATURE_AO_ADDITIVE) { const Spectrum ao_weight = INTEGRATOR_STATE(state, shadow_path, unshadowed_throughput); - kernel_accum_combined_pass(kg, path_flag, sample, contribution * ao_weight, buffer); + film_write_combined_pass(kg, path_flag, sample, contribution * ao_weight, buffer); } return; } /* Direct light shadow. */ - kernel_accum_combined_pass(kg, path_flag, sample, contribution, buffer); + film_write_combined_pass(kg, path_flag, sample, contribution, buffer); #ifdef __PASSES__ if (kernel_data.film.light_pass_flag & PASS_ANY) { @@ -469,8 +474,8 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, /* Write lightgroup pass. LIGHTGROUP_NONE is ~0 so decode from unsigned to signed */ const int lightgroup = (int)(INTEGRATOR_STATE(state, shadow_path, lightgroup)) - 1; if (lightgroup != LIGHTGROUP_NONE && kernel_data.film.pass_lightgroup != PASS_UNUSED) { - kernel_write_pass_spectrum(buffer + kernel_data.film.pass_lightgroup + 3 * lightgroup, - contribution); + film_write_pass_spectrum(buffer + kernel_data.film.pass_lightgroup + 3 * lightgroup, + contribution); } if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { @@ -486,7 +491,7 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, kernel_data.film.pass_glossy_direct : kernel_data.film.pass_glossy_indirect); if (glossy_pass_offset != PASS_UNUSED) { - kernel_write_pass_spectrum(buffer + glossy_pass_offset, glossy_weight * contribution); + film_write_pass_spectrum(buffer + glossy_pass_offset, glossy_weight * contribution); } /* Transmission */ @@ -498,8 +503,8 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, /* Transmission is what remains if not diffuse and glossy, not stored explicitly to save * GPU memory. */ const Spectrum transmission_weight = one_spectrum() - diffuse_weight - glossy_weight; - kernel_write_pass_spectrum(buffer + transmission_pass_offset, - transmission_weight * contribution); + film_write_pass_spectrum(buffer + transmission_pass_offset, + transmission_weight * contribution); } /* Reconstruct diffuse subset of throughput. */ @@ -519,7 +524,7 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, /* Single write call for GPU coherence. */ if (pass_offset != PASS_UNUSED) { - kernel_write_pass_spectrum(buffer + pass_offset, contribution); + film_write_pass_spectrum(buffer + pass_offset, contribution); } } @@ -531,7 +536,7 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, const Spectrum shadowed_throughput = INTEGRATOR_STATE(state, shadow_path, throughput); const Spectrum shadow = safe_divide(shadowed_throughput, unshadowed_throughput) * kernel_data.film.pass_shadow_scale; - kernel_write_pass_spectrum(buffer + kernel_data.film.pass_shadow, shadow); + film_write_pass_spectrum(buffer + kernel_data.film.pass_shadow, shadow); } } #endif @@ -542,78 +547,96 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, * Note that we accumulate transparency = 1 - alpha in the render buffer. * Otherwise we'd have to write alpha on path termination, which happens * in many places. */ -ccl_device_inline void kernel_accum_transparent(KernelGlobals kg, - ConstIntegratorState state, - const uint32_t path_flag, - const float transparent, - ccl_global float *ccl_restrict buffer) +ccl_device_inline void film_write_transparent(KernelGlobals kg, + ConstIntegratorState state, + const uint32_t path_flag, + const float transparent, + ccl_global float *ccl_restrict buffer) { if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { - kernel_write_pass_float(buffer + kernel_data.film.pass_combined + 3, transparent); + film_write_pass_float(buffer + kernel_data.film.pass_combined + 3, transparent); } - kernel_accum_shadow_catcher_transparent_only(kg, path_flag, transparent, buffer); + film_write_shadow_catcher_transparent_only(kg, path_flag, transparent, buffer); } /* Write holdout to render buffer. */ -ccl_device_inline void kernel_accum_holdout(KernelGlobals kg, - ConstIntegratorState state, - const uint32_t path_flag, - const float transparent, - ccl_global float *ccl_restrict render_buffer) +ccl_device_inline void film_write_holdout(KernelGlobals kg, + ConstIntegratorState state, + const uint32_t path_flag, + const float transparent, + ccl_global float *ccl_restrict render_buffer) { - ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); - kernel_accum_transparent(kg, state, path_flag, transparent, buffer); + ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer); + film_write_transparent(kg, state, path_flag, transparent, buffer); } /* Write background contribution to render buffer. * - * Includes transparency, matching kernel_accum_transparent. */ -ccl_device_inline void kernel_accum_background(KernelGlobals kg, - ConstIntegratorState state, - const Spectrum L, - const float transparent, - const bool is_transparent_background_ray, - ccl_global float *ccl_restrict render_buffer) + * Includes transparency, matching film_write_transparent. */ +ccl_device_inline void film_write_background(KernelGlobals kg, + ConstIntegratorState state, + const Spectrum L, + const float transparent, + const bool is_transparent_background_ray, + ccl_global float *ccl_restrict render_buffer) { Spectrum contribution = INTEGRATOR_STATE(state, path, throughput) * L; - kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); + film_clamp_light(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); - ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); + ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer); const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); if (is_transparent_background_ray) { - kernel_accum_transparent(kg, state, path_flag, transparent, buffer); + film_write_transparent(kg, state, path_flag, transparent, buffer); } else { const int sample = INTEGRATOR_STATE(state, path, sample); - kernel_accum_combined_transparent_pass( - kg, path_flag, sample, contribution, transparent, buffer); - } - kernel_accum_emission_or_background_pass(kg, - state, - contribution, - buffer, - kernel_data.film.pass_background, - kernel_data.background.lightgroup); + film_write_combined_transparent_pass(kg, path_flag, sample, contribution, transparent, buffer); + } + film_write_emission_or_background_pass(kg, + state, + contribution, + buffer, + kernel_data.film.pass_background, + kernel_data.background.lightgroup); } /* Write emission to render buffer. */ -ccl_device_inline void kernel_accum_emission(KernelGlobals kg, - ConstIntegratorState state, - const Spectrum L, - ccl_global float *ccl_restrict render_buffer, - const int lightgroup = LIGHTGROUP_NONE) +ccl_device_inline void film_write_volume_emission(KernelGlobals kg, + ConstIntegratorState state, + const Spectrum L, + ccl_global float *ccl_restrict render_buffer, + const int lightgroup = LIGHTGROUP_NONE) { Spectrum contribution = L; - kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); + film_clamp_light(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); + + ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + const int sample = INTEGRATOR_STATE(state, path, sample); + + film_write_combined_pass(kg, path_flag, sample, contribution, buffer); + film_write_emission_or_background_pass( + kg, state, contribution, buffer, kernel_data.film.pass_emission, lightgroup); +} + +ccl_device_inline void film_write_surface_emission(KernelGlobals kg, + ConstIntegratorState state, + const Spectrum L, + const float mis_weight, + ccl_global float *ccl_restrict render_buffer, + const int lightgroup = LIGHTGROUP_NONE) +{ + Spectrum contribution = INTEGRATOR_STATE(state, path, throughput) * L * mis_weight; + film_clamp_light(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); - ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); + ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer); const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); const int sample = INTEGRATOR_STATE(state, path, sample); - kernel_accum_combined_pass(kg, path_flag, sample, contribution, buffer); - kernel_accum_emission_or_background_pass( + film_write_combined_pass(kg, path_flag, sample, contribution, buffer); + film_write_emission_or_background_pass( kg, state, contribution, buffer, kernel_data.film.pass_emission, lightgroup); } diff --git a/intern/cycles/kernel/film/passes.h b/intern/cycles/kernel/film/passes.h deleted file mode 100644 index bea23411000..00000000000 --- a/intern/cycles/kernel/film/passes.h +++ /dev/null @@ -1,304 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * Copyright 2011-2022 Blender Foundation */ - -#pragma once - -#include "kernel/geom/geom.h" - -#include "kernel/film/id_passes.h" -#include "kernel/film/write_passes.h" - -CCL_NAMESPACE_BEGIN - -/* Get pointer to pixel in render buffer. */ -ccl_device_forceinline ccl_global float *kernel_pass_pixel_render_buffer( - KernelGlobals kg, ConstIntegratorState state, ccl_global float *ccl_restrict render_buffer) -{ - const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); - const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * - kernel_data.film.pass_stride; - return render_buffer + render_buffer_offset; -} - -#ifdef __DENOISING_FEATURES__ - -ccl_device_forceinline void kernel_write_denoising_features_surface( - KernelGlobals kg, - IntegratorState state, - ccl_private const ShaderData *sd, - ccl_global float *ccl_restrict render_buffer) -{ - if (!(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_DENOISING_FEATURES)) { - return; - } - - /* Skip implicitly transparent surfaces. */ - if (sd->flag & SD_HAS_ONLY_VOLUME) { - return; - } - - ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); - - if (kernel_data.film.pass_denoising_depth != PASS_UNUSED) { - const Spectrum denoising_feature_throughput = INTEGRATOR_STATE( - state, path, denoising_feature_throughput); - const float denoising_depth = ensure_finite(average(denoising_feature_throughput) * - sd->ray_length); - kernel_write_pass_float(buffer + kernel_data.film.pass_denoising_depth, denoising_depth); - } - - float3 normal = zero_float3(); - Spectrum diffuse_albedo = zero_spectrum(); - Spectrum specular_albedo = zero_spectrum(); - float sum_weight = 0.0f, sum_nonspecular_weight = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private const ShaderClosure *sc = &sd->closure[i]; - - if (!CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { - continue; - } - - /* All closures contribute to the normal feature, but only diffuse-like ones to the albedo. */ - normal += sc->N * sc->sample_weight; - sum_weight += sc->sample_weight; - - Spectrum closure_albedo = sc->weight; - /* Closures that include a Fresnel term typically have weights close to 1 even though their - * actual contribution is significantly lower. - * To account for this, we scale their weight by the average fresnel factor (the same is also - * done for the sample weight in the BSDF setup, so we don't need to scale that here). */ - if (CLOSURE_IS_BSDF_MICROFACET_FRESNEL(sc->type)) { - ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc; - closure_albedo *= bsdf->extra->fresnel_color; - } - else if (sc->type == CLOSURE_BSDF_PRINCIPLED_SHEEN_ID) { - ccl_private PrincipledSheenBsdf *bsdf = (ccl_private PrincipledSheenBsdf *)sc; - closure_albedo *= bsdf->avg_value; - } - else if (sc->type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID) { - closure_albedo *= bsdf_principled_hair_albedo(sc); - } - else if (sc->type == CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID) { - /* BSSRDF already accounts for weight, retro-reflection would double up. */ - ccl_private const PrincipledDiffuseBsdf *bsdf = (ccl_private const PrincipledDiffuseBsdf *) - sc; - if (bsdf->components == PRINCIPLED_DIFFUSE_RETRO_REFLECTION) { - continue; - } - } - - if (bsdf_get_specular_roughness_squared(sc) > sqr(0.075f)) { - diffuse_albedo += closure_albedo; - sum_nonspecular_weight += sc->sample_weight; - } - else { - specular_albedo += closure_albedo; - } - } - - /* Wait for next bounce if 75% or more sample weight belongs to specular-like closures. */ - if ((sum_weight == 0.0f) || (sum_nonspecular_weight * 4.0f > sum_weight)) { - if (sum_weight != 0.0f) { - normal /= sum_weight; - } - - if (kernel_data.film.pass_denoising_normal != PASS_UNUSED) { - /* Transform normal into camera space. */ - const Transform worldtocamera = kernel_data.cam.worldtocamera; - normal = transform_direction(&worldtocamera, normal); - - const float3 denoising_normal = ensure_finite(normal); - kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_normal, denoising_normal); - } - - if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { - const Spectrum denoising_feature_throughput = INTEGRATOR_STATE( - state, path, denoising_feature_throughput); - const Spectrum denoising_albedo = ensure_finite(denoising_feature_throughput * - diffuse_albedo); - kernel_write_pass_spectrum(buffer + kernel_data.film.pass_denoising_albedo, - denoising_albedo); - } - - INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_DENOISING_FEATURES; - } - else { - INTEGRATOR_STATE_WRITE(state, path, denoising_feature_throughput) *= specular_albedo; - } -} - -ccl_device_forceinline void kernel_write_denoising_features_volume(KernelGlobals kg, - IntegratorState state, - const Spectrum albedo, - const bool scatter, - ccl_global float *ccl_restrict - render_buffer) -{ - ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); - const Spectrum denoising_feature_throughput = INTEGRATOR_STATE( - state, path, denoising_feature_throughput); - - if (scatter && kernel_data.film.pass_denoising_normal != PASS_UNUSED) { - /* Assume scatter is sufficiently diffuse to stop writing denoising features. */ - INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_DENOISING_FEATURES; - - /* Write view direction as normal. */ - const float3 denoising_normal = make_float3(0.0f, 0.0f, -1.0f); - kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_normal, denoising_normal); - } - - if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { - /* Write albedo. */ - const Spectrum denoising_albedo = ensure_finite(denoising_feature_throughput * albedo); - kernel_write_pass_spectrum(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); - } -} -#endif /* __DENOISING_FEATURES__ */ - -ccl_device_inline size_t kernel_write_id_pass(ccl_global float *ccl_restrict buffer, - size_t depth, - float id, - float matte_weight) -{ - kernel_write_id_slots(buffer, depth * 2, id, matte_weight); - return depth * 4; -} - -ccl_device_inline void kernel_write_data_passes(KernelGlobals kg, - IntegratorState state, - ccl_private const ShaderData *sd, - ccl_global float *ccl_restrict render_buffer) -{ -#ifdef __PASSES__ - const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); - - if (!(path_flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { - return; - } - - const int flag = kernel_data.film.pass_flag; - - if (!(flag & PASS_ANY)) { - return; - } - - ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); - - if (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)) { - if (!(sd->flag & SD_TRANSPARENT) || kernel_data.film.pass_alpha_threshold == 0.0f || - average(shader_bsdf_alpha(kg, sd)) >= kernel_data.film.pass_alpha_threshold) { - if (INTEGRATOR_STATE(state, path, sample) == 0) { - if (flag & PASSMASK(DEPTH)) { - const float depth = camera_z_depth(kg, sd->P); - kernel_write_pass_float(buffer + kernel_data.film.pass_depth, depth); - } - if (flag & PASSMASK(OBJECT_ID)) { - const float id = object_pass_id(kg, sd->object); - kernel_write_pass_float(buffer + kernel_data.film.pass_object_id, id); - } - if (flag & PASSMASK(MATERIAL_ID)) { - const float id = shader_pass_id(kg, sd); - kernel_write_pass_float(buffer + kernel_data.film.pass_material_id, id); - } - if (flag & PASSMASK(POSITION)) { - const float3 position = sd->P; - kernel_write_pass_float3(buffer + kernel_data.film.pass_position, position); - } - } - - if (flag & PASSMASK(NORMAL)) { - const float3 normal = shader_bsdf_average_normal(kg, sd); - kernel_write_pass_float3(buffer + kernel_data.film.pass_normal, normal); - } - if (flag & PASSMASK(ROUGHNESS)) { - const float roughness = shader_bsdf_average_roughness(sd); - kernel_write_pass_float(buffer + kernel_data.film.pass_roughness, roughness); - } - if (flag & PASSMASK(UV)) { - const float3 uv = primitive_uv(kg, sd); - kernel_write_pass_float3(buffer + kernel_data.film.pass_uv, uv); - } - if (flag & PASSMASK(MOTION)) { - const float4 speed = primitive_motion_vector(kg, sd); - kernel_write_pass_float4(buffer + kernel_data.film.pass_motion, speed); - kernel_write_pass_float(buffer + kernel_data.film.pass_motion_weight, 1.0f); - } - - INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_SINGLE_PASS_DONE; - } - } - - if (kernel_data.film.cryptomatte_passes) { - const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); - const float matte_weight = average(throughput) * - (1.0f - average(shader_bsdf_transparency(kg, sd))); - if (matte_weight > 0.0f) { - ccl_global float *cryptomatte_buffer = buffer + kernel_data.film.pass_cryptomatte; - if (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) { - const float id = object_cryptomatte_id(kg, sd->object); - cryptomatte_buffer += kernel_write_id_pass( - cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight); - } - if (kernel_data.film.cryptomatte_passes & CRYPT_MATERIAL) { - const float id = shader_cryptomatte_id(kg, sd->shader); - cryptomatte_buffer += kernel_write_id_pass( - cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight); - } - if (kernel_data.film.cryptomatte_passes & CRYPT_ASSET) { - const float id = object_cryptomatte_asset_id(kg, sd->object); - cryptomatte_buffer += kernel_write_id_pass( - cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight); - } - } - } - - if (flag & PASSMASK(DIFFUSE_COLOR)) { - const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); - kernel_write_pass_spectrum(buffer + kernel_data.film.pass_diffuse_color, - shader_bsdf_diffuse(kg, sd) * throughput); - } - if (flag & PASSMASK(GLOSSY_COLOR)) { - const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); - kernel_write_pass_spectrum(buffer + kernel_data.film.pass_glossy_color, - shader_bsdf_glossy(kg, sd) * throughput); - } - if (flag & PASSMASK(TRANSMISSION_COLOR)) { - const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); - kernel_write_pass_spectrum(buffer + kernel_data.film.pass_transmission_color, - shader_bsdf_transmission(kg, sd) * throughput); - } - if (flag & PASSMASK(MIST)) { - /* Bring depth into 0..1 range. */ - const float mist_start = kernel_data.film.mist_start; - const float mist_inv_depth = kernel_data.film.mist_inv_depth; - - const float depth = camera_distance(kg, sd->P); - float mist = saturatef((depth - mist_start) * mist_inv_depth); - - /* Falloff */ - const float mist_falloff = kernel_data.film.mist_falloff; - - if (mist_falloff == 1.0f) - ; - else if (mist_falloff == 2.0f) - mist = mist * mist; - else if (mist_falloff == 0.5f) - mist = sqrtf(mist); - else - mist = powf(mist, mist_falloff); - - /* Modulate by transparency */ - const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); - const Spectrum alpha = shader_bsdf_alpha(kg, sd); - const float mist_output = (1.0f - mist) * average(throughput * alpha); - - /* Note that the final value in the render buffer we want is 1 - mist_output, - * to avoid having to tracking this in the Integrator state we do the negation - * after rendering. */ - kernel_write_pass_float(buffer + kernel_data.film.pass_mist, mist_output); - } -#endif -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/film/read.h b/intern/cycles/kernel/film/read.h index 995c20a0053..108f992e29d 100644 --- a/intern/cycles/kernel/film/read.h +++ b/intern/cycles/kernel/film/read.h @@ -1,6 +1,10 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ +/* Functions to retrieving render passes for display or output. Reading from + * the raw render buffer and normalizing based on the number of samples, + * computing alpha, compositing shadow catchers, etc. */ + #pragma once CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/film/write_passes.h b/intern/cycles/kernel/film/write.h index c78116cedc6..c630a522ee3 100644 --- a/intern/cycles/kernel/film/write_passes.h +++ b/intern/cycles/kernel/film/write.h @@ -11,7 +11,18 @@ CCL_NAMESPACE_BEGIN -ccl_device_inline void kernel_write_pass_float(ccl_global float *ccl_restrict buffer, float value) +/* Get pointer to pixel in render buffer. */ +ccl_device_forceinline ccl_global float *film_pass_pixel_render_buffer( + KernelGlobals kg, ConstIntegratorState state, ccl_global float *ccl_restrict render_buffer) +{ + const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * + kernel_data.film.pass_stride; + return render_buffer + render_buffer_offset; +} + +/* Write to pixel. */ +ccl_device_inline void film_write_pass_float(ccl_global float *ccl_restrict buffer, float value) { #ifdef __ATOMIC_PASS_WRITE__ atomic_add_and_fetch_float(buffer, value); @@ -20,8 +31,7 @@ ccl_device_inline void kernel_write_pass_float(ccl_global float *ccl_restrict bu #endif } -ccl_device_inline void kernel_write_pass_float3(ccl_global float *ccl_restrict buffer, - float3 value) +ccl_device_inline void film_write_pass_float3(ccl_global float *ccl_restrict buffer, float3 value) { #ifdef __ATOMIC_PASS_WRITE__ ccl_global float *buf_x = buffer + 0; @@ -38,14 +48,13 @@ ccl_device_inline void kernel_write_pass_float3(ccl_global float *ccl_restrict b #endif } -ccl_device_inline void kernel_write_pass_spectrum(ccl_global float *ccl_restrict buffer, - Spectrum value) +ccl_device_inline void film_write_pass_spectrum(ccl_global float *ccl_restrict buffer, + Spectrum value) { - kernel_write_pass_float3(buffer, spectrum_to_rgb(value)); + film_write_pass_float3(buffer, spectrum_to_rgb(value)); } -ccl_device_inline void kernel_write_pass_float4(ccl_global float *ccl_restrict buffer, - float4 value) +ccl_device_inline void film_write_pass_float4(ccl_global float *ccl_restrict buffer, float4 value) { #ifdef __ATOMIC_PASS_WRITE__ ccl_global float *buf_x = buffer + 0; diff --git a/intern/cycles/kernel/integrator/displacement_shader.h b/intern/cycles/kernel/integrator/displacement_shader.h new file mode 100644 index 00000000000..71a0f56fb3e --- /dev/null +++ b/intern/cycles/kernel/integrator/displacement_shader.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +/* Functions to evaluate displacement shader. */ + +#pragma once + +#include "kernel/svm/svm.h" + +#ifdef __OSL__ +# include "kernel/osl/shader.h" +#endif + +CCL_NAMESPACE_BEGIN + +template<typename ConstIntegratorGenericState> +ccl_device void displacement_shader_eval(KernelGlobals kg, + ConstIntegratorGenericState state, + ccl_private ShaderData *sd) +{ + sd->num_closure = 0; + sd->num_closure_left = 0; + + /* this will modify sd->P */ +#ifdef __SVM__ +# ifdef __OSL__ + if (kg->osl) + OSLShader::eval_displacement(kg, state, sd); + else +# endif + { + svm_eval_nodes<KERNEL_FEATURE_NODE_MASK_DISPLACEMENT, SHADER_TYPE_DISPLACEMENT>( + kg, state, sd, NULL, 0); + } +#endif +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/init_from_bake.h b/intern/cycles/kernel/integrator/init_from_bake.h index 9897bc3d65c..eca2c0b9ffb 100644 --- a/intern/cycles/kernel/integrator/init_from_bake.h +++ b/intern/cycles/kernel/integrator/init_from_bake.h @@ -5,8 +5,8 @@ #include "kernel/camera/camera.h" -#include "kernel/film/accumulate.h" #include "kernel/film/adaptive_sampling.h" +#include "kernel/film/light_passes.h" #include "kernel/integrator/path_state.h" @@ -92,12 +92,12 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, path_state_init(state, tile, x, y); /* Check whether the pixel has converged and should not be sampled anymore. */ - if (!kernel_need_sample_pixel(kg, state, render_buffer)) { + if (!film_need_sample_pixel(kg, state, render_buffer)) { return false; } /* Always count the sample, even if the camera sample will reject the ray. */ - const int sample = kernel_accum_sample( + const int sample = film_write_sample( kg, state, render_buffer, scheduled_sample, tile->sample_offset); /* Setup render buffers. */ @@ -112,7 +112,7 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, int prim = __float_as_uint(primitive[1]); if (prim == -1) { /* Accumulate transparency for empty pixels. */ - kernel_accum_transparent(kg, state, 0, 1.0f, buffer); + film_write_transparent(kg, state, 0, 1.0f, buffer); return true; } @@ -200,11 +200,11 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, /* Fast path for position and normal passes not affected by shaders. */ if (kernel_data.film.pass_position != PASS_UNUSED) { - kernel_write_pass_float3(buffer + kernel_data.film.pass_position, P); + film_write_pass_float3(buffer + kernel_data.film.pass_position, P); return true; } else if (kernel_data.film.pass_normal != PASS_UNUSED && !(shader_flags & SD_HAS_BUMP)) { - kernel_write_pass_float3(buffer + kernel_data.film.pass_normal, N); + film_write_pass_float3(buffer + kernel_data.film.pass_normal, N); return true; } diff --git a/intern/cycles/kernel/integrator/init_from_camera.h b/intern/cycles/kernel/integrator/init_from_camera.h index 67ac3603d4c..8df3e1b9fb3 100644 --- a/intern/cycles/kernel/integrator/init_from_camera.h +++ b/intern/cycles/kernel/integrator/init_from_camera.h @@ -5,8 +5,8 @@ #include "kernel/camera/camera.h" -#include "kernel/film/accumulate.h" #include "kernel/film/adaptive_sampling.h" +#include "kernel/film/light_passes.h" #include "kernel/integrator/path_state.h" #include "kernel/integrator/shadow_catcher.h" @@ -57,7 +57,7 @@ ccl_device bool integrator_init_from_camera(KernelGlobals kg, path_state_init(state, tile, x, y); /* Check whether the pixel has converged and should not be sampled anymore. */ - if (!kernel_need_sample_pixel(kg, state, render_buffer)) { + if (!film_need_sample_pixel(kg, state, render_buffer)) { return false; } @@ -66,7 +66,7 @@ ccl_device bool integrator_init_from_camera(KernelGlobals kg, * This logic allows to both count actual number of samples per pixel, and to add samples to this * pixel after it was converged and samples were added somewhere else (in which case the * `scheduled_sample` will be different from actual number of samples in this pixel). */ - const int sample = kernel_accum_sample( + const int sample = film_write_sample( kg, state, render_buffer, scheduled_sample, tile->sample_offset); /* Initialize random number seed for path. */ diff --git a/intern/cycles/kernel/integrator/intersect_closest.h b/intern/cycles/kernel/integrator/intersect_closest.h index 60299f2cb2f..4ecff56a3fd 100644 --- a/intern/cycles/kernel/integrator/intersect_closest.h +++ b/intern/cycles/kernel/integrator/intersect_closest.h @@ -5,6 +5,8 @@ #include "kernel/camera/projection.h" +#include "kernel/film/light_passes.h" + #include "kernel/integrator/path_state.h" #include "kernel/integrator/shadow_catcher.h" @@ -87,7 +89,7 @@ ccl_device_forceinline void integrator_split_shadow_catcher( return; } - kernel_write_shadow_catcher_bounce_data(kg, state, render_buffer); + film_write_shadow_catcher_bounce_data(kg, state, render_buffer); /* Mark state as having done a shadow catcher split so that it stops contributing to * the shadow catcher matte pass, but keeps contributing to the combined pass. */ diff --git a/intern/cycles/kernel/integrator/intersect_volume_stack.h b/intern/cycles/kernel/integrator/intersect_volume_stack.h index b53bee11312..c2490581e4d 100644 --- a/intern/cycles/kernel/integrator/intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/intersect_volume_stack.h @@ -5,7 +5,6 @@ #include "kernel/bvh/bvh.h" #include "kernel/geom/geom.h" -#include "kernel/integrator/shader_eval.h" #include "kernel/integrator/volume_stack.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/mnee.h b/intern/cycles/kernel/integrator/mnee.h index 84d527bc8b1..a0ad7afe591 100644 --- a/intern/cycles/kernel/integrator/mnee.h +++ b/intern/cycles/kernel/integrator/mnee.h @@ -807,7 +807,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg, float3 wo = normalize_len(vertices[0].p - sd->P, &wo_len); /* Initialize throughput and evaluate receiver bsdf * |n.wo|. */ - shader_bsdf_eval(kg, sd, wo, false, throughput, ls->shader); + surface_shader_bsdf_eval(kg, sd, wo, false, throughput, ls->shader); /* Update light sample with new position / direct.ion * and keep pdf in vertex area measure */ @@ -913,7 +913,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg, INTEGRATOR_STATE_WRITE(state, path, bounce) = bounce + 1 + vi; /* Evaluate shader nodes at solution vi. */ - shader_eval_surface<KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW>( + surface_shader_eval<KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW>( kg, state, sd_mnee, NULL, PATH_RAY_DIFFUSE, true); /* Set light looking dir. */ @@ -1006,7 +1006,7 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg, return 0; /* Last bool argument is the MNEE flag (for TINY_MAX_CLOSURE cap in kernel_shader.h). */ - shader_eval_surface<KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW>( + surface_shader_eval<KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW>( kg, state, sd_mnee, NULL, PATH_RAY_DIFFUSE, true); /* Get and sample refraction bsdf */ diff --git a/intern/cycles/kernel/integrator/shade_background.h b/intern/cycles/kernel/integrator/shade_background.h index 57d060d58df..30ce0999258 100644 --- a/intern/cycles/kernel/integrator/shade_background.h +++ b/intern/cycles/kernel/integrator/shade_background.h @@ -3,8 +3,10 @@ #pragma once -#include "kernel/film/accumulate.h" -#include "kernel/integrator/shader_eval.h" +#include "kernel/film/light_passes.h" + +#include "kernel/integrator/surface_shader.h" + #include "kernel/light/light.h" #include "kernel/light/sample.h" @@ -14,7 +16,6 @@ ccl_device Spectrum integrator_eval_background_shader(KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer) { -#ifdef __BACKGROUND__ const int shader = kernel_data.background.surface_shader; const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); @@ -31,50 +32,30 @@ ccl_device Spectrum integrator_eval_background_shader(KernelGlobals kg, /* Use fast constant background color if available. */ Spectrum L = zero_spectrum(); - if (!shader_constant_emission_eval(kg, shader, &L)) { - /* Evaluate background shader. */ - - /* TODO: does aliasing like this break automatic SoA in CUDA? - * Should we instead store closures separate from ShaderData? */ - ShaderDataTinyStorage emission_sd_storage; - ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); - - PROFILING_INIT_FOR_SHADER(kg, PROFILING_SHADE_LIGHT_SETUP); - shader_setup_from_background(kg, - emission_sd, - INTEGRATOR_STATE(state, ray, P), - INTEGRATOR_STATE(state, ray, D), - INTEGRATOR_STATE(state, ray, time)); - - PROFILING_SHADER(emission_sd->object, emission_sd->shader); - PROFILING_EVENT(PROFILING_SHADE_LIGHT_EVAL); - shader_eval_surface<KERNEL_FEATURE_NODE_MASK_SURFACE_BACKGROUND>( - kg, state, emission_sd, render_buffer, path_flag | PATH_RAY_EMISSION); - - L = shader_background_eval(emission_sd); + if (surface_shader_constant_emission(kg, shader, &L)) { + return L; } - /* Background MIS weights. */ -# ifdef __BACKGROUND_MIS__ - /* Check if background light exists or if we should skip pdf. */ - if (!(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_MIS_SKIP) && - kernel_data.background.use_mis) { - const float3 ray_P = INTEGRATOR_STATE(state, ray, P); - const float3 ray_D = INTEGRATOR_STATE(state, ray, D); - const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); - - /* multiple importance sampling, get background light pdf for ray - * direction, and compute weight with respect to BSDF pdf */ - const float pdf = background_light_pdf(kg, ray_P, ray_D); - const float mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, pdf); - L *= mis_weight; - } -# endif + /* Evaluate background shader. */ - return L; -#else - return make_spectrum(0.8f); -#endif + /* TODO: does aliasing like this break automatic SoA in CUDA? + * Should we instead store closures separate from ShaderData? */ + ShaderDataTinyStorage emission_sd_storage; + ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + + PROFILING_INIT_FOR_SHADER(kg, PROFILING_SHADE_LIGHT_SETUP); + shader_setup_from_background(kg, + emission_sd, + INTEGRATOR_STATE(state, ray, P), + INTEGRATOR_STATE(state, ray, D), + INTEGRATOR_STATE(state, ray, time)); + + PROFILING_SHADER(emission_sd->object, emission_sd->shader); + PROFILING_EVENT(PROFILING_SHADE_LIGHT_EVAL); + surface_shader_eval<KERNEL_FEATURE_NODE_MASK_SURFACE_BACKGROUND>( + kg, state, emission_sd, render_buffer, path_flag | PATH_RAY_EMISSION); + + return surface_shader_background(emission_sd); } ccl_device_inline void integrate_background(KernelGlobals kg, @@ -117,17 +98,37 @@ ccl_device_inline void integrate_background(KernelGlobals kg, #endif /* __MNEE__ */ /* Evaluate background shader. */ - Spectrum L = (eval_background) ? integrator_eval_background_shader(kg, state, render_buffer) : - zero_spectrum(); + Spectrum L = zero_spectrum(); + + if (eval_background) { + L = integrator_eval_background_shader(kg, state, render_buffer); + + /* When using the ao bounces approximation, adjust background + * shader intensity with ao factor. */ + if (path_state_ao_bounce(kg, state)) { + L *= kernel_data.integrator.ao_bounces_factor; + } + + /* Background MIS weights. */ + float mis_weight = 1.0f; + /* Check if background light exists or if we should skip pdf. */ + if (!(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_MIS_SKIP) && + kernel_data.background.use_mis) { + const float3 ray_P = INTEGRATOR_STATE(state, ray, P); + const float3 ray_D = INTEGRATOR_STATE(state, ray, D); + const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); + + /* multiple importance sampling, get background light pdf for ray + * direction, and compute weight with respect to BSDF pdf */ + const float pdf = background_light_pdf(kg, ray_P, ray_D); + mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, pdf); + } - /* When using the ao bounces approximation, adjust background - * shader intensity with ao factor. */ - if (path_state_ao_bounce(kg, state)) { - L *= kernel_data.integrator.ao_bounces_factor; + L *= mis_weight; } /* Write to render buffer. */ - kernel_accum_background(kg, state, L, transparent, is_transparent_background_ray, render_buffer); + film_write_background(kg, state, L, transparent, is_transparent_background_ray, render_buffer); } ccl_device_inline void integrate_distant_lights(KernelGlobals kg, @@ -175,18 +176,17 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg, } /* MIS weighting. */ + float mis_weight = 1.0f; if (!(path_flag & PATH_RAY_MIS_SKIP)) { /* multiple importance sampling, get regular light pdf, * and compute weight with respect to BSDF pdf */ const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); - const float mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, ls.pdf); - light_eval *= mis_weight; + mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, ls.pdf); } /* Write to render buffer. */ - const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); - kernel_accum_emission( - kg, state, throughput * light_eval, render_buffer, kernel_data.background.lightgroup); + film_write_surface_emission( + kg, state, light_eval, mis_weight, render_buffer, kernel_data.background.lightgroup); } } } diff --git a/intern/cycles/kernel/integrator/shade_light.h b/intern/cycles/kernel/integrator/shade_light.h index ac9d1415abb..a4246f99bbf 100644 --- a/intern/cycles/kernel/integrator/shade_light.h +++ b/intern/cycles/kernel/integrator/shade_light.h @@ -3,8 +3,8 @@ #pragma once -#include "kernel/film/accumulate.h" -#include "kernel/integrator/shader_eval.h" +#include "kernel/film/light_passes.h" +#include "kernel/integrator/surface_shader.h" #include "kernel/light/light.h" #include "kernel/light/sample.h" @@ -57,6 +57,7 @@ ccl_device_inline void integrate_light(KernelGlobals kg, } /* MIS weighting. */ + float mis_weight = 1.0f; if (!(path_flag & PATH_RAY_MIS_SKIP)) { /* multiple importance sampling, get regular light pdf, * and compute weight with respect to BSDF pdf */ @@ -66,8 +67,7 @@ ccl_device_inline void integrate_light(KernelGlobals kg, } /* Write to render buffer. */ - const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); - kernel_accum_emission(kg, state, throughput * light_eval, render_buffer, ls.group); + film_write_surface_emission(kg, state, light_eval, mis_weight, render_buffer, ls.group); } ccl_device void integrator_shade_light(KernelGlobals kg, diff --git a/intern/cycles/kernel/integrator/shade_shadow.h b/intern/cycles/kernel/integrator/shade_shadow.h index a52706a77f1..ba18aed6ff0 100644 --- a/intern/cycles/kernel/integrator/shade_shadow.h +++ b/intern/cycles/kernel/integrator/shade_shadow.h @@ -4,7 +4,7 @@ #pragma once #include "kernel/integrator/shade_volume.h" -#include "kernel/integrator/shader_eval.h" +#include "kernel/integrator/surface_shader.h" #include "kernel/integrator/volume_stack.h" CCL_NAMESPACE_BEGIN @@ -40,7 +40,7 @@ ccl_device_inline Spectrum integrate_transparent_surface_shadow(KernelGlobals kg /* Evaluate shader. */ if (!(shadow_sd->flag & SD_HAS_ONLY_VOLUME)) { - shader_eval_surface<KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW>( + surface_shader_eval<KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW>( kg, state, shadow_sd, NULL, PATH_RAY_SHADOW); } @@ -50,7 +50,7 @@ ccl_device_inline Spectrum integrate_transparent_surface_shadow(KernelGlobals kg # endif /* Compute transparency from closures. */ - return shader_bsdf_transparency(kg, shadow_sd); + return surface_shader_transparency(kg, shadow_sd); } # ifdef __VOLUME__ @@ -165,7 +165,7 @@ ccl_device void integrator_shade_shadow(KernelGlobals kg, return; } else { - kernel_accum_light(kg, state, render_buffer); + film_write_direct_light(kg, state, render_buffer); integrator_shadow_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); return; } diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h index f3f8ed67713..c19f56a9b70 100644 --- a/intern/cycles/kernel/integrator/shade_surface.h +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -3,14 +3,15 @@ #pragma once -#include "kernel/film/accumulate.h" -#include "kernel/film/passes.h" +#include "kernel/film/data_passes.h" +#include "kernel/film/denoising_passes.h" +#include "kernel/film/light_passes.h" #include "kernel/integrator/mnee.h" #include "kernel/integrator/path_state.h" -#include "kernel/integrator/shader_eval.h" #include "kernel/integrator/subsurface.h" +#include "kernel/integrator/surface_shader.h" #include "kernel/integrator/volume_stack.h" #include "kernel/light/light.h" @@ -77,7 +78,6 @@ ccl_device_forceinline float3 integrate_surface_ray_offset(KernelGlobals kg, } } -#ifdef __HOLDOUT__ ccl_device_forceinline bool integrate_surface_holdout(KernelGlobals kg, ConstIntegratorState state, ccl_private ShaderData *sd, @@ -88,10 +88,10 @@ ccl_device_forceinline bool integrate_surface_holdout(KernelGlobals kg, if (((sd->flag & SD_HOLDOUT) || (sd->object_flag & SD_OBJECT_HOLDOUT_MASK)) && (path_flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { - const Spectrum holdout_weight = shader_holdout_apply(kg, sd); + const Spectrum holdout_weight = surface_shader_apply_holdout(kg, sd); const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); const float transparent = average(holdout_weight * throughput); - kernel_accum_holdout(kg, state, path_flag, transparent, render_buffer); + film_write_holdout(kg, state, path_flag, transparent, render_buffer); if (isequal(holdout_weight, one_spectrum())) { return false; } @@ -99,9 +99,7 @@ ccl_device_forceinline bool integrate_surface_holdout(KernelGlobals kg, return true; } -#endif /* __HOLDOUT__ */ -#ifdef __EMISSION__ ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg, ConstIntegratorState state, ccl_private const ShaderData *sd, @@ -111,14 +109,15 @@ ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg, const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); /* Evaluate emissive closure. */ - Spectrum L = shader_emissive_eval(sd); + Spectrum L = surface_shader_emission(sd); + float mis_weight = 1.0f; -# ifdef __HAIR__ +#ifdef __HAIR__ if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS) && (sd->type & PRIMITIVE_TRIANGLE)) -# else +#else if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS)) -# endif +#endif { const float bsdf_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); const float t = sd->ray_length; @@ -126,17 +125,13 @@ ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg, /* Multiple importance sampling, get triangle light pdf, * and compute weight with respect to BSDF pdf. */ float pdf = triangle_light_pdf(kg, sd, t); - float mis_weight = light_sample_mis_weight_forward(kg, bsdf_pdf, pdf); - L *= mis_weight; + mis_weight = light_sample_mis_weight_forward(kg, bsdf_pdf, pdf); } - const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); - kernel_accum_emission( - kg, state, throughput * L, render_buffer, object_lightgroup(kg, sd->object)); + film_write_surface_emission( + kg, state, L, mis_weight, render_buffer, object_lightgroup(kg, sd->object)); } -#endif /* __EMISSION__ */ -#ifdef __EMISSION__ /* Path tracing: sample point on light and evaluate light shader, then * queue shadow ray to be traced. */ template<uint node_feature_mask> @@ -176,9 +171,9 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, Ray ray ccl_optional_struct_init; BsdfEval bsdf_eval ccl_optional_struct_init; - const bool is_transmission = shader_bsdf_is_transmission(sd, ls.D); + const bool is_transmission = surface_shader_is_transmission(sd, ls.D); -# ifdef __MNEE__ +#ifdef __MNEE__ int mnee_vertex_count = 0; IF_KERNEL_FEATURE(MNEE) { @@ -204,7 +199,7 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, light_sample_to_surface_shadow_ray(kg, emission_sd, &ls, &ray); } else -# endif /* __MNEE__ */ +#endif /* __MNEE__ */ { const Spectrum light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, sd->time); if (is_zero(light_eval)) { @@ -212,7 +207,8 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, } /* Evaluate BSDF. */ - const float bsdf_pdf = shader_bsdf_eval(kg, sd, ls.D, is_transmission, &bsdf_eval, ls.shader); + const float bsdf_pdf = surface_shader_bsdf_eval( + kg, sd, ls.D, is_transmission, &bsdf_eval, ls.shader); bsdf_eval_mul(&bsdf_eval, light_eval / ls.pdf); if (ls.shader & SHADER_USE_MIS) { @@ -240,9 +236,9 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state); if (is_transmission) { -# ifdef __VOLUME__ +#ifdef __VOLUME__ shadow_volume_stack_enter_exit(kg, shadow_state, sd); -# endif +#endif } if (ray.self.object != OBJECT_NONE) { @@ -298,7 +294,7 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, glossy_bounce) = INTEGRATOR_STATE( state, path, glossy_bounce); -# ifdef __MNEE__ +#ifdef __MNEE__ if (mnee_vertex_count > 0) { INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transmission_bounce) = INTEGRATOR_STATE(state, path, transmission_bounce) + mnee_vertex_count - 1; @@ -310,7 +306,7 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, bounce) = INTEGRATOR_STATE(state, path, bounce) + mnee_vertex_count; } else -# endif +#endif { INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transmission_bounce) = INTEGRATOR_STATE( state, path, transmission_bounce); @@ -332,7 +328,6 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, ls.group + 1 : kernel_data.background.lightgroup + 1; } -#endif /* Path tracing: bounce off or through surface with new direction. */ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( @@ -347,7 +342,7 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( } float2 rand_bsdf = path_state_rng_2D(kg, rng_state, PRNG_SURFACE_BSDF); - ccl_private const ShaderClosure *sc = shader_bsdf_bssrdf_pick(sd, &rand_bsdf); + ccl_private const ShaderClosure *sc = surface_shader_bsdf_bssrdf_pick(sd, &rand_bsdf); #ifdef __SUBSURFACE__ /* BSSRDF closure, we schedule subsurface intersection kernel. */ @@ -362,7 +357,8 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( float3 bsdf_omega_in ccl_optional_struct_init; int label; - label = shader_bsdf_sample_closure(kg, sd, sc, rand_bsdf, &bsdf_eval, &bsdf_omega_in, &bsdf_pdf); + label = surface_shader_bsdf_sample_closure( + kg, sd, sc, rand_bsdf, &bsdf_eval, &bsdf_omega_in, &bsdf_pdf); if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) { return LABEL_NONE; @@ -456,7 +452,7 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, const float2 rand_bsdf = path_state_rng_2D(kg, rng_state, PRNG_SURFACE_BSDF); float3 ao_N; - const Spectrum ao_weight = shader_bsdf_ao( + const Spectrum ao_weight = surface_shader_ao( kg, sd, kernel_data.integrator.ao_additive_factor, &ao_N); float3 ao_D; @@ -500,7 +496,7 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce); uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag) | PATH_RAY_SHADOW_FOR_AO; const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput) * - shader_bsdf_alpha(kg, sd); + surface_shader_alpha(kg, sd); INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE( state, path, render_pixel_index); @@ -549,7 +545,7 @@ ccl_device bool integrate_surface(KernelGlobals kg, { /* Evaluate shader. */ PROFILING_EVENT(PROFILING_SHADE_SURFACE_EVAL); - shader_eval_surface<node_feature_mask>(kg, state, &sd, render_buffer, path_flag); + surface_shader_eval<node_feature_mask>(kg, state, &sd, render_buffer, path_flag); /* Initialize additional RNG for BSDFs. */ if (sd.flag & SD_BSDF_NEEDS_LCG) { @@ -571,21 +567,17 @@ ccl_device bool integrate_surface(KernelGlobals kg, #endif { /* Filter closures. */ - shader_prepare_surface_closures(kg, state, &sd, path_flag); + surface_shader_prepare_closures(kg, state, &sd, path_flag); -#ifdef __HOLDOUT__ /* Evaluate holdout. */ if (!integrate_surface_holdout(kg, state, &sd, render_buffer)) { return false; } -#endif -#ifdef __EMISSION__ /* Write emission. */ if (sd.flag & SD_EMISSION) { integrate_surface_emission(kg, state, &sd, render_buffer); } -#endif /* Perform path termination. Most paths have already been terminated in * the intersect_closest kernel, this is just for emission and for dividing @@ -599,11 +591,11 @@ ccl_device bool integrate_surface(KernelGlobals kg, /* Write render passes. */ #ifdef __PASSES__ PROFILING_EVENT(PROFILING_SHADE_SURFACE_PASSES); - kernel_write_data_passes(kg, state, &sd, render_buffer); + film_write_data_passes(kg, state, &sd, render_buffer); #endif #ifdef __DENOISING_FEATURES__ - kernel_write_denoising_features_surface(kg, state, &sd, render_buffer); + film_write_denoising_features_surface(kg, state, &sd, render_buffer); #endif } diff --git a/intern/cycles/kernel/integrator/shade_volume.h b/intern/cycles/kernel/integrator/shade_volume.h index 5e0584d4f98..aaef92729d6 100644 --- a/intern/cycles/kernel/integrator/shade_volume.h +++ b/intern/cycles/kernel/integrator/shade_volume.h @@ -3,12 +3,13 @@ #pragma once -#include "kernel/film/accumulate.h" -#include "kernel/film/passes.h" +#include "kernel/film/data_passes.h" +#include "kernel/film/denoising_passes.h" +#include "kernel/film/light_passes.h" #include "kernel/integrator/intersect_closest.h" #include "kernel/integrator/path_state.h" -#include "kernel/integrator/shader_eval.h" +#include "kernel/integrator/volume_shader.h" #include "kernel/integrator/volume_stack.h" #include "kernel/light/light.h" @@ -64,7 +65,7 @@ ccl_device_inline bool shadow_volume_shader_sample(KernelGlobals kg, ccl_private Spectrum *ccl_restrict extinction) { VOLUME_READ_LAMBDA(integrator_state_read_shadow_volume_stack(state, i)) - shader_eval_volume<true>(kg, state, sd, PATH_RAY_SHADOW, volume_read_lambda_pass); + volume_shader_eval<true>(kg, state, sd, PATH_RAY_SHADOW, volume_read_lambda_pass); if (!(sd->flag & SD_EXTINCTION)) { return false; @@ -83,7 +84,7 @@ ccl_device_inline bool volume_shader_sample(KernelGlobals kg, { const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); VOLUME_READ_LAMBDA(integrator_state_read_volume_stack(state, i)) - shader_eval_volume<false>(kg, state, sd, path_flag, volume_read_lambda_pass); + volume_shader_eval<false>(kg, state, sd, path_flag, volume_read_lambda_pass); if (!(sd->flag & (SD_EXTINCTION | SD_SCATTER | SD_EMISSION))) { return false; @@ -442,7 +443,7 @@ ccl_device_forceinline void volume_integrate_step_scattering( result.direct_scatter = true; result.direct_throughput *= coeff.sigma_s * new_transmittance / vstate.equiangular_pdf; - shader_copy_volume_phases(&result.direct_phases, sd); + volume_shader_copy_phases(&result.direct_phases, sd); /* Multiple importance sampling. */ if (vstate.use_mis) { @@ -478,7 +479,7 @@ ccl_device_forceinline void volume_integrate_step_scattering( result.indirect_scatter = true; result.indirect_t = new_t; result.indirect_throughput *= coeff.sigma_s * new_transmittance / distance_pdf; - shader_copy_volume_phases(&result.indirect_phases, sd); + volume_shader_copy_phases(&result.indirect_phases, sd); if (vstate.direct_sample_method != VOLUME_SAMPLE_EQUIANGULAR) { /* If using distance sampling for direct light, just copy parameters @@ -486,7 +487,7 @@ ccl_device_forceinline void volume_integrate_step_scattering( result.direct_scatter = true; result.direct_t = result.indirect_t; result.direct_throughput = result.indirect_throughput; - shader_copy_volume_phases(&result.direct_phases, sd); + volume_shader_copy_phases(&result.direct_phases, sd); /* Multiple importance sampling. */ if (vstate.use_mis) { @@ -663,20 +664,19 @@ ccl_device_forceinline void volume_integrate_heterogeneous( /* Write accumulated emission. */ if (!is_zero(accum_emission)) { - kernel_accum_emission( + film_write_volume_emission( kg, state, accum_emission, render_buffer, object_lightgroup(kg, sd->object)); } # ifdef __DENOISING_FEATURES__ /* Write denoising features. */ if (write_denoising_features) { - kernel_write_denoising_features_volume( + film_write_denoising_features_volume( kg, state, accum_albedo, result.indirect_scatter, render_buffer); } # endif /* __DENOISING_FEATURES__ */ } -# ifdef __EMISSION__ /* Path tracing: sample point on light and evaluate light shader, then * queue shadow ray to be traced. */ ccl_device_forceinline bool integrate_volume_sample_light( @@ -761,7 +761,7 @@ ccl_device_forceinline void integrate_volume_direct_light( /* Evaluate BSDF. */ BsdfEval phase_eval ccl_optional_struct_init; - const float phase_pdf = shader_volume_phase_eval(kg, sd, phases, ls->D, &phase_eval); + const float phase_pdf = volume_shader_phase_eval(kg, sd, phases, ls->D, &phase_eval); if (ls->shader & SHADER_USE_MIS) { float mis_weight = light_sample_mis_weight_nee(kg, ls->pdf, phase_pdf); @@ -850,7 +850,6 @@ ccl_device_forceinline void integrate_volume_direct_light( integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state); } -# endif /* Path tracing: scatter in new direction using phase function */ ccl_device_forceinline bool integrate_volume_phase_scatter( @@ -869,7 +868,7 @@ ccl_device_forceinline bool integrate_volume_phase_scatter( BsdfEval phase_eval ccl_optional_struct_init; float3 phase_omega_in ccl_optional_struct_init; - const int label = shader_volume_phase_sample( + const int label = volume_shader_phase_sample( kg, sd, phases, rand_phase, &phase_eval, &phase_omega_in, &phase_pdf); if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) { diff --git a/intern/cycles/kernel/integrator/shader_eval.h b/intern/cycles/kernel/integrator/shader_eval.h deleted file mode 100644 index b1316bda9bb..00000000000 --- a/intern/cycles/kernel/integrator/shader_eval.h +++ /dev/null @@ -1,947 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * Copyright 2011-2022 Blender Foundation */ - -/* Functions to evaluate shaders and use the resulting shader closures. */ - -#pragma once - -#include "kernel/closure/alloc.h" -#include "kernel/closure/bsdf.h" -#include "kernel/closure/bsdf_util.h" -#include "kernel/closure/emissive.h" - -#include "kernel/film/accumulate.h" - -#include "kernel/svm/svm.h" - -#ifdef __OSL__ -# include "kernel/osl/shader.h" -#endif - -CCL_NAMESPACE_BEGIN - -/* Merging */ - -#if defined(__VOLUME__) -ccl_device_inline void shader_merge_volume_closures(ccl_private ShaderData *sd) -{ - /* Merge identical closures to save closure space with stacked volumes. */ - for (int i = 0; i < sd->num_closure; i++) { - ccl_private ShaderClosure *sci = &sd->closure[i]; - - if (sci->type != CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) { - continue; - } - - for (int j = i + 1; j < sd->num_closure; j++) { - ccl_private ShaderClosure *scj = &sd->closure[j]; - if (sci->type != scj->type) { - continue; - } - - ccl_private const HenyeyGreensteinVolume *hgi = (ccl_private const HenyeyGreensteinVolume *) - sci; - ccl_private const HenyeyGreensteinVolume *hgj = (ccl_private const HenyeyGreensteinVolume *) - scj; - if (!(hgi->g == hgj->g)) { - continue; - } - - sci->weight += scj->weight; - sci->sample_weight += scj->sample_weight; - - int size = sd->num_closure - (j + 1); - if (size > 0) { - for (int k = 0; k < size; k++) { - scj[k] = scj[k + 1]; - } - } - - sd->num_closure--; - kernel_assert(sd->num_closure >= 0); - j--; - } - } -} - -ccl_device_inline void shader_copy_volume_phases(ccl_private ShaderVolumePhases *ccl_restrict - phases, - ccl_private const ShaderData *ccl_restrict sd) -{ - phases->num_closure = 0; - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private const ShaderClosure *from_sc = &sd->closure[i]; - ccl_private const HenyeyGreensteinVolume *from_hg = - (ccl_private const HenyeyGreensteinVolume *)from_sc; - - if (from_sc->type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) { - ccl_private ShaderVolumeClosure *to_sc = &phases->closure[phases->num_closure]; - - to_sc->weight = from_sc->weight; - to_sc->sample_weight = from_sc->sample_weight; - to_sc->g = from_hg->g; - phases->num_closure++; - if (phases->num_closure >= MAX_VOLUME_CLOSURE) { - break; - } - } - } -} -#endif /* __VOLUME__ */ - -ccl_device_inline void shader_prepare_surface_closures(KernelGlobals kg, - ConstIntegratorState state, - ccl_private ShaderData *sd, - const uint32_t path_flag) -{ - /* Filter out closures. */ - if (kernel_data.integrator.filter_closures) { - if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_EMISSION) { - sd->closure_emission_background = zero_spectrum(); - } - - if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_DIRECT_LIGHT) { - sd->flag &= ~SD_BSDF_HAS_EVAL; - } - - if (path_flag & PATH_RAY_CAMERA) { - for (int i = 0; i < sd->num_closure; i++) { - ccl_private ShaderClosure *sc = &sd->closure[i]; - - if ((CLOSURE_IS_BSDF_DIFFUSE(sc->type) && - (kernel_data.integrator.filter_closures & FILTER_CLOSURE_DIFFUSE)) || - (CLOSURE_IS_BSDF_GLOSSY(sc->type) && - (kernel_data.integrator.filter_closures & FILTER_CLOSURE_GLOSSY)) || - (CLOSURE_IS_BSDF_TRANSMISSION(sc->type) && - (kernel_data.integrator.filter_closures & FILTER_CLOSURE_TRANSMISSION))) { - sc->type = CLOSURE_NONE_ID; - sc->sample_weight = 0.0f; - } - else if ((CLOSURE_IS_BSDF_TRANSPARENT(sc->type) && - (kernel_data.integrator.filter_closures & FILTER_CLOSURE_TRANSPARENT))) { - sc->type = CLOSURE_HOLDOUT_ID; - sc->sample_weight = 0.0f; - sd->flag |= SD_HOLDOUT; - } - } - } - } - - /* Defensive sampling. - * - * We can likely also do defensive sampling at deeper bounces, particularly - * for cases like a perfect mirror but possibly also others. This will need - * a good heuristic. */ - if (INTEGRATOR_STATE(state, path, bounce) + INTEGRATOR_STATE(state, path, transparent_bounce) == - 0 && - sd->num_closure > 1) { - float sum = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private ShaderClosure *sc = &sd->closure[i]; - if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { - sum += sc->sample_weight; - } - } - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private ShaderClosure *sc = &sd->closure[i]; - if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { - sc->sample_weight = max(sc->sample_weight, 0.125f * sum); - } - } - } - - /* Filter glossy. - * - * Blurring of bsdf after bounces, for rays that have a small likelihood - * of following this particular path (diffuse, rough glossy) */ - if (kernel_data.integrator.filter_glossy != FLT_MAX -#ifdef __MNEE__ - && !(INTEGRATOR_STATE(state, path, mnee) & PATH_MNEE_VALID) -#endif - ) { - float blur_pdf = kernel_data.integrator.filter_glossy * - INTEGRATOR_STATE(state, path, min_ray_pdf); - - if (blur_pdf < 1.0f) { - float blur_roughness = sqrtf(1.0f - blur_pdf) * 0.5f; - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private ShaderClosure *sc = &sd->closure[i]; - if (CLOSURE_IS_BSDF(sc->type)) { - bsdf_blur(kg, sc, blur_roughness); - } - } - } - } -} - -/* BSDF */ - -ccl_device_inline bool shader_bsdf_is_transmission(ccl_private const ShaderData *sd, - const float3 omega_in) -{ - return dot(sd->N, omega_in) < 0.0f; -} - -ccl_device_forceinline bool _shader_bsdf_exclude(ClosureType type, uint light_shader_flags) -{ - if (!(light_shader_flags & SHADER_EXCLUDE_ANY)) { - return false; - } - if (light_shader_flags & SHADER_EXCLUDE_DIFFUSE) { - if (CLOSURE_IS_BSDF_DIFFUSE(type)) { - return true; - } - } - if (light_shader_flags & SHADER_EXCLUDE_GLOSSY) { - if (CLOSURE_IS_BSDF_GLOSSY(type)) { - return true; - } - } - if (light_shader_flags & SHADER_EXCLUDE_TRANSMIT) { - if (CLOSURE_IS_BSDF_TRANSMISSION(type)) { - return true; - } - } - return false; -} - -ccl_device_inline float _shader_bsdf_multi_eval(KernelGlobals kg, - ccl_private ShaderData *sd, - const float3 omega_in, - const bool is_transmission, - ccl_private const ShaderClosure *skip_sc, - ccl_private BsdfEval *result_eval, - float sum_pdf, - float sum_sample_weight, - const uint light_shader_flags) -{ - /* This is the veach one-sample model with balance heuristic, - * some PDF factors drop out when using balance heuristic weighting. */ - for (int i = 0; i < sd->num_closure; i++) { - ccl_private const ShaderClosure *sc = &sd->closure[i]; - - if (sc == skip_sc) { - continue; - } - - if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { - if (CLOSURE_IS_BSDF(sc->type) && !_shader_bsdf_exclude(sc->type, light_shader_flags)) { - float bsdf_pdf = 0.0f; - Spectrum eval = bsdf_eval(kg, sd, sc, omega_in, is_transmission, &bsdf_pdf); - - if (bsdf_pdf != 0.0f) { - bsdf_eval_accum(result_eval, sc->type, eval * sc->weight); - sum_pdf += bsdf_pdf * sc->sample_weight; - } - } - - sum_sample_weight += sc->sample_weight; - } - } - - return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f; -} - -#ifndef __KERNEL_CUDA__ -ccl_device -#else -ccl_device_inline -#endif - float - shader_bsdf_eval(KernelGlobals kg, - ccl_private ShaderData *sd, - const float3 omega_in, - const bool is_transmission, - ccl_private BsdfEval *bsdf_eval, - const uint light_shader_flags) -{ - bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, zero_spectrum()); - - return _shader_bsdf_multi_eval( - kg, sd, omega_in, is_transmission, NULL, bsdf_eval, 0.0f, 0.0f, light_shader_flags); -} - -/* Randomly sample a BSSRDF or BSDF proportional to ShaderClosure.sample_weight. */ -ccl_device_inline ccl_private const ShaderClosure *shader_bsdf_bssrdf_pick( - ccl_private const ShaderData *ccl_restrict sd, ccl_private float2 *rand_bsdf) -{ - int sampled = 0; - - if (sd->num_closure > 1) { - /* Pick a BSDF or based on sample weights. */ - float sum = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { - sum += sc->sample_weight; - } - } - - float r = (*rand_bsdf).x * sum; - float partial_sum = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { - float next_sum = partial_sum + sc->sample_weight; - - if (r < next_sum) { - sampled = i; - - /* Rescale to reuse for direction sample, to better preserve stratification. */ - (*rand_bsdf).x = (r - partial_sum) / sc->sample_weight; - break; - } - - partial_sum = next_sum; - } - } - } - - return &sd->closure[sampled]; -} - -/* Return weight for picked BSSRDF. */ -ccl_device_inline Spectrum -shader_bssrdf_sample_weight(ccl_private const ShaderData *ccl_restrict sd, - ccl_private const ShaderClosure *ccl_restrict bssrdf_sc) -{ - Spectrum weight = bssrdf_sc->weight; - - if (sd->num_closure > 1) { - float sum = 0.0f; - for (int i = 0; i < sd->num_closure; i++) { - ccl_private const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { - sum += sc->sample_weight; - } - } - weight *= sum / bssrdf_sc->sample_weight; - } - - return weight; -} - -/* Sample direction for picked BSDF, and return evaluation and pdf for all - * BSDFs combined using MIS. */ -ccl_device int shader_bsdf_sample_closure(KernelGlobals kg, - ccl_private ShaderData *sd, - ccl_private const ShaderClosure *sc, - const float2 rand_bsdf, - ccl_private BsdfEval *bsdf_eval, - ccl_private float3 *omega_in, - ccl_private float *pdf) -{ - /* BSSRDF should already have been handled elsewhere. */ - kernel_assert(CLOSURE_IS_BSDF(sc->type)); - - int label; - Spectrum eval = zero_spectrum(); - - *pdf = 0.0f; - label = bsdf_sample(kg, sd, sc, rand_bsdf.x, rand_bsdf.y, &eval, omega_in, pdf); - - if (*pdf != 0.0f) { - bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight); - - if (sd->num_closure > 1) { - const bool is_transmission = shader_bsdf_is_transmission(sd, *omega_in); - float sweight = sc->sample_weight; - *pdf = _shader_bsdf_multi_eval( - kg, sd, *omega_in, is_transmission, sc, bsdf_eval, *pdf * sweight, sweight, 0); - } - } - - return label; -} - -ccl_device float shader_bsdf_average_roughness(ccl_private const ShaderData *sd) -{ - float roughness = 0.0f; - float sum_weight = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF(sc->type)) { - /* sqrt once to undo the squaring from multiplying roughness on the - * two axes, and once for the squared roughness convention. */ - float weight = fabsf(average(sc->weight)); - roughness += weight * sqrtf(safe_sqrtf(bsdf_get_roughness_squared(sc))); - sum_weight += weight; - } - } - - return (sum_weight > 0.0f) ? roughness / sum_weight : 0.0f; -} - -ccl_device Spectrum shader_bsdf_transparency(KernelGlobals kg, ccl_private const ShaderData *sd) -{ - if (sd->flag & SD_HAS_ONLY_VOLUME) { - return one_spectrum(); - } - else if (sd->flag & SD_TRANSPARENT) { - return sd->closure_transparent_extinction; - } - else { - return zero_spectrum(); - } -} - -ccl_device void shader_bsdf_disable_transparency(KernelGlobals kg, ccl_private ShaderData *sd) -{ - if (sd->flag & SD_TRANSPARENT) { - for (int i = 0; i < sd->num_closure; i++) { - ccl_private ShaderClosure *sc = &sd->closure[i]; - - if (sc->type == CLOSURE_BSDF_TRANSPARENT_ID) { - sc->sample_weight = 0.0f; - sc->weight = zero_spectrum(); - } - } - - sd->flag &= ~SD_TRANSPARENT; - } -} - -ccl_device Spectrum shader_bsdf_alpha(KernelGlobals kg, ccl_private const ShaderData *sd) -{ - Spectrum alpha = one_spectrum() - shader_bsdf_transparency(kg, sd); - - alpha = saturate(alpha); - - return alpha; -} - -ccl_device Spectrum shader_bsdf_diffuse(KernelGlobals kg, ccl_private const ShaderData *sd) -{ - Spectrum eval = zero_spectrum(); - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || CLOSURE_IS_BSSRDF(sc->type)) - eval += sc->weight; - } - - return eval; -} - -ccl_device Spectrum shader_bsdf_glossy(KernelGlobals kg, ccl_private const ShaderData *sd) -{ - Spectrum eval = zero_spectrum(); - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) - eval += sc->weight; - } - - return eval; -} - -ccl_device Spectrum shader_bsdf_transmission(KernelGlobals kg, ccl_private const ShaderData *sd) -{ - Spectrum eval = zero_spectrum(); - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF_TRANSMISSION(sc->type)) - eval += sc->weight; - } - - return eval; -} - -ccl_device float3 shader_bsdf_average_normal(KernelGlobals kg, ccl_private const ShaderData *sd) -{ - float3 N = zero_float3(); - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private const ShaderClosure *sc = &sd->closure[i]; - if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) - N += sc->N * fabsf(average(sc->weight)); - } - - return (is_zero(N)) ? sd->N : normalize(N); -} - -ccl_device Spectrum shader_bsdf_ao(KernelGlobals kg, - ccl_private const ShaderData *sd, - const float ao_factor, - ccl_private float3 *N_) -{ - Spectrum eval = zero_spectrum(); - float3 N = zero_float3(); - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) { - ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; - eval += sc->weight * ao_factor; - N += bsdf->N * fabsf(average(sc->weight)); - } - } - - *N_ = (is_zero(N)) ? sd->N : normalize(N); - return eval; -} - -#ifdef __SUBSURFACE__ -ccl_device float3 shader_bssrdf_normal(ccl_private const ShaderData *sd) -{ - float3 N = zero_float3(); - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSSRDF(sc->type)) { - ccl_private const Bssrdf *bssrdf = (ccl_private const Bssrdf *)sc; - float avg_weight = fabsf(average(sc->weight)); - - N += bssrdf->N * avg_weight; - } - } - - return (is_zero(N)) ? sd->N : normalize(N); -} -#endif /* __SUBSURFACE__ */ - -/* Constant emission optimization */ - -ccl_device bool shader_constant_emission_eval(KernelGlobals kg, - int shader, - ccl_private Spectrum *eval) -{ - int shader_index = shader & SHADER_MASK; - int shader_flag = kernel_data_fetch(shaders, shader_index).flags; - - if (shader_flag & SD_HAS_CONSTANT_EMISSION) { - const float3 emission_rgb = make_float3( - kernel_data_fetch(shaders, shader_index).constant_emission[0], - kernel_data_fetch(shaders, shader_index).constant_emission[1], - kernel_data_fetch(shaders, shader_index).constant_emission[2]); - *eval = rgb_to_spectrum(emission_rgb); - - return true; - } - - return false; -} - -/* Background */ - -ccl_device Spectrum shader_background_eval(ccl_private const ShaderData *sd) -{ - if (sd->flag & SD_EMISSION) { - return sd->closure_emission_background; - } - else { - return zero_spectrum(); - } -} - -/* Emission */ - -ccl_device Spectrum shader_emissive_eval(ccl_private const ShaderData *sd) -{ - if (sd->flag & SD_EMISSION) { - return emissive_simple_eval(sd->Ng, sd->I) * sd->closure_emission_background; - } - else { - return zero_spectrum(); - } -} - -/* Holdout */ - -ccl_device Spectrum shader_holdout_apply(KernelGlobals kg, ccl_private ShaderData *sd) -{ - Spectrum weight = zero_spectrum(); - - /* For objects marked as holdout, preserve transparency and remove all other - * closures, replacing them with a holdout weight. */ - if (sd->object_flag & SD_OBJECT_HOLDOUT_MASK) { - if ((sd->flag & SD_TRANSPARENT) && !(sd->flag & SD_HAS_ONLY_VOLUME)) { - weight = one_spectrum() - sd->closure_transparent_extinction; - - for (int i = 0; i < sd->num_closure; i++) { - ccl_private ShaderClosure *sc = &sd->closure[i]; - if (!CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) { - sc->type = NBUILTIN_CLOSURES; - } - } - - sd->flag &= ~(SD_CLOSURE_FLAGS - (SD_TRANSPARENT | SD_BSDF)); - } - else { - weight = one_spectrum(); - } - } - else { - for (int i = 0; i < sd->num_closure; i++) { - ccl_private const ShaderClosure *sc = &sd->closure[i]; - if (CLOSURE_IS_HOLDOUT(sc->type)) { - weight += sc->weight; - } - } - } - - return weight; -} - -/* Surface Evaluation */ - -template<uint node_feature_mask, typename ConstIntegratorGenericState> -ccl_device void shader_eval_surface(KernelGlobals kg, - ConstIntegratorGenericState state, - ccl_private ShaderData *ccl_restrict sd, - ccl_global float *ccl_restrict buffer, - uint32_t path_flag, - bool use_caustics_storage = false) -{ - /* If path is being terminated, we are tracing a shadow ray or evaluating - * emission, then we don't need to store closures. The emission and shadow - * shader data also do not have a closure array to save GPU memory. */ - int max_closures; - if (path_flag & (PATH_RAY_TERMINATE | PATH_RAY_SHADOW | PATH_RAY_EMISSION)) { - max_closures = 0; - } - else { - max_closures = use_caustics_storage ? CAUSTICS_MAX_CLOSURE : kernel_data.max_closures; - } - - sd->num_closure = 0; - sd->num_closure_left = max_closures; - -#ifdef __OSL__ - if (kg->osl) { - if (sd->object == OBJECT_NONE && sd->lamp == LAMP_NONE) { - OSLShader::eval_background(kg, state, sd, path_flag); - } - else { - OSLShader::eval_surface(kg, state, sd, path_flag); - } - } - else -#endif - { -#ifdef __SVM__ - svm_eval_nodes<node_feature_mask, SHADER_TYPE_SURFACE>(kg, state, sd, buffer, path_flag); -#else - if (sd->object == OBJECT_NONE) { - sd->closure_emission_background = make_spectrum(0.8f); - sd->flag |= SD_EMISSION; - } - else { - ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( - sd, sizeof(DiffuseBsdf), make_spectrum(0.8f)); - if (bsdf != NULL) { - bsdf->N = sd->N; - sd->flag |= bsdf_diffuse_setup(bsdf); - } - } -#endif - } -} - -/* Volume */ - -#ifdef __VOLUME__ - -ccl_device_inline float _shader_volume_phase_multi_eval( - ccl_private const ShaderData *sd, - ccl_private const ShaderVolumePhases *phases, - const float3 omega_in, - int skip_phase, - ccl_private BsdfEval *result_eval, - float sum_pdf, - float sum_sample_weight) -{ - for (int i = 0; i < phases->num_closure; i++) { - if (i == skip_phase) - continue; - - ccl_private const ShaderVolumeClosure *svc = &phases->closure[i]; - float phase_pdf = 0.0f; - Spectrum eval = volume_phase_eval(sd, svc, omega_in, &phase_pdf); - - if (phase_pdf != 0.0f) { - bsdf_eval_accum(result_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval); - sum_pdf += phase_pdf * svc->sample_weight; - } - - sum_sample_weight += svc->sample_weight; - } - - return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f; -} - -ccl_device float shader_volume_phase_eval(KernelGlobals kg, - ccl_private const ShaderData *sd, - ccl_private const ShaderVolumePhases *phases, - const float3 omega_in, - ccl_private BsdfEval *phase_eval) -{ - bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum()); - - return _shader_volume_phase_multi_eval(sd, phases, omega_in, -1, phase_eval, 0.0f, 0.0f); -} - -ccl_device int shader_volume_phase_sample(KernelGlobals kg, - ccl_private const ShaderData *sd, - ccl_private const ShaderVolumePhases *phases, - float2 rand_phase, - ccl_private BsdfEval *phase_eval, - ccl_private float3 *omega_in, - ccl_private float *pdf) -{ - int sampled = 0; - - if (phases->num_closure > 1) { - /* pick a phase closure based on sample weights */ - float sum = 0.0f; - - for (sampled = 0; sampled < phases->num_closure; sampled++) { - ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled]; - sum += svc->sample_weight; - } - - float r = rand_phase.x * sum; - float partial_sum = 0.0f; - - for (sampled = 0; sampled < phases->num_closure; sampled++) { - ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled]; - float next_sum = partial_sum + svc->sample_weight; - - if (r <= next_sum) { - /* Rescale to reuse for BSDF direction sample. */ - rand_phase.x = (r - partial_sum) / svc->sample_weight; - break; - } - - partial_sum = next_sum; - } - - if (sampled == phases->num_closure) { - *pdf = 0.0f; - return LABEL_NONE; - } - } - - /* todo: this isn't quite correct, we don't weight anisotropy properly - * depending on color channels, even if this is perhaps not a common case */ - ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled]; - int label; - Spectrum eval = zero_spectrum(); - - *pdf = 0.0f; - label = volume_phase_sample(sd, svc, rand_phase.x, rand_phase.y, &eval, omega_in, pdf); - - if (*pdf != 0.0f) { - bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval); - } - - return label; -} - -ccl_device int shader_phase_sample_closure(KernelGlobals kg, - ccl_private const ShaderData *sd, - ccl_private const ShaderVolumeClosure *sc, - const float2 rand_phase, - ccl_private BsdfEval *phase_eval, - ccl_private float3 *omega_in, - ccl_private float *pdf) -{ - int label; - Spectrum eval = zero_spectrum(); - - *pdf = 0.0f; - label = volume_phase_sample(sd, sc, rand_phase.x, rand_phase.y, &eval, omega_in, pdf); - - if (*pdf != 0.0f) - bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval); - - return label; -} - -/* Volume Evaluation */ - -template<const bool shadow, typename StackReadOp, typename ConstIntegratorGenericState> -ccl_device_inline void shader_eval_volume(KernelGlobals kg, - ConstIntegratorGenericState state, - ccl_private ShaderData *ccl_restrict sd, - const uint32_t path_flag, - StackReadOp stack_read) -{ - /* If path is being terminated, we are tracing a shadow ray or evaluating - * emission, then we don't need to store closures. The emission and shadow - * shader data also do not have a closure array to save GPU memory. */ - int max_closures; - if (path_flag & (PATH_RAY_TERMINATE | PATH_RAY_SHADOW | PATH_RAY_EMISSION)) { - max_closures = 0; - } - else { - max_closures = kernel_data.max_closures; - } - - /* reset closures once at the start, we will be accumulating the closures - * for all volumes in the stack into a single array of closures */ - sd->num_closure = 0; - sd->num_closure_left = max_closures; - sd->flag = 0; - sd->object_flag = 0; - - for (int i = 0;; i++) { - const VolumeStack entry = stack_read(i); - if (entry.shader == SHADER_NONE) { - break; - } - - /* Setup shader-data from stack. it's mostly setup already in - * shader_setup_from_volume, this switching should be quick. */ - sd->object = entry.object; - sd->lamp = LAMP_NONE; - sd->shader = entry.shader; - - sd->flag &= ~SD_SHADER_FLAGS; - sd->flag |= kernel_data_fetch(shaders, (sd->shader & SHADER_MASK)).flags; - sd->object_flag &= ~SD_OBJECT_FLAGS; - - if (sd->object != OBJECT_NONE) { - sd->object_flag |= kernel_data_fetch(object_flag, sd->object); - -# ifdef __OBJECT_MOTION__ - /* todo: this is inefficient for motion blur, we should be - * caching matrices instead of recomputing them each step */ - shader_setup_object_transforms(kg, sd, sd->time); - - if ((sd->object_flag & SD_OBJECT_HAS_VOLUME_MOTION) != 0) { - AttributeDescriptor v_desc = find_attribute(kg, sd, ATTR_STD_VOLUME_VELOCITY); - kernel_assert(v_desc.offset != ATTR_STD_NOT_FOUND); - - const float3 P = sd->P; - const float velocity_scale = kernel_data_fetch(objects, sd->object).velocity_scale; - const float time_offset = kernel_data.cam.motion_position == MOTION_POSITION_CENTER ? - 0.5f : - 0.0f; - const float time = kernel_data.cam.motion_position == MOTION_POSITION_END ? - (1.0f - kernel_data.cam.shuttertime) + sd->time : - sd->time; - - /* Use a 1st order semi-lagrangian advection scheme to estimate what volume quantity - * existed, or will exist, at the given time: - * - * `phi(x, T) = phi(x - (T - t) * u(x, T), t)` - * - * where - * - * x : position - * T : super-sampled time (or ray time) - * t : current time of the simulation (in rendering we assume this is center frame with - * relative time = 0) - * phi : the volume quantity - * u : the velocity field - * - * But first we need to determine the velocity field `u(x, T)`, which we can estimate also - * using semi-lagrangian advection. - * - * `u(x, T) = u(x - (T - t) * u(x, T), t)` - * - * This is the typical way to model self-advection in fluid dynamics, however, we do not - * account for other forces affecting the velocity during simulation (pressure, buoyancy, - * etc.): this gives a linear interpolation when fluid are mostly "curvy". For better - * results, a higher order interpolation scheme can be used (at the cost of more lookups), - * or an interpolation of the velocity fields for the previous and next frames could also - * be used to estimate `u(x, T)` (which will cost more memory and lookups). - * - * References: - * "Eulerian Motion Blur", Kim and Ko, 2007 - * "Production Volume Rendering", Wreninge et al., 2012 - */ - - /* Find velocity. */ - float3 velocity = primitive_volume_attribute_float3(kg, sd, v_desc); - object_dir_transform(kg, sd, &velocity); - - /* Find advected P. */ - sd->P = P - (time - time_offset) * velocity_scale * velocity; - - /* Find advected velocity. */ - velocity = primitive_volume_attribute_float3(kg, sd, v_desc); - object_dir_transform(kg, sd, &velocity); - - /* Find advected P. */ - sd->P = P - (time - time_offset) * velocity_scale * velocity; - } -# endif - } - - /* evaluate shader */ -# ifdef __SVM__ -# ifdef __OSL__ - if (kg->osl) { - OSLShader::eval_volume(kg, state, sd, path_flag); - } - else -# endif - { - svm_eval_nodes<KERNEL_FEATURE_NODE_MASK_VOLUME, SHADER_TYPE_VOLUME>( - kg, state, sd, NULL, path_flag); - } -# endif - - /* Merge closures to avoid exceeding number of closures limit. */ - if (!shadow) { - if (i > 0) { - shader_merge_volume_closures(sd); - } - } - } -} - -#endif /* __VOLUME__ */ - -/* Displacement Evaluation */ - -template<typename ConstIntegratorGenericState> -ccl_device void shader_eval_displacement(KernelGlobals kg, - ConstIntegratorGenericState state, - ccl_private ShaderData *sd) -{ - sd->num_closure = 0; - sd->num_closure_left = 0; - - /* this will modify sd->P */ -#ifdef __SVM__ -# ifdef __OSL__ - if (kg->osl) - OSLShader::eval_displacement(kg, state, sd); - else -# endif - { - svm_eval_nodes<KERNEL_FEATURE_NODE_MASK_DISPLACEMENT, SHADER_TYPE_DISPLACEMENT>( - kg, state, sd, NULL, 0); - } -#endif -} - -/* Cryptomatte */ - -ccl_device float shader_cryptomatte_id(KernelGlobals kg, int shader) -{ - return kernel_data_fetch(shaders, (shader & SHADER_MASK)).cryptomatte_id; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/shadow_catcher.h b/intern/cycles/kernel/integrator/shadow_catcher.h index 7103b6032ac..a620853faea 100644 --- a/intern/cycles/kernel/integrator/shadow_catcher.h +++ b/intern/cycles/kernel/integrator/shadow_catcher.h @@ -3,7 +3,6 @@ #pragma once -#include "kernel/film/write_passes.h" #include "kernel/integrator/path_state.h" #include "kernel/integrator/state_util.h" @@ -76,28 +75,6 @@ ccl_device_forceinline bool kernel_shadow_catcher_is_object_pass(const uint32_t return path_flag & PATH_RAY_SHADOW_CATCHER_PASS; } -/* Write shadow catcher passes on a bounce from the shadow catcher object. */ -ccl_device_forceinline void kernel_write_shadow_catcher_bounce_data( - KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer) -{ - kernel_assert(kernel_data.film.pass_shadow_catcher_sample_count != PASS_UNUSED); - kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); - - const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); - const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * - kernel_data.film.pass_stride; - ccl_global float *buffer = render_buffer + render_buffer_offset; - - /* Count sample for the shadow catcher object. */ - kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_sample_count, 1.0f); - - /* Since the split is done, the sample does not contribute to the matte, so accumulate it as - * transparency to the matte. */ - const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); - kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, - average(throughput)); -} - #endif /* __SHADOW_CATCHER__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/subsurface.h b/intern/cycles/kernel/integrator/subsurface.h index ee3a619f968..15c2cb1c708 100644 --- a/intern/cycles/kernel/integrator/subsurface.h +++ b/intern/cycles/kernel/integrator/subsurface.h @@ -15,9 +15,9 @@ #include "kernel/integrator/intersect_volume_stack.h" #include "kernel/integrator/path_state.h" -#include "kernel/integrator/shader_eval.h" #include "kernel/integrator/subsurface_disk.h" #include "kernel/integrator/subsurface_random_walk.h" +#include "kernel/integrator/surface_shader.h" CCL_NAMESPACE_BEGIN @@ -51,12 +51,10 @@ ccl_device int subsurface_bounce(KernelGlobals kg, PATH_RAY_SUBSURFACE_RANDOM_WALK); /* Compute weight, optionally including Fresnel from entry point. */ - Spectrum weight = shader_bssrdf_sample_weight(sd, sc); -# ifdef __PRINCIPLED__ + Spectrum weight = surface_shader_bssrdf_sample_weight(sd, sc); if (bssrdf->roughness != FLT_MAX) { path_flag |= PATH_RAY_SUBSURFACE_USE_FRESNEL; } -# endif if (sd->flag & SD_BACKFACING) { path_flag |= PATH_RAY_SUBSURFACE_BACKFACING; @@ -91,7 +89,7 @@ ccl_device void subsurface_shader_data_setup(KernelGlobals kg, /* Get bump mapped normal from shader evaluation at exit point. */ float3 N = sd->N; if (sd->flag & SD_HAS_BSSRDF_BUMP) { - N = shader_bssrdf_normal(sd); + N = surface_shader_bssrdf_normal(sd); } /* Setup diffuse BSDF at the exit point. This replaces shader_eval_surface. */ @@ -101,7 +99,6 @@ ccl_device void subsurface_shader_data_setup(KernelGlobals kg, const Spectrum weight = one_spectrum(); -# ifdef __PRINCIPLED__ if (path_flag & PATH_RAY_SUBSURFACE_USE_FRESNEL) { ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *)bsdf_alloc( sd, sizeof(PrincipledDiffuseBsdf), weight); @@ -112,9 +109,7 @@ ccl_device void subsurface_shader_data_setup(KernelGlobals kg, sd->flag |= bsdf_principled_diffuse_setup(bsdf, PRINCIPLED_DIFFUSE_LAMBERT_EXIT); } } - else -# endif /* __PRINCIPLED__ */ - { + else { ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( sd, sizeof(DiffuseBsdf), weight); diff --git a/intern/cycles/kernel/integrator/surface_shader.h b/intern/cycles/kernel/integrator/surface_shader.h new file mode 100644 index 00000000000..f40ff3c33ee --- /dev/null +++ b/intern/cycles/kernel/integrator/surface_shader.h @@ -0,0 +1,587 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +/* Functions to evaluate shaders. */ + +#pragma once + +#include "kernel/closure/alloc.h" +#include "kernel/closure/bsdf.h" +#include "kernel/closure/bsdf_util.h" +#include "kernel/closure/emissive.h" + +#include "kernel/svm/svm.h" + +#ifdef __OSL__ +# include "kernel/osl/shader.h" +#endif + +CCL_NAMESPACE_BEGIN + +ccl_device_inline void surface_shader_prepare_closures(KernelGlobals kg, + ConstIntegratorState state, + ccl_private ShaderData *sd, + const uint32_t path_flag) +{ + /* Filter out closures. */ + if (kernel_data.integrator.filter_closures) { + if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_EMISSION) { + sd->closure_emission_background = zero_spectrum(); + } + + if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_DIRECT_LIGHT) { + sd->flag &= ~SD_BSDF_HAS_EVAL; + } + + if (path_flag & PATH_RAY_CAMERA) { + for (int i = 0; i < sd->num_closure; i++) { + ccl_private ShaderClosure *sc = &sd->closure[i]; + + if ((CLOSURE_IS_BSDF_DIFFUSE(sc->type) && + (kernel_data.integrator.filter_closures & FILTER_CLOSURE_DIFFUSE)) || + (CLOSURE_IS_BSDF_GLOSSY(sc->type) && + (kernel_data.integrator.filter_closures & FILTER_CLOSURE_GLOSSY)) || + (CLOSURE_IS_BSDF_TRANSMISSION(sc->type) && + (kernel_data.integrator.filter_closures & FILTER_CLOSURE_TRANSMISSION))) { + sc->type = CLOSURE_NONE_ID; + sc->sample_weight = 0.0f; + } + else if ((CLOSURE_IS_BSDF_TRANSPARENT(sc->type) && + (kernel_data.integrator.filter_closures & FILTER_CLOSURE_TRANSPARENT))) { + sc->type = CLOSURE_HOLDOUT_ID; + sc->sample_weight = 0.0f; + sd->flag |= SD_HOLDOUT; + } + } + } + } + + /* Defensive sampling. + * + * We can likely also do defensive sampling at deeper bounces, particularly + * for cases like a perfect mirror but possibly also others. This will need + * a good heuristic. */ + if (INTEGRATOR_STATE(state, path, bounce) + INTEGRATOR_STATE(state, path, transparent_bounce) == + 0 && + sd->num_closure > 1) { + float sum = 0.0f; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private ShaderClosure *sc = &sd->closure[i]; + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + sum += sc->sample_weight; + } + } + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private ShaderClosure *sc = &sd->closure[i]; + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + sc->sample_weight = max(sc->sample_weight, 0.125f * sum); + } + } + } + + /* Filter glossy. + * + * Blurring of bsdf after bounces, for rays that have a small likelihood + * of following this particular path (diffuse, rough glossy) */ + if (kernel_data.integrator.filter_glossy != FLT_MAX +#ifdef __MNEE__ + && !(INTEGRATOR_STATE(state, path, mnee) & PATH_MNEE_VALID) +#endif + ) { + float blur_pdf = kernel_data.integrator.filter_glossy * + INTEGRATOR_STATE(state, path, min_ray_pdf); + + if (blur_pdf < 1.0f) { + float blur_roughness = sqrtf(1.0f - blur_pdf) * 0.5f; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private ShaderClosure *sc = &sd->closure[i]; + if (CLOSURE_IS_BSDF(sc->type)) { + bsdf_blur(kg, sc, blur_roughness); + } + } + } + } +} + +/* BSDF */ + +ccl_device_inline bool surface_shader_is_transmission(ccl_private const ShaderData *sd, + const float3 omega_in) +{ + return dot(sd->N, omega_in) < 0.0f; +} + +ccl_device_forceinline bool _surface_shader_exclude(ClosureType type, uint light_shader_flags) +{ + if (!(light_shader_flags & SHADER_EXCLUDE_ANY)) { + return false; + } + if (light_shader_flags & SHADER_EXCLUDE_DIFFUSE) { + if (CLOSURE_IS_BSDF_DIFFUSE(type)) { + return true; + } + } + if (light_shader_flags & SHADER_EXCLUDE_GLOSSY) { + if (CLOSURE_IS_BSDF_GLOSSY(type)) { + return true; + } + } + if (light_shader_flags & SHADER_EXCLUDE_TRANSMIT) { + if (CLOSURE_IS_BSDF_TRANSMISSION(type)) { + return true; + } + } + return false; +} + +ccl_device_inline float _surface_shader_bsdf_eval_mis(KernelGlobals kg, + ccl_private ShaderData *sd, + const float3 omega_in, + const bool is_transmission, + ccl_private const ShaderClosure *skip_sc, + ccl_private BsdfEval *result_eval, + float sum_pdf, + float sum_sample_weight, + const uint light_shader_flags) +{ + /* This is the veach one-sample model with balance heuristic, + * some PDF factors drop out when using balance heuristic weighting. */ + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (sc == skip_sc) { + continue; + } + + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + if (CLOSURE_IS_BSDF(sc->type) && !_surface_shader_exclude(sc->type, light_shader_flags)) { + float bsdf_pdf = 0.0f; + Spectrum eval = bsdf_eval(kg, sd, sc, omega_in, is_transmission, &bsdf_pdf); + + if (bsdf_pdf != 0.0f) { + bsdf_eval_accum(result_eval, sc->type, eval * sc->weight); + sum_pdf += bsdf_pdf * sc->sample_weight; + } + } + + sum_sample_weight += sc->sample_weight; + } + } + + return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f; +} + +#ifndef __KERNEL_CUDA__ +ccl_device +#else +ccl_device_inline +#endif + float + surface_shader_bsdf_eval(KernelGlobals kg, + ccl_private ShaderData *sd, + const float3 omega_in, + const bool is_transmission, + ccl_private BsdfEval *bsdf_eval, + const uint light_shader_flags) +{ + bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, zero_spectrum()); + + return _surface_shader_bsdf_eval_mis( + kg, sd, omega_in, is_transmission, NULL, bsdf_eval, 0.0f, 0.0f, light_shader_flags); +} + +/* Randomly sample a BSSRDF or BSDF proportional to ShaderClosure.sample_weight. */ +ccl_device_inline ccl_private const ShaderClosure *surface_shader_bsdf_bssrdf_pick( + ccl_private const ShaderData *ccl_restrict sd, ccl_private float2 *rand_bsdf) +{ + int sampled = 0; + + if (sd->num_closure > 1) { + /* Pick a BSDF or based on sample weights. */ + float sum = 0.0f; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + sum += sc->sample_weight; + } + } + + float r = (*rand_bsdf).x * sum; + float partial_sum = 0.0f; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + float next_sum = partial_sum + sc->sample_weight; + + if (r < next_sum) { + sampled = i; + + /* Rescale to reuse for direction sample, to better preserve stratification. */ + (*rand_bsdf).x = (r - partial_sum) / sc->sample_weight; + break; + } + + partial_sum = next_sum; + } + } + } + + return &sd->closure[sampled]; +} + +/* Return weight for picked BSSRDF. */ +ccl_device_inline Spectrum +surface_shader_bssrdf_sample_weight(ccl_private const ShaderData *ccl_restrict sd, + ccl_private const ShaderClosure *ccl_restrict bssrdf_sc) +{ + Spectrum weight = bssrdf_sc->weight; + + if (sd->num_closure > 1) { + float sum = 0.0f; + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + sum += sc->sample_weight; + } + } + weight *= sum / bssrdf_sc->sample_weight; + } + + return weight; +} + +/* Sample direction for picked BSDF, and return evaluation and pdf for all + * BSDFs combined using MIS. */ +ccl_device int surface_shader_bsdf_sample_closure(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private const ShaderClosure *sc, + const float2 rand_bsdf, + ccl_private BsdfEval *bsdf_eval, + ccl_private float3 *omega_in, + ccl_private float *pdf) +{ + /* BSSRDF should already have been handled elsewhere. */ + kernel_assert(CLOSURE_IS_BSDF(sc->type)); + + int label; + Spectrum eval = zero_spectrum(); + + *pdf = 0.0f; + label = bsdf_sample(kg, sd, sc, rand_bsdf.x, rand_bsdf.y, &eval, omega_in, pdf); + + if (*pdf != 0.0f) { + bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight); + + if (sd->num_closure > 1) { + const bool is_transmission = surface_shader_is_transmission(sd, *omega_in); + float sweight = sc->sample_weight; + *pdf = _surface_shader_bsdf_eval_mis( + kg, sd, *omega_in, is_transmission, sc, bsdf_eval, *pdf * sweight, sweight, 0); + } + } + + return label; +} + +ccl_device float surface_shader_average_roughness(ccl_private const ShaderData *sd) +{ + float roughness = 0.0f; + float sum_weight = 0.0f; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF(sc->type)) { + /* sqrt once to undo the squaring from multiplying roughness on the + * two axes, and once for the squared roughness convention. */ + float weight = fabsf(average(sc->weight)); + roughness += weight * sqrtf(safe_sqrtf(bsdf_get_roughness_squared(sc))); + sum_weight += weight; + } + } + + return (sum_weight > 0.0f) ? roughness / sum_weight : 0.0f; +} + +ccl_device Spectrum surface_shader_transparency(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + if (sd->flag & SD_HAS_ONLY_VOLUME) { + return one_spectrum(); + } + else if (sd->flag & SD_TRANSPARENT) { + return sd->closure_transparent_extinction; + } + else { + return zero_spectrum(); + } +} + +ccl_device void surface_shader_disable_transparency(KernelGlobals kg, ccl_private ShaderData *sd) +{ + if (sd->flag & SD_TRANSPARENT) { + for (int i = 0; i < sd->num_closure; i++) { + ccl_private ShaderClosure *sc = &sd->closure[i]; + + if (sc->type == CLOSURE_BSDF_TRANSPARENT_ID) { + sc->sample_weight = 0.0f; + sc->weight = zero_spectrum(); + } + } + + sd->flag &= ~SD_TRANSPARENT; + } +} + +ccl_device Spectrum surface_shader_alpha(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + Spectrum alpha = one_spectrum() - surface_shader_transparency(kg, sd); + + alpha = saturate(alpha); + + return alpha; +} + +ccl_device Spectrum surface_shader_diffuse(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + Spectrum eval = zero_spectrum(); + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || CLOSURE_IS_BSSRDF(sc->type)) + eval += sc->weight; + } + + return eval; +} + +ccl_device Spectrum surface_shader_glossy(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + Spectrum eval = zero_spectrum(); + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) + eval += sc->weight; + } + + return eval; +} + +ccl_device Spectrum surface_shader_transmission(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + Spectrum eval = zero_spectrum(); + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF_TRANSMISSION(sc->type)) + eval += sc->weight; + } + + return eval; +} + +ccl_device float3 surface_shader_average_normal(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + float3 N = zero_float3(); + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) + N += sc->N * fabsf(average(sc->weight)); + } + + return (is_zero(N)) ? sd->N : normalize(N); +} + +ccl_device Spectrum surface_shader_ao(KernelGlobals kg, + ccl_private const ShaderData *sd, + const float ao_factor, + ccl_private float3 *N_) +{ + Spectrum eval = zero_spectrum(); + float3 N = zero_float3(); + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) { + ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; + eval += sc->weight * ao_factor; + N += bsdf->N * fabsf(average(sc->weight)); + } + } + + *N_ = (is_zero(N)) ? sd->N : normalize(N); + return eval; +} + +#ifdef __SUBSURFACE__ +ccl_device float3 surface_shader_bssrdf_normal(ccl_private const ShaderData *sd) +{ + float3 N = zero_float3(); + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSSRDF(sc->type)) { + ccl_private const Bssrdf *bssrdf = (ccl_private const Bssrdf *)sc; + float avg_weight = fabsf(average(sc->weight)); + + N += bssrdf->N * avg_weight; + } + } + + return (is_zero(N)) ? sd->N : normalize(N); +} +#endif /* __SUBSURFACE__ */ + +/* Constant emission optimization */ + +ccl_device bool surface_shader_constant_emission(KernelGlobals kg, + int shader, + ccl_private Spectrum *eval) +{ + int shader_index = shader & SHADER_MASK; + int shader_flag = kernel_data_fetch(shaders, shader_index).flags; + + if (shader_flag & SD_HAS_CONSTANT_EMISSION) { + const float3 emission_rgb = make_float3( + kernel_data_fetch(shaders, shader_index).constant_emission[0], + kernel_data_fetch(shaders, shader_index).constant_emission[1], + kernel_data_fetch(shaders, shader_index).constant_emission[2]); + *eval = rgb_to_spectrum(emission_rgb); + + return true; + } + + return false; +} + +/* Background */ + +ccl_device Spectrum surface_shader_background(ccl_private const ShaderData *sd) +{ + if (sd->flag & SD_EMISSION) { + return sd->closure_emission_background; + } + else { + return zero_spectrum(); + } +} + +/* Emission */ + +ccl_device Spectrum surface_shader_emission(ccl_private const ShaderData *sd) +{ + if (sd->flag & SD_EMISSION) { + return emissive_simple_eval(sd->Ng, sd->I) * sd->closure_emission_background; + } + else { + return zero_spectrum(); + } +} + +/* Holdout */ + +ccl_device Spectrum surface_shader_apply_holdout(KernelGlobals kg, ccl_private ShaderData *sd) +{ + Spectrum weight = zero_spectrum(); + + /* For objects marked as holdout, preserve transparency and remove all other + * closures, replacing them with a holdout weight. */ + if (sd->object_flag & SD_OBJECT_HOLDOUT_MASK) { + if ((sd->flag & SD_TRANSPARENT) && !(sd->flag & SD_HAS_ONLY_VOLUME)) { + weight = one_spectrum() - sd->closure_transparent_extinction; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private ShaderClosure *sc = &sd->closure[i]; + if (!CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) { + sc->type = NBUILTIN_CLOSURES; + } + } + + sd->flag &= ~(SD_CLOSURE_FLAGS - (SD_TRANSPARENT | SD_BSDF)); + } + else { + weight = one_spectrum(); + } + } + else { + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + if (CLOSURE_IS_HOLDOUT(sc->type)) { + weight += sc->weight; + } + } + } + + return weight; +} + +/* Surface Evaluation */ + +template<uint node_feature_mask, typename ConstIntegratorGenericState> +ccl_device void surface_shader_eval(KernelGlobals kg, + ConstIntegratorGenericState state, + ccl_private ShaderData *ccl_restrict sd, + ccl_global float *ccl_restrict buffer, + uint32_t path_flag, + bool use_caustics_storage = false) +{ + /* If path is being terminated, we are tracing a shadow ray or evaluating + * emission, then we don't need to store closures. The emission and shadow + * shader data also do not have a closure array to save GPU memory. */ + int max_closures; + if (path_flag & (PATH_RAY_TERMINATE | PATH_RAY_SHADOW | PATH_RAY_EMISSION)) { + max_closures = 0; + } + else { + max_closures = use_caustics_storage ? CAUSTICS_MAX_CLOSURE : kernel_data.max_closures; + } + + sd->num_closure = 0; + sd->num_closure_left = max_closures; + +#ifdef __OSL__ + if (kg->osl) { + if (sd->object == OBJECT_NONE && sd->lamp == LAMP_NONE) { + OSLShader::eval_background(kg, state, sd, path_flag); + } + else { + OSLShader::eval_surface(kg, state, sd, path_flag); + } + } + else +#endif + { +#ifdef __SVM__ + svm_eval_nodes<node_feature_mask, SHADER_TYPE_SURFACE>(kg, state, sd, buffer, path_flag); +#else + if (sd->object == OBJECT_NONE) { + sd->closure_emission_background = make_spectrum(0.8f); + sd->flag |= SD_EMISSION; + } + else { + ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( + sd, sizeof(DiffuseBsdf), make_spectrum(0.8f)); + if (bsdf != NULL) { + bsdf->N = sd->N; + sd->flag |= bsdf_diffuse_setup(bsdf); + } + } +#endif + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/volume_shader.h b/intern/cycles/kernel/integrator/volume_shader.h new file mode 100644 index 00000000000..a1d191e2d32 --- /dev/null +++ b/intern/cycles/kernel/integrator/volume_shader.h @@ -0,0 +1,353 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +/* Volume shader evaluation and sampling. */ + +#pragma once + +#include "kernel/closure/alloc.h" +#include "kernel/closure/bsdf.h" +#include "kernel/closure/bsdf_util.h" +#include "kernel/closure/emissive.h" + +#include "kernel/svm/svm.h" + +#ifdef __OSL__ +# include "kernel/osl/shader.h" +#endif + +CCL_NAMESPACE_BEGIN + +#ifdef __VOLUME__ + +/* Merging */ +ccl_device_inline void volume_shader_merge_closures(ccl_private ShaderData *sd) +{ + /* Merge identical closures to save closure space with stacked volumes. */ + for (int i = 0; i < sd->num_closure; i++) { + ccl_private ShaderClosure *sci = &sd->closure[i]; + + if (sci->type != CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) { + continue; + } + + for (int j = i + 1; j < sd->num_closure; j++) { + ccl_private ShaderClosure *scj = &sd->closure[j]; + if (sci->type != scj->type) { + continue; + } + + ccl_private const HenyeyGreensteinVolume *hgi = (ccl_private const HenyeyGreensteinVolume *) + sci; + ccl_private const HenyeyGreensteinVolume *hgj = (ccl_private const HenyeyGreensteinVolume *) + scj; + if (!(hgi->g == hgj->g)) { + continue; + } + + sci->weight += scj->weight; + sci->sample_weight += scj->sample_weight; + + int size = sd->num_closure - (j + 1); + if (size > 0) { + for (int k = 0; k < size; k++) { + scj[k] = scj[k + 1]; + } + } + + sd->num_closure--; + kernel_assert(sd->num_closure >= 0); + j--; + } + } +} + +ccl_device_inline void volume_shader_copy_phases(ccl_private ShaderVolumePhases *ccl_restrict + phases, + ccl_private const ShaderData *ccl_restrict sd) +{ + phases->num_closure = 0; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *from_sc = &sd->closure[i]; + ccl_private const HenyeyGreensteinVolume *from_hg = + (ccl_private const HenyeyGreensteinVolume *)from_sc; + + if (from_sc->type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) { + ccl_private ShaderVolumeClosure *to_sc = &phases->closure[phases->num_closure]; + + to_sc->weight = from_sc->weight; + to_sc->sample_weight = from_sc->sample_weight; + to_sc->g = from_hg->g; + phases->num_closure++; + if (phases->num_closure >= MAX_VOLUME_CLOSURE) { + break; + } + } + } +} + +ccl_device_inline float _volume_shader_phase_eval_mis(ccl_private const ShaderData *sd, + ccl_private const ShaderVolumePhases *phases, + const float3 omega_in, + int skip_phase, + ccl_private BsdfEval *result_eval, + float sum_pdf, + float sum_sample_weight) +{ + for (int i = 0; i < phases->num_closure; i++) { + if (i == skip_phase) + continue; + + ccl_private const ShaderVolumeClosure *svc = &phases->closure[i]; + float phase_pdf = 0.0f; + Spectrum eval = volume_phase_eval(sd, svc, omega_in, &phase_pdf); + + if (phase_pdf != 0.0f) { + bsdf_eval_accum(result_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval); + sum_pdf += phase_pdf * svc->sample_weight; + } + + sum_sample_weight += svc->sample_weight; + } + + return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f; +} + +ccl_device float volume_shader_phase_eval(KernelGlobals kg, + ccl_private const ShaderData *sd, + ccl_private const ShaderVolumePhases *phases, + const float3 omega_in, + ccl_private BsdfEval *phase_eval) +{ + bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum()); + + return _volume_shader_phase_eval_mis(sd, phases, omega_in, -1, phase_eval, 0.0f, 0.0f); +} + +ccl_device int volume_shader_phase_sample(KernelGlobals kg, + ccl_private const ShaderData *sd, + ccl_private const ShaderVolumePhases *phases, + float2 rand_phase, + ccl_private BsdfEval *phase_eval, + ccl_private float3 *omega_in, + ccl_private float *pdf) +{ + int sampled = 0; + + if (phases->num_closure > 1) { + /* pick a phase closure based on sample weights */ + float sum = 0.0f; + + for (sampled = 0; sampled < phases->num_closure; sampled++) { + ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled]; + sum += svc->sample_weight; + } + + float r = rand_phase.x * sum; + float partial_sum = 0.0f; + + for (sampled = 0; sampled < phases->num_closure; sampled++) { + ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled]; + float next_sum = partial_sum + svc->sample_weight; + + if (r <= next_sum) { + /* Rescale to reuse for BSDF direction sample. */ + rand_phase.x = (r - partial_sum) / svc->sample_weight; + break; + } + + partial_sum = next_sum; + } + + if (sampled == phases->num_closure) { + *pdf = 0.0f; + return LABEL_NONE; + } + } + + /* todo: this isn't quite correct, we don't weight anisotropy properly + * depending on color channels, even if this is perhaps not a common case */ + ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled]; + int label; + Spectrum eval = zero_spectrum(); + + *pdf = 0.0f; + label = volume_phase_sample(sd, svc, rand_phase.x, rand_phase.y, &eval, omega_in, pdf); + + if (*pdf != 0.0f) { + bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval); + } + + return label; +} + +ccl_device int volume_shader_phase_sample_closure(KernelGlobals kg, + ccl_private const ShaderData *sd, + ccl_private const ShaderVolumeClosure *sc, + const float2 rand_phase, + ccl_private BsdfEval *phase_eval, + ccl_private float3 *omega_in, + ccl_private float *pdf) +{ + int label; + Spectrum eval = zero_spectrum(); + + *pdf = 0.0f; + label = volume_phase_sample(sd, sc, rand_phase.x, rand_phase.y, &eval, omega_in, pdf); + + if (*pdf != 0.0f) + bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval); + + return label; +} + +/* Motion Blur */ + +# ifdef __OBJECT_MOTION__ +ccl_device_inline void volume_shader_motion_blur(KernelGlobals kg, + ccl_private ShaderData *ccl_restrict sd) +{ + if ((sd->object_flag & SD_OBJECT_HAS_VOLUME_MOTION) == 0) { + return; + } + + AttributeDescriptor v_desc = find_attribute(kg, sd, ATTR_STD_VOLUME_VELOCITY); + kernel_assert(v_desc.offset != ATTR_STD_NOT_FOUND); + + const float3 P = sd->P; + const float velocity_scale = kernel_data_fetch(objects, sd->object).velocity_scale; + const float time_offset = kernel_data.cam.motion_position == MOTION_POSITION_CENTER ? 0.5f : + 0.0f; + const float time = kernel_data.cam.motion_position == MOTION_POSITION_END ? + (1.0f - kernel_data.cam.shuttertime) + sd->time : + sd->time; + + /* Use a 1st order semi-lagrangian advection scheme to estimate what volume quantity + * existed, or will exist, at the given time: + * + * `phi(x, T) = phi(x - (T - t) * u(x, T), t)` + * + * where + * + * x : position + * T : super-sampled time (or ray time) + * t : current time of the simulation (in rendering we assume this is center frame with + * relative time = 0) + * phi : the volume quantity + * u : the velocity field + * + * But first we need to determine the velocity field `u(x, T)`, which we can estimate also + * using semi-lagrangian advection. + * + * `u(x, T) = u(x - (T - t) * u(x, T), t)` + * + * This is the typical way to model self-advection in fluid dynamics, however, we do not + * account for other forces affecting the velocity during simulation (pressure, buoyancy, + * etc.): this gives a linear interpolation when fluid are mostly "curvy". For better + * results, a higher order interpolation scheme can be used (at the cost of more lookups), + * or an interpolation of the velocity fields for the previous and next frames could also + * be used to estimate `u(x, T)` (which will cost more memory and lookups). + * + * References: + * "Eulerian Motion Blur", Kim and Ko, 2007 + * "Production Volume Rendering", Wreninge et al., 2012 + */ + + /* Find velocity. */ + float3 velocity = primitive_volume_attribute_float3(kg, sd, v_desc); + object_dir_transform(kg, sd, &velocity); + + /* Find advected P. */ + sd->P = P - (time - time_offset) * velocity_scale * velocity; + + /* Find advected velocity. */ + velocity = primitive_volume_attribute_float3(kg, sd, v_desc); + object_dir_transform(kg, sd, &velocity); + + /* Find advected P. */ + sd->P = P - (time - time_offset) * velocity_scale * velocity; +} +# endif + +/* Volume Evaluation */ + +template<const bool shadow, typename StackReadOp, typename ConstIntegratorGenericState> +ccl_device_inline void volume_shader_eval(KernelGlobals kg, + ConstIntegratorGenericState state, + ccl_private ShaderData *ccl_restrict sd, + const uint32_t path_flag, + StackReadOp stack_read) +{ + /* If path is being terminated, we are tracing a shadow ray or evaluating + * emission, then we don't need to store closures. The emission and shadow + * shader data also do not have a closure array to save GPU memory. */ + int max_closures; + if (path_flag & (PATH_RAY_TERMINATE | PATH_RAY_SHADOW | PATH_RAY_EMISSION)) { + max_closures = 0; + } + else { + max_closures = kernel_data.max_closures; + } + + /* reset closures once at the start, we will be accumulating the closures + * for all volumes in the stack into a single array of closures */ + sd->num_closure = 0; + sd->num_closure_left = max_closures; + sd->flag = 0; + sd->object_flag = 0; + + for (int i = 0;; i++) { + const VolumeStack entry = stack_read(i); + if (entry.shader == SHADER_NONE) { + break; + } + + /* Setup shader-data from stack. it's mostly setup already in + * shader_setup_from_volume, this switching should be quick. */ + sd->object = entry.object; + sd->lamp = LAMP_NONE; + sd->shader = entry.shader; + + sd->flag &= ~SD_SHADER_FLAGS; + sd->flag |= kernel_data_fetch(shaders, (sd->shader & SHADER_MASK)).flags; + sd->object_flag &= ~SD_OBJECT_FLAGS; + + if (sd->object != OBJECT_NONE) { + sd->object_flag |= kernel_data_fetch(object_flag, sd->object); + +# ifdef __OBJECT_MOTION__ + /* todo: this is inefficient for motion blur, we should be + * caching matrices instead of recomputing them each step */ + shader_setup_object_transforms(kg, sd, sd->time); + + volume_shader_motion_blur(kg, sd); +# endif + } + + /* evaluate shader */ +# ifdef __SVM__ +# ifdef __OSL__ + if (kg->osl) { + OSLShader::eval_volume(kg, state, sd, path_flag); + } + else +# endif + { + svm_eval_nodes<KERNEL_FEATURE_NODE_MASK_VOLUME, SHADER_TYPE_VOLUME>( + kg, state, sd, NULL, path_flag); + } +# endif + + /* Merge closures to avoid exceeding number of closures limit. */ + if (!shadow) { + if (i > 0) { + volume_shader_merge_closures(sd); + } + } + } +} + +#endif /* __VOLUME__ */ + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/light/background.h b/intern/cycles/kernel/light/background.h index 2a97d43c9ce..951620ff1cb 100644 --- a/intern/cycles/kernel/light/background.h +++ b/intern/cycles/kernel/light/background.h @@ -9,8 +9,6 @@ CCL_NAMESPACE_BEGIN /* Background Light */ -#ifdef __BACKGROUND_MIS__ - ccl_device float3 background_map_sample(KernelGlobals kg, float randu, float randv, @@ -435,6 +433,4 @@ ccl_device float background_light_pdf(KernelGlobals kg, float3 P, float3 directi return pdf * kernel_data.integrator.pdf_lights; } -#endif - CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/light/light.h b/intern/cycles/kernel/light/light.h index b939489bb18..85478cd09c5 100644 --- a/intern/cycles/kernel/light/light.h +++ b/intern/cycles/kernel/light/light.h @@ -86,7 +86,6 @@ ccl_device_inline bool light_sample(KernelGlobals kg, ls->pdf = invarea / (costheta * costheta * costheta); ls->eval_fac = ls->pdf; } -#ifdef __BACKGROUND_MIS__ else if (type == LIGHT_BACKGROUND) { /* infinite area light (e.g. light dome or env light) */ float3 D = -background_light_sample(kg, P, randu, randv, &ls->pdf); @@ -97,7 +96,6 @@ ccl_device_inline bool light_sample(KernelGlobals kg, ls->t = FLT_MAX; ls->eval_fac = 1.0f; } -#endif else { ls->P = make_float3(klight->co[0], klight->co[1], klight->co[2]); diff --git a/intern/cycles/kernel/light/sample.h b/intern/cycles/kernel/light/sample.h index 4195675dd13..e0d4f221bef 100644 --- a/intern/cycles/kernel/light/sample.h +++ b/intern/cycles/kernel/light/sample.h @@ -4,7 +4,7 @@ #pragma once #include "kernel/integrator/path_state.h" -#include "kernel/integrator/shader_eval.h" +#include "kernel/integrator/surface_shader.h" #include "kernel/light/light.h" @@ -24,22 +24,19 @@ light_sample_shader_eval(KernelGlobals kg, /* setup shading at emitter */ Spectrum eval = zero_spectrum(); - if (shader_constant_emission_eval(kg, ls->shader, &eval)) { + if (surface_shader_constant_emission(kg, ls->shader, &eval)) { if ((ls->prim != PRIM_NONE) && dot(ls->Ng, ls->D) > 0.0f) { ls->Ng = -ls->Ng; } } else { - /* Setup shader data and call shader_eval_surface once, better + /* Setup shader data and call surface_shader_eval once, better * for GPU coherence and compile times. */ PROFILING_INIT_FOR_SHADER(kg, PROFILING_SHADE_LIGHT_SETUP); -#ifdef __BACKGROUND_MIS__ if (ls->type == LIGHT_BACKGROUND) { shader_setup_from_background(kg, emission_sd, ls->P, ls->D, time); } - else -#endif - { + else { shader_setup_from_sample(kg, emission_sd, ls->P, @@ -63,18 +60,15 @@ light_sample_shader_eval(KernelGlobals kg, /* No proper path flag, we're evaluating this for all closures. that's * weak but we'd have to do multiple evaluations otherwise. */ - shader_eval_surface<KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT>( + surface_shader_eval<KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT>( kg, state, emission_sd, NULL, PATH_RAY_EMISSION); /* Evaluate closures. */ -#ifdef __BACKGROUND_MIS__ if (ls->type == LIGHT_BACKGROUND) { - eval = shader_background_eval(emission_sd); + eval = surface_shader_background(emission_sd); } - else -#endif - { - eval = shader_emissive_eval(emission_sd); + else { + eval = surface_shader_emission(emission_sd); } } diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp index 6766fe2ce89..faa027f4e1e 100644 --- a/intern/cycles/kernel/osl/services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -27,7 +27,6 @@ #include "util/log.h" #include "util/string.h" -// clang-format off #include "kernel/device/cpu/compat.h" #include "kernel/device/cpu/globals.h" #include "kernel/device/cpu/image.h" @@ -45,10 +44,10 @@ #include "kernel/camera/projection.h" #include "kernel/integrator/path_state.h" -#include "kernel/integrator/shader_eval.h" + +#include "kernel/svm/svm.h" #include "kernel/util/color.h" -// clang-format on CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/sample/pattern.h b/intern/cycles/kernel/sample/pattern.h index 6477e29fa40..ebdecc1bff9 100644 --- a/intern/cycles/kernel/sample/pattern.h +++ b/intern/cycles/kernel/sample/pattern.h @@ -90,18 +90,33 @@ ccl_device_inline uint path_rng_hash_init(KernelGlobals kg, return rng_hash; } -ccl_device_inline bool sample_is_even(int pattern, int sample) +/** + * Splits samples into two different classes, A and B, which can be + * compared for variance estimation. + */ +ccl_device_inline bool sample_is_class_A(int pattern, int sample) { - if (pattern == SAMPLING_PATTERN_PMJ) { - /* See Section 10.2.1, "Progressive Multi-Jittered Sample Sequences", Christensen et al. - * We can use this to get divide sample sequence into two classes for easier variance - * estimation. */ - return popcount(uint(sample) & 0xaaaaaaaa) & 1; - } - else { - /* TODO(Stefan): Are there reliable ways of dividing Sobol-Burley into two classes? */ - return sample & 0x1; +#if 0 + if (!(pattern == SAMPLING_PATTERN_PMJ || pattern == SAMPLING_PATTERN_SOBOL_BURLEY)) { + /* Fallback: assign samples randomly. + * This is guaranteed to work "okay" for any sampler, but isn't good. + * (Note: the seed constant is just a random number to guard against + * possible interactions with other uses of the hash. There's nothing + * special about it.) + */ + return hash_hp_seeded_uint(sample, 0xa771f873) & 1; } -} +#else + (void)pattern; +#endif + /* This follows the approach from section 10.2.1 of "Progressive + * Multi-Jittered Sample Sequences" by Christensen et al., but + * implemented with efficient bit-fiddling. + * + * This approach also turns out to work equally well with Sobol-Burley + * (see https://developer.blender.org/D15746#429471). + */ + return popcount(uint(sample) & 0xaaaaaaaa) & 1; +} CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/aov.h b/intern/cycles/kernel/svm/aov.h index 9b818f0e6f8..c574b28c078 100644 --- a/intern/cycles/kernel/svm/aov.h +++ b/intern/cycles/kernel/svm/aov.h @@ -3,7 +3,7 @@ #pragma once -#include "kernel/film/write_passes.h" +#include "kernel/film/aov_passes.h" CCL_NAMESPACE_BEGIN @@ -27,12 +27,7 @@ ccl_device void svm_node_aov_color(KernelGlobals kg, IF_KERNEL_NODES_FEATURE(AOV) { const float3 val = stack_load_float3(stack, node.y); - const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); - const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * - kernel_data.film.pass_stride; - ccl_global float *buffer = render_buffer + render_buffer_offset + - (kernel_data.film.pass_aov_color + node.z); - kernel_write_pass_float4(buffer, make_float4(val.x, val.y, val.z, 1.0f)); + film_write_aov_pass_color(kg, state, render_buffer, node.z, val); } } @@ -47,12 +42,7 @@ ccl_device void svm_node_aov_value(KernelGlobals kg, IF_KERNEL_NODES_FEATURE(AOV) { const float val = stack_load_float(stack, node.y); - const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); - const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * - kernel_data.film.pass_stride; - ccl_global float *buffer = render_buffer + render_buffer_offset + - (kernel_data.film.pass_aov_value + node.z); - kernel_write_pass_float(buffer, val); + film_write_aov_pass_value(kg, state, render_buffer, node.z, val); } } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/closure.h b/intern/cycles/kernel/svm/closure.h index 5e8ef69fd15..2d91b014f60 100644 --- a/intern/cycles/kernel/svm/closure.h +++ b/intern/cycles/kernel/svm/closure.h @@ -3,6 +3,11 @@ #pragma once +#include "kernel/closure/alloc.h" +#include "kernel/closure/bsdf.h" +#include "kernel/closure/bsdf_util.h" +#include "kernel/closure/emissive.h" + #include "kernel/util/color.h" CCL_NAMESPACE_BEGIN @@ -106,7 +111,6 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, __uint_as_float(node.w); switch (type) { -#ifdef __PRINCIPLED__ case CLOSURE_BSDF_PRINCIPLED_ID: { uint specular_offset, roughness_offset, specular_tint_offset, anisotropic_offset, sheen_offset, sheen_tint_offset, clearcoat_offset, clearcoat_roughness_offset, @@ -202,7 +206,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, Spectrum weight = sd->svm_closure_weight * mix_weight; -# ifdef __SUBSURFACE__ +#ifdef __SUBSURFACE__ float3 mixed_ss_base_color = subsurface_color * subsurface + base_color * (1.0f - subsurface); Spectrum subsurf_weight = weight * rgb_to_spectrum(mixed_ss_base_color) * diffuse_weight; @@ -253,7 +257,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, } } } -# else +#else /* diffuse */ if (diffuse_weight > CLOSURE_WEIGHT_CUTOFF) { Spectrum diff_weight = weight * rgb_to_spectrum(base_color) * diffuse_weight; @@ -269,7 +273,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, sd->flag |= bsdf_principled_diffuse_setup(bsdf, PRINCIPLED_DIFFUSE_FULL); } } -# endif +#endif /* sheen */ if (diffuse_weight > CLOSURE_WEIGHT_CUTOFF && sheen > CLOSURE_WEIGHT_CUTOFF) { @@ -294,9 +298,9 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, } /* specular reflection */ -# ifdef __CAUSTICS_TRICKS__ +#ifdef __CAUSTICS_TRICKS__ if (kernel_data.integrator.caustics_reflective || (path_flag & PATH_RAY_DIFFUSE) == 0) { -# endif +#endif if (specular_weight > CLOSURE_WEIGHT_CUTOFF && (specular > CLOSURE_WEIGHT_CUTOFF || metallic > CLOSURE_WEIGHT_CUTOFF)) { Spectrum spec_weight = weight * specular_weight; @@ -339,15 +343,15 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, sd->flag |= bsdf_microfacet_multi_ggx_fresnel_setup(bsdf, sd); } } -# ifdef __CAUSTICS_TRICKS__ +#ifdef __CAUSTICS_TRICKS__ } -# endif +#endif /* BSDF */ -# ifdef __CAUSTICS_TRICKS__ +#ifdef __CAUSTICS_TRICKS__ if (kernel_data.integrator.caustics_reflective || kernel_data.integrator.caustics_refractive || (path_flag & PATH_RAY_DIFFUSE) == 0) { -# endif +#endif if (final_transmission > CLOSURE_WEIGHT_CUTOFF) { Spectrum glass_weight = weight * final_transmission; float3 cspec0 = base_color * specular_tint + make_float3(1.0f - specular_tint); @@ -357,9 +361,9 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, float refl_roughness = roughness; /* reflection */ -# ifdef __CAUSTICS_TRICKS__ +#ifdef __CAUSTICS_TRICKS__ if (kernel_data.integrator.caustics_reflective || (path_flag & PATH_RAY_DIFFUSE) == 0) -# endif +#endif { ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), glass_weight * fresnel); @@ -387,9 +391,9 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, } /* refraction */ -# ifdef __CAUSTICS_TRICKS__ +#ifdef __CAUSTICS_TRICKS__ if (kernel_data.integrator.caustics_refractive || (path_flag & PATH_RAY_DIFFUSE) == 0) -# endif +#endif { /* This is to prevent MNEE from receiving a null BSDF. */ float refraction_fresnel = fmaxf(0.0001f, 1.0f - fresnel); @@ -443,14 +447,14 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, } } } -# ifdef __CAUSTICS_TRICKS__ +#ifdef __CAUSTICS_TRICKS__ } -# endif +#endif /* clearcoat */ -# ifdef __CAUSTICS_TRICKS__ +#ifdef __CAUSTICS_TRICKS__ if (kernel_data.integrator.caustics_reflective || (path_flag & PATH_RAY_DIFFUSE) == 0) { -# endif +#endif if (clearcoat > CLOSURE_WEIGHT_CUTOFF) { ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), weight); @@ -476,13 +480,12 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, sd->flag |= bsdf_microfacet_ggx_clearcoat_setup(bsdf, sd); } } -# ifdef __CAUSTICS_TRICKS__ +#ifdef __CAUSTICS_TRICKS__ } -# endif +#endif break; } -#endif /* __PRINCIPLED__ */ case CLOSURE_BSDF_DIFFUSE_ID: { Spectrum weight = sd->svm_closure_weight * mix_weight; ccl_private OrenNayarBsdf *bsdf = (ccl_private OrenNayarBsdf *)bsdf_alloc( diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index 16c37beeb5a..33a27ad3677 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -54,36 +54,25 @@ CCL_NAMESPACE_BEGIN #endif /* Kernel features */ -#define __DPDU__ -#define __BACKGROUND__ +#define __AO__ #define __CAUSTICS_TRICKS__ -#define __VISIBILITY_FLAG__ -#define __RAY_DIFFERENTIALS__ -#define __CAMERA_CLIPPING__ -#define __INTERSECTION_REFINE__ #define __CLAMP_SAMPLE__ -#define __PATCH_EVAL__ -#define __SHADOW_CATCHER__ #define __DENOISING_FEATURES__ -#define __SHADER_RAYTRACE__ -#define __AO__ -#define __PASSES__ +#define __DPDU__ #define __HAIR__ +#define __OBJECT_MOTION__ +#define __PASSES__ +#define __PATCH_EVAL__ #define __POINTCLOUD__ +#define __RAY_DIFFERENTIALS__ +#define __SHADER_RAYTRACE__ +#define __SHADOW_CATCHER__ +#define __SHADOW_RECORD_ALL__ +#define __SUBSURFACE__ #define __SVM__ -#define __EMISSION__ -#define __HOLDOUT__ #define __TRANSPARENT_SHADOWS__ -#define __BACKGROUND_MIS__ -#define __LAMP_MIS__ -#define __CAMERA_MOTION__ -#define __OBJECT_MOTION__ -#define __PRINCIPLED__ -#define __SUBSURFACE__ +#define __VISIBILITY_FLAG__ #define __VOLUME__ -#define __CMJ__ -#define __SHADOW_RECORD_ALL__ -#define __BRANCHED_PATH__ /* Device specific features */ #ifndef __KERNEL_GPU__ @@ -101,9 +90,6 @@ CCL_NAMESPACE_BEGIN /* Scene-based selective features compilation. */ #ifdef __KERNEL_FEATURES__ -# if !(__KERNEL_FEATURES & KERNEL_FEATURE_CAMERA_MOTION) -# undef __CAMERA_MOTION__ -# endif # if !(__KERNEL_FEATURES & KERNEL_FEATURE_OBJECT_MOTION) # undef __OBJECT_MOTION__ # endif @@ -128,9 +114,6 @@ CCL_NAMESPACE_BEGIN # if !(__KERNEL_FEATURES & KERNEL_FEATURE_SHADOW_CATCHER) # undef __SHADOW_CATCHER__ # endif -# if !(__KERNEL_FEATURES & KERNEL_FEATURE_PRINCIPLED) -# undef __PRINCIPLED__ -# endif # if !(__KERNEL_FEATURES & KERNEL_FEATURE_DENOISING) # undef __DENOISING_FEATURES__ # endif @@ -1488,42 +1471,38 @@ enum KernelFeatureFlag : uint32_t { KERNEL_FEATURE_HAIR = (1U << 12U), KERNEL_FEATURE_HAIR_THICK = (1U << 13U), KERNEL_FEATURE_OBJECT_MOTION = (1U << 14U), - KERNEL_FEATURE_CAMERA_MOTION = (1U << 15U), /* Denotes whether baking functionality is needed. */ - KERNEL_FEATURE_BAKING = (1U << 16U), + KERNEL_FEATURE_BAKING = (1U << 15U), /* Use subsurface scattering materials. */ - KERNEL_FEATURE_SUBSURFACE = (1U << 17U), + KERNEL_FEATURE_SUBSURFACE = (1U << 16U), /* Use volume materials. */ - KERNEL_FEATURE_VOLUME = (1U << 18U), + KERNEL_FEATURE_VOLUME = (1U << 17U), /* Use OpenSubdiv patch evaluation */ - KERNEL_FEATURE_PATCH_EVALUATION = (1U << 19U), + KERNEL_FEATURE_PATCH_EVALUATION = (1U << 18U), /* Use Transparent shadows */ - KERNEL_FEATURE_TRANSPARENT = (1U << 20U), + KERNEL_FEATURE_TRANSPARENT = (1U << 19U), /* Use shadow catcher. */ - KERNEL_FEATURE_SHADOW_CATCHER = (1U << 21U), - - /* Per-uber shader usage flags. */ - KERNEL_FEATURE_PRINCIPLED = (1U << 22U), + KERNEL_FEATURE_SHADOW_CATCHER = (1U << 29U), /* Light render passes. */ - KERNEL_FEATURE_LIGHT_PASSES = (1U << 23U), + KERNEL_FEATURE_LIGHT_PASSES = (1U << 21U), /* Shadow render pass. */ - KERNEL_FEATURE_SHADOW_PASS = (1U << 24U), + KERNEL_FEATURE_SHADOW_PASS = (1U << 22U), /* AO. */ - KERNEL_FEATURE_AO_PASS = (1U << 25U), - KERNEL_FEATURE_AO_ADDITIVE = (1U << 26U), + KERNEL_FEATURE_AO_PASS = (1U << 23U), + KERNEL_FEATURE_AO_ADDITIVE = (1U << 24U), KERNEL_FEATURE_AO = (KERNEL_FEATURE_AO_PASS | KERNEL_FEATURE_AO_ADDITIVE), /* MNEE. */ - KERNEL_FEATURE_MNEE = (1U << 27U), + KERNEL_FEATURE_MNEE = (1U << 25U), }; /* Shader node feature mask, to specialize shader evaluation for kernels. */ diff --git a/intern/cycles/scene/camera.cpp b/intern/cycles/scene/camera.cpp index d9e574873bd..240e5d9c128 100644 --- a/intern/cycles/scene/camera.cpp +++ b/intern/cycles/scene/camera.cpp @@ -761,9 +761,7 @@ float Camera::world_to_raster_size(float3 P) } #else camera_sample_panorama(&kernel_camera, -# ifdef __CAMERA_MOTION__ kernel_camera_motion.data(), -# endif 0.5f * full_width, 0.5f * full_height, 0.0f, diff --git a/intern/cycles/scene/integrator.cpp b/intern/cycles/scene/integrator.cpp index 86a2c9571c6..e9cd753854f 100644 --- a/intern/cycles/scene/integrator.cpp +++ b/intern/cycles/scene/integrator.cpp @@ -217,9 +217,7 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene FLT_MAX : sample_clamp_indirect * 3.0f; - /* Adaptive sampling requires PMJ, see sample_is_even. */ - kintegrator->sampling_pattern = (use_adaptive_sampling) ? SAMPLING_PATTERN_PMJ : - sampling_pattern; + kintegrator->sampling_pattern = sampling_pattern; kintegrator->scrambling_distance = scrambling_distance; if (light_sampling_threshold > 0.0f) { diff --git a/intern/cycles/scene/scene.cpp b/intern/cycles/scene/scene.cpp index 18cd665ac74..478396ea98e 100644 --- a/intern/cycles/scene/scene.cpp +++ b/intern/cycles/scene/scene.cpp @@ -497,9 +497,6 @@ void Scene::update_kernel_features() if (params.hair_shape == CURVE_THICK) { kernel_features |= KERNEL_FEATURE_HAIR_THICK; } - if (use_motion && camera->use_motion()) { - kernel_features |= KERNEL_FEATURE_CAMERA_MOTION; - } /* Figure out whether the scene will use shader ray-trace we need at least * one caustic light, one caustic caster and one caustic receiver to use @@ -520,9 +517,6 @@ void Scene::update_kernel_features() if (object->use_motion() || geom->get_use_motion_blur()) { kernel_features |= KERNEL_FEATURE_OBJECT_MOTION; } - if (geom->get_use_motion_blur()) { - kernel_features |= KERNEL_FEATURE_CAMERA_MOTION; - } } if (object->get_is_shadow_catcher()) { kernel_features |= KERNEL_FEATURE_SHADOW_CATCHER; @@ -590,8 +584,6 @@ static void log_kernel_features(const uint features) { VLOG_INFO << "Requested features:\n"; VLOG_INFO << "Use BSDF " << string_from_bool(features & KERNEL_FEATURE_NODE_BSDF) << "\n"; - VLOG_INFO << "Use Principled BSDF " << string_from_bool(features & KERNEL_FEATURE_PRINCIPLED) - << "\n"; VLOG_INFO << "Use Emission " << string_from_bool(features & KERNEL_FEATURE_NODE_EMISSION) << "\n"; VLOG_INFO << "Use Volume " << string_from_bool(features & KERNEL_FEATURE_NODE_VOLUME) << "\n"; @@ -611,8 +603,6 @@ static void log_kernel_features(const uint features) << "\n"; VLOG_INFO << "Use Object Motion " << string_from_bool(features & KERNEL_FEATURE_OBJECT_MOTION) << "\n"; - VLOG_INFO << "Use Camera Motion " << string_from_bool(features & KERNEL_FEATURE_CAMERA_MOTION) - << "\n"; VLOG_INFO << "Use Baking " << string_from_bool(features & KERNEL_FEATURE_BAKING) << "\n"; VLOG_INFO << "Use Subsurface " << string_from_bool(features & KERNEL_FEATURE_SUBSURFACE) << "\n"; VLOG_INFO << "Use Volume " << string_from_bool(features & KERNEL_FEATURE_VOLUME) << "\n"; diff --git a/intern/cycles/scene/shader.cpp b/intern/cycles/scene/shader.cpp index e1af92ea8cf..bd647ab55e7 100644 --- a/intern/cycles/scene/shader.cpp +++ b/intern/cycles/scene/shader.cpp @@ -685,9 +685,6 @@ uint ShaderManager::get_graph_kernel_features(ShaderGraph *graph) if (CLOSURE_IS_VOLUME(bsdf_node->get_closure_type())) { kernel_features |= KERNEL_FEATURE_NODE_VOLUME; } - else if (CLOSURE_IS_PRINCIPLED(bsdf_node->get_closure_type())) { - kernel_features |= KERNEL_FEATURE_PRINCIPLED; - } } if (node->has_surface_bssrdf()) { kernel_features |= KERNEL_FEATURE_SUBSURFACE; diff --git a/release/datafiles/fonts/NotoSansHebrew-Regular.woff2 b/release/datafiles/fonts/NotoSansHebrew-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..afbe2b7f62b --- /dev/null +++ b/release/datafiles/fonts/NotoSansHebrew-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansHebrew-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansHebrew-VariableFont_wdth,wght.woff2 Binary files differdeleted file mode 100644 index 4f6033c916f..00000000000 --- a/release/datafiles/fonts/NotoSansHebrew-VariableFont_wdth,wght.woff2 +++ /dev/null diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 25ffc6f430fc995b1c046b01acba1c3e6c1896b +Subproject 67f1fbca1482d9d9362a4001332e785c3fd5d23 diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index cd11938e146..49f0fef5849 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2327,6 +2327,7 @@ class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel): ({"property": "use_cycles_debug"}, None), ({"property": "show_asset_debug_info"}, None), ({"property": "use_asset_indexing"}, None), + ({"property": "use_viewport_debug"}, None), ), ) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index b1b5738aecd..b2fa8e4d64f 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -7809,6 +7809,25 @@ class VIEW3D_PT_curves_sculpt_grow_shrink_scaling(Panel): layout.prop(brush.curves_sculpt_settings, "minimum_length") +class VIEW3D_PT_viewport_debug(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_parent_id = 'VIEW3D_PT_overlay' + bl_label = "Viewport Debug" + + @classmethod + def poll(cls, context): + prefs = context.preferences + return prefs.experimental.use_viewport_debug + + def draw(self, context): + layout = self.layout + view = context.space_data + overlay = view.overlay + + layout.prop(overlay, "use_debug_freeze_view_culling") + + classes = ( VIEW3D_HT_header, VIEW3D_HT_tool_header, @@ -8046,6 +8065,7 @@ classes = ( TOPBAR_PT_annotation_layers, VIEW3D_PT_curves_sculpt_add_shape, VIEW3D_PT_curves_sculpt_grow_shrink_scaling, + VIEW3D_PT_viewport_debug, ) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index fb157c71172..03629db0acd 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -231,7 +231,7 @@ void blf_batch_draw_begin(FontBLF *font) float gpumat[4][4]; GPU_matrix_model_view_get(gpumat); - bool mat_changed = (memcmp(gpumat, g_batch.mat, sizeof(g_batch.mat)) != 0); + bool mat_changed = equals_m4m4(gpumat, g_batch.mat) == false; if (mat_changed) { /* Modelviewmat is no longer the same. @@ -1450,7 +1450,7 @@ static const struct FaceDetails static_face_details[] = { {"NotoSansGeorgian-VariableFont_wdth,wght.woff2", TT_UCR_GEORGIAN, 0, 0, 0}, {"NotoSansGujarati-Regular.woff2", TT_UCR_GUJARATI, 0, 0, 0}, {"NotoSansGurmukhi-VariableFont_wdth,wght.woff2", TT_UCR_GURMUKHI, 0, 0, 0}, - {"NotoSansHebrew-VariableFont_wdth,wght.woff2", TT_UCR_HEBREW, 0, 0, 0}, + {"NotoSansHebrew-Regular.woff2", TT_UCR_HEBREW, 0, 0, 0}, {"NotoSansJavanese-Regular.woff2", 0x80000003L, 0x2000L, 0, 0}, {"NotoSansKannada-VariableFont_wdth,wght.woff2", TT_UCR_KANNADA, 0, 0, 0}, {"NotoSansMalayalam-VariableFont_wdth,wght.woff2", TT_UCR_MALAYALAM, 0, 0, 0}, diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index 39ef738c631..33735fddf55 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -466,8 +466,8 @@ void BKE_gpencil_stroke_uniform_subdivide(struct bGPdata *gpd, * This allows for manipulations in 2D but also easy conversion back to 3D. * \note also takes care of parent space transform. */ -void BKE_gpencil_stroke_to_view_space(struct RegionView3D *rv3d, - struct bGPDstroke *gps, +void BKE_gpencil_stroke_to_view_space(struct bGPDstroke *gps, + float viewmat[4][4], const float diff_mat[4][4]); /** * Stroke from view space @@ -475,15 +475,15 @@ void BKE_gpencil_stroke_to_view_space(struct RegionView3D *rv3d, * Inverse of #BKE_gpencil_stroke_to_view_space * \note also takes care of parent space transform. */ -void BKE_gpencil_stroke_from_view_space(struct RegionView3D *rv3d, - struct bGPDstroke *gps, +void BKE_gpencil_stroke_from_view_space(struct bGPDstroke *gps, + float viewinv[4][4], const float diff_mat[4][4]); /** * Calculates the perimeter of a stroke projected from the view and returns it as a new stroke. * \param subdivisions: Number of subdivisions for the start and end caps. * \return: bGPDstroke pointer to stroke perimeter. */ -struct bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d, +struct bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(float viewmat[4][4], struct bGPdata *gpd, const struct bGPDlayer *gpl, struct bGPDstroke *gps, diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 1118552b643..8affbf0ca67 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -707,7 +707,10 @@ struct bNodeLink *nodeAddLink(struct bNodeTree *ntree, struct bNodeSocket *tosock); void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link); void nodeRemSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock); -void nodeMuteLinkToggle(struct bNodeTree *ntree, struct bNodeLink *link); +/** + * Set the mute status of a single link. + */ +void nodeLinkSetMute(struct bNodeTree *ntree, struct bNodeLink *link, const bool muted); bool nodeLinkIsHidden(const struct bNodeLink *link); bool nodeLinkIsSelected(const struct bNodeLink *link); void nodeInternalRelink(struct bNodeTree *ntree, struct bNode *node); diff --git a/source/blender/blenkernel/intern/attribute.cc b/source/blender/blenkernel/intern/attribute.cc index 0f7fabcff9b..8524f340803 100644 --- a/source/blender/blenkernel/intern/attribute.cc +++ b/source/blender/blenkernel/intern/attribute.cc @@ -474,7 +474,7 @@ CustomDataLayer *BKE_id_attributes_active_get(ID *id) for (int i = 0; i < customdata->totlayer; i++) { CustomDataLayer *layer = &customdata->layers[i]; if (CD_MASK_PROP_ALL & CD_TYPE_AS_MASK(layer->type)) { - if (index == active_index) { + if (index == active_index && BKE_attribute_allow_procedural_access(layer->name)) { return layer; } index++; diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 3f549b39a00..7c450b86d52 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -1369,6 +1369,10 @@ static void reverse_swap_curve_point_data(const CurvesGeometry &curves, std::swap(a[end_index], b[i]); std::swap(b[end_index], a[i]); } + if (points.size() % 2) { + const int64_t middle_index = points.size() / 2; + std::swap(a[middle_index], b[middle_index]); + } } }); } diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 8914c01f34f..3ca2c2440a1 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -3770,8 +3770,8 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd, BKE_gpencil_stroke_geometry_update(gpd, gps); } -void BKE_gpencil_stroke_to_view_space(RegionView3D *rv3d, - bGPDstroke *gps, +void BKE_gpencil_stroke_to_view_space(bGPDstroke *gps, + float viewmat[4][4], const float diff_mat[4][4]) { for (int i = 0; i < gps->totpoints; i++) { @@ -3779,12 +3779,12 @@ void BKE_gpencil_stroke_to_view_space(RegionView3D *rv3d, /* Point to parent space. */ mul_v3_m4v3(&pt->x, diff_mat, &pt->x); /* point to view space */ - mul_m4_v3(rv3d->viewmat, &pt->x); + mul_m4_v3(viewmat, &pt->x); } } -void BKE_gpencil_stroke_from_view_space(RegionView3D *rv3d, - bGPDstroke *gps, +void BKE_gpencil_stroke_from_view_space(bGPDstroke *gps, + float viewinv[4][4], const float diff_mat[4][4]) { float inverse_diff_mat[4][4]; @@ -3792,7 +3792,7 @@ void BKE_gpencil_stroke_from_view_space(RegionView3D *rv3d, for (int i = 0; i < gps->totpoints; i++) { bGPDspoint *pt = &gps->points[i]; - mul_v3_m4v3(&pt->x, rv3d->viewinv, &pt->x); + mul_v3_m4v3(&pt->x, viewinv, &pt->x); mul_m4_v3(inverse_diff_mat, &pt->x); } } @@ -4209,7 +4209,7 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd, return perimeter_list; } -bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d, +bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(float viewmat[4][4], bGPdata *gpd, const bGPDlayer *gpl, bGPDstroke *gps, @@ -4220,6 +4220,10 @@ bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d, if (gps->totpoints == 0) { return nullptr; } + + float viewinv[4][4]; + invert_m4_m4(viewinv, viewmat); + /* Duplicate only points and fill data. Weight and Curve are not needed. */ bGPDstroke *gps_temp = (bGPDstroke *)MEM_dupallocN(gps); gps_temp->prev = gps_temp->next = nullptr; @@ -4244,7 +4248,7 @@ bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d, pt_dst->uv_rot = 0; } - BKE_gpencil_stroke_to_view_space(rv3d, gps_temp, diff_mat); + BKE_gpencil_stroke_to_view_space(gps_temp, viewmat, diff_mat); int num_perimeter_points = 0; ListBase *perimeter_points = gpencil_stroke_perimeter_ex( gpd, gpl, gps_temp, subdivisions, thickness_chg, &num_perimeter_points); @@ -4267,7 +4271,7 @@ bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d, pt->flag |= GP_SPOINT_SELECT; } - BKE_gpencil_stroke_from_view_space(rv3d, perimeter_stroke, diff_mat); + BKE_gpencil_stroke_from_view_space(perimeter_stroke, viewinv, diff_mat); /* Free temp data. */ BLI_freelistN(perimeter_points); diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 4eb48e9edc9..e1eaed71f37 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -118,9 +118,6 @@ static void node_free_node(bNodeTree *ntree, bNode *node); static void node_socket_interface_free(bNodeTree *UNUSED(ntree), bNodeSocket *sock, const bool do_id_user); -static void nodeMuteRerouteOutputLinks(struct bNodeTree *ntree, - struct bNode *node, - const bool mute); static void ntree_init_data(ID *id) { @@ -2350,102 +2347,11 @@ void nodeRemLink(bNodeTree *ntree, bNodeLink *link) } } -/* Check if all output links are muted or not. */ -static bool nodeMuteFromSocketLinks(const bNodeTree *ntree, const bNodeSocket *sock) +void nodeLinkSetMute(bNodeTree *ntree, bNodeLink *link, const bool muted) { - int tot = 0; - int muted = 0; - LISTBASE_FOREACH (const bNodeLink *, link, &ntree->links) { - if (link->fromsock == sock) { - tot++; - if (link->flag & NODE_LINK_MUTED) { - muted++; - } - } - } - return tot == muted; -} - -static void nodeMuteLink(bNodeLink *link) -{ - link->flag |= NODE_LINK_MUTED; - link->flag |= NODE_LINK_TEST; - if (!(link->tosock->flag & SOCK_MULTI_INPUT)) { - link->tosock->flag &= ~SOCK_IN_USE; - } -} - -static void nodeUnMuteLink(bNodeLink *link) -{ - link->flag &= ~NODE_LINK_MUTED; - link->flag |= NODE_LINK_TEST; - link->tosock->flag |= SOCK_IN_USE; -} - -/* Upstream muting. Always happens when unmuting but checks when muting. O(n^2) algorithm. */ -static void nodeMuteRerouteInputLinks(bNodeTree *ntree, bNode *node, const bool mute) -{ - if (node->type != NODE_REROUTE) { - return; - } - if (!mute || nodeMuteFromSocketLinks(ntree, (bNodeSocket *)node->outputs.first)) { - bNodeSocket *sock = (bNodeSocket *)node->inputs.first; - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { - if (!(link->flag & NODE_LINK_VALID) || (link->tosock != sock)) { - continue; - } - if (mute) { - nodeMuteLink(link); - } - else { - nodeUnMuteLink(link); - } - nodeMuteRerouteInputLinks(ntree, link->fromnode, mute); - } - } -} - -/* Downstream muting propagates when reaching reroute nodes. O(n^2) algorithm. */ -static void nodeMuteRerouteOutputLinks(bNodeTree *ntree, bNode *node, const bool mute) -{ - if (node->type != NODE_REROUTE) { - return; - } - bNodeSocket *sock; - sock = (bNodeSocket *)node->outputs.first; - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { - if (!(link->flag & NODE_LINK_VALID) || (link->fromsock != sock)) { - continue; - } - if (mute) { - nodeMuteLink(link); - } - else { - nodeUnMuteLink(link); - } - nodeMuteRerouteOutputLinks(ntree, link->tonode, mute); - } -} - -void nodeMuteLinkToggle(bNodeTree *ntree, bNodeLink *link) -{ - if (link->tosock) { - bool mute = !(link->flag & NODE_LINK_MUTED); - if (mute) { - nodeMuteLink(link); - } - else { - nodeUnMuteLink(link); - } - if (link->tonode->type == NODE_REROUTE) { - nodeMuteRerouteOutputLinks(ntree, link->tonode, mute); - } - if (link->fromnode->type == NODE_REROUTE) { - nodeMuteRerouteInputLinks(ntree, link->fromnode, mute); - } - } - - if (ntree) { + const bool was_muted = link->flag & NODE_LINK_MUTED; + SET_FLAG_FROM_TEST(link->flag, muted, NODE_LINK_MUTED); + if (muted != was_muted) { BKE_ntree_update_tag_link_mute(ntree, link); } } @@ -2660,9 +2566,9 @@ void nodePositionRelative(bNode *from_node, void nodePositionPropagate(bNode *node) { - LISTBASE_FOREACH (bNodeSocket *, nsock, &node->inputs) { - if (nsock->link != nullptr) { - bNodeLink *link = nsock->link; + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (socket->link != nullptr) { + bNodeLink *link = socket->link; nodePositionRelative(link->fromnode, link->tonode, link->fromsock, link->tosock); nodePositionPropagate(link->fromnode); } diff --git a/source/blender/blenkernel/intern/vfont.c b/source/blender/blenkernel/intern/vfont.c index 9a6f861eae8..e016cf8db84 100644 --- a/source/blender/blenkernel/intern/vfont.c +++ b/source/blender/blenkernel/intern/vfont.c @@ -1422,7 +1422,8 @@ static bool vfont_to_curve(Object *ob, for (i = 0; i <= selend; i++, ct++) { if (i >= selstart) { selboxes[i - selstart].x = ct->xof * font_size; - selboxes[i - selstart].y = ct->yof * font_size; + selboxes[i - selstart].y = (ct->yof - 0.25f) * font_size; + selboxes[i - selstart].h = font_size; } } } @@ -1481,17 +1482,17 @@ static bool vfont_to_curve(Object *ob, f = ef->textcurs[0]; - f[0] = font_size * (-0.1f * co + ct->xof); - f[1] = font_size * (0.1f * si + ct->yof); + f[0] = font_size * (-0.02f * co + ct->xof); + f[1] = font_size * (0.1f * si - (0.25f * co) + ct->yof); - f[2] = font_size * (0.1f * co + ct->xof); - f[3] = font_size * (-0.1f * si + ct->yof); + f[2] = font_size * (0.02f * co + ct->xof); + f[3] = font_size * (-0.1f * si - (0.25f * co) + ct->yof); - f[4] = font_size * (0.1f * co + 0.8f * si + ct->xof); - f[5] = font_size * (-0.1f * si + 0.8f * co + ct->yof); + f[4] = font_size * (0.02f * co + 0.8f * si + ct->xof); + f[5] = font_size * (-0.1f * si + 0.75f * co + ct->yof); - f[6] = font_size * (-0.1f * co + 0.8f * si + ct->xof); - f[7] = font_size * (0.1f * si + 0.8f * co + ct->yof); + f[6] = font_size * (-0.02f * co + 0.8f * si + ct->xof); + f[7] = font_size * (0.1f * si + 0.75f * co + ct->yof); } if (mode == FO_SELCHANGE) { diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index e0250db8c4b..0190f91250b 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -486,7 +486,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar BM_face_select_set(bm, f, true); } - f->mat_nr = material_indices == NULL ? 0 : material_indices[i]; + f->mat_nr = material_indices == nullptr ? 0 : material_indices[i]; if (i == me->act_face) { bm->act_face = f; } diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 939e302b3d2..f0c0a435e2a 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -79,19 +79,21 @@ set(SRC intern/draw_cache_impl_subdivision.cc intern/draw_cache_impl_volume.c intern/draw_color_management.cc + intern/draw_command.cc intern/draw_common.c intern/draw_curves.cc intern/draw_debug.cc intern/draw_fluid.c intern/draw_hair.cc intern/draw_instance_data.c - intern/draw_manager.c intern/draw_manager_data.c intern/draw_manager_exec.c intern/draw_manager_profiling.c intern/draw_manager_shader.c intern/draw_manager_text.c intern/draw_manager_texture.c + intern/draw_manager.c + intern/draw_manager.cc intern/draw_select_buffer.c intern/draw_shader.cc intern/draw_texture_pool.cc @@ -206,28 +208,34 @@ set(SRC intern/DRW_gpu_wrapper.hh intern/DRW_render.h intern/draw_attributes.h - intern/draw_cache.h intern/draw_cache_extract.hh intern/draw_cache_impl.h intern/draw_cache_inline.h + intern/draw_cache.h intern/draw_color_management.h - intern/draw_common.h + intern/draw_command.hh intern/draw_common_shader_shared.h + intern/draw_common.h intern/draw_curves_private.h intern/draw_debug.h intern/draw_debug.hh intern/draw_hair_private.h intern/draw_instance_data.h - intern/draw_manager.h intern/draw_manager_profiling.h intern/draw_manager_testing.h intern/draw_manager_text.h - intern/draw_shader.h + intern/draw_manager.h + intern/draw_manager.hh + intern/draw_pass.hh + intern/draw_resource.cc + intern/draw_resource.hh intern/draw_shader_shared.h + intern/draw_shader.h intern/draw_subdivision.h intern/draw_texture_pool.h - intern/draw_view.h intern/draw_view_data.h + intern/draw_view.cc + intern/draw_view.h intern/mesh_extractors/extract_mesh.hh intern/smaa_textures.h engines/basic/basic_engine.h @@ -496,14 +504,19 @@ set(GLSL_SRC intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl intern/shaders/common_view_clipping_lib.glsl intern/shaders/common_view_lib.glsl + intern/shaders/draw_command_generate_comp.glsl intern/shaders/draw_debug_draw_display_frag.glsl intern/shaders/draw_debug_draw_display_vert.glsl intern/shaders/draw_debug_info.hh intern/shaders/draw_debug_print_display_frag.glsl intern/shaders/draw_debug_print_display_vert.glsl + intern/shaders/draw_resource_finalize_comp.glsl + intern/shaders/draw_visibility_comp.glsl intern/draw_common_shader_shared.h + intern/draw_command_shared.hh intern/draw_shader_shared.h + intern/draw_defines.h engines/gpencil/shaders/gpencil_frag.glsl engines/gpencil/shaders/gpencil_vert.glsl @@ -708,6 +721,7 @@ if(WITH_GTESTS) if(WITH_OPENGL_DRAW_TESTS) set(TEST_SRC tests/draw_testing.cc + tests/draw_pass_test.cc tests/shaders_test.cc tests/draw_testing.hh diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl index 9ecc50d9df5..c7f6687d2e2 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl @@ -100,7 +100,7 @@ void main() coef = 0.315392 * (3.0 * cubevec.y * cubevec.y - 1.0) * 1.0 / 4.0; } else if (comp == 7) { - coef = 1.092548 * cubevec.x * cubevec.y * 1.0 / 4.0; + coef = -1.092548 * cubevec.x * cubevec.y * 1.0 / 4.0; } else { /* (comp == 8) */ coef = 0.546274 * (cubevec.x * cubevec.x - cubevec.z * cubevec.z) * 1.0 / 4.0; diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl index 57d70334651..062a40f35c2 100644 --- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl @@ -152,3 +152,7 @@ vec4 attr_load_color_post(vec4 attr) { return attr; } +vec4 attr_load_uniform(vec4 attr, const uint attr_hash) +{ + return attr; +} diff --git a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl index 2a212b757c2..f84db01de18 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl @@ -182,3 +182,7 @@ vec4 attr_load_color_post(vec4 attr) { return attr; } +vec4 attr_load_uniform(vec4 attr, const uint attr_hash) +{ + return attr; +} diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl index 488e229bff7..b984a6df7b3 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -98,10 +98,11 @@ GlobalData init_globals(void) # if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) surf.P = transform_direction(ViewMatrixInverse, -viewCameraVec(viewPosition)); - surf.N = surf.Ng = -surf.P; + surf.N = surf.Ng = surf.Ni = -surf.P; surf.ray_length = 0.0; # else surf.P = worldPosition; + surf.Ni = worldNormal; surf.N = safe_normalize(worldNormal); surf.Ng = safe_normalize(cross(dFdx(surf.P), dFdy(surf.P))); surf.ray_length = distance(surf.P, cameraPos); @@ -123,7 +124,7 @@ GlobalData init_globals(void) cos_theta = hairThickTime / hairThickness; } float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); - surf.N = safe_normalize(worldNormal * sin_theta + B * cos_theta); + surf.N = surf.Ni = safe_normalize(worldNormal * sin_theta + B * cos_theta); surf.curve_T = -hairTangent; /* Costly, but follows cycles per pixel tangent space (not following curve shape). */ surf.curve_B = cross(V, surf.curve_T); diff --git a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl index 4a3a91b8534..54aad7891dc 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl @@ -165,3 +165,7 @@ vec4 attr_load_color_post(vec4 attr) { return attr; } +vec4 attr_load_uniform(vec4 attr, const uint attr_hash) +{ + return attr; +} diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl index c6de723ac25..9ed21fc0bf5 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -178,3 +178,7 @@ vec4 attr_load_color_post(vec4 attr) #endif return attr; } +vec4 attr_load_uniform(vec4 attr, const uint attr_hash) +{ + return attr; +} diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl index b3b9c7af19c..2d51fbd9edc 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl @@ -87,3 +87,8 @@ vec4 attr_load_color_post(vec4 attr) { return attr; } + +vec4 attr_load_uniform(vec4 attr, const uint attr_hash) +{ + return attr; +} diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index 67643471639..ec05cce3d02 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -68,3 +68,37 @@ #define DOF_FILTER_GROUP_SIZE 8 #define DOF_GATHER_GROUP_SIZE DOF_TILES_SIZE #define DOF_RESOLVE_GROUP_SIZE (DOF_TILES_SIZE * 2) + +/* Resource bindings. */ + +/* Texture. */ +#define RBUFS_UTILITY_TEX_SLOT 14 + +/* Images. */ +#define RBUFS_NORMAL_SLOT 0 +#define RBUFS_LIGHT_SLOT 1 +#define RBUFS_DIFF_COLOR_SLOT 2 +#define RBUFS_SPEC_COLOR_SLOT 3 +#define RBUFS_EMISSION_SLOT 4 +#define RBUFS_AOV_COLOR_SLOT 5 +#define RBUFS_AOV_VALUE_SLOT 6 + +/* Uniform Bufs. */ +/* Only during prepass. */ +#define VELOCITY_CAMERA_PREV_BUF 3 +#define VELOCITY_CAMERA_CURR_BUF 4 +#define VELOCITY_CAMERA_NEXT_BUF 5 + +/* Storage Bufs. */ +#define LIGHT_CULL_BUF_SLOT 0 +#define LIGHT_BUF_SLOT 1 +#define LIGHT_ZBIN_BUF_SLOT 2 +#define LIGHT_TILE_BUF_SLOT 3 +#define RBUFS_AOV_BUF_SLOT 5 +#define SAMPLING_BUF_SLOT 6 +/* Only during prepass. */ +#define VELOCITY_OBJ_PREV_BUF_SLOT 0 +#define VELOCITY_OBJ_NEXT_BUF_SLOT 1 +#define VELOCITY_GEO_PREV_BUF_SLOT 2 +#define VELOCITY_GEO_NEXT_BUF_SLOT 3 +#define VELOCITY_INDIRECTION_BUF_SLOT 4 diff --git a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc index afabeb8b729..bc0891ceb92 100644 --- a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc +++ b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc @@ -237,35 +237,34 @@ void DepthOfField::bokeh_lut_pass_sync() const bool has_anisotropy = data_.bokeh_anisotropic_scale != float2(1.0f); if (!has_anisotropy && (data_.bokeh_blades == 0.0)) { /* No need for LUTs in these cases. */ - bokeh_lut_ps_ = nullptr; + use_bokeh_lut_ = false; return; } + use_bokeh_lut_ = true; /* Precompute bokeh texture. */ - bokeh_lut_ps_ = DRW_pass_create("Dof.bokeh_lut_ps_", DRW_STATE_NO_DRAW); - GPUShader *sh = inst_.shaders.static_shader_get(DOF_BOKEH_LUT); - DRWShadingGroup *grp = DRW_shgroup_create(sh, bokeh_lut_ps_); - DRW_shgroup_uniform_block(grp, "dof_buf", data_); - DRW_shgroup_uniform_image_ref(grp, "out_gather_lut_img", &bokeh_gather_lut_tx_); - DRW_shgroup_uniform_image_ref(grp, "out_scatter_lut_img", &bokeh_scatter_lut_tx_); - DRW_shgroup_uniform_image_ref(grp, "out_resolve_lut_img", &bokeh_resolve_lut_tx_); - DRW_shgroup_call_compute(grp, 1, 1, 1); + bokeh_lut_ps_.init(); + bokeh_lut_ps_.shader_set(inst_.shaders.static_shader_get(DOF_BOKEH_LUT)); + bokeh_lut_ps_.bind_ubo("dof_buf", data_); + bokeh_lut_ps_.bind_image("out_gather_lut_img", &bokeh_gather_lut_tx_); + bokeh_lut_ps_.bind_image("out_scatter_lut_img", &bokeh_scatter_lut_tx_); + bokeh_lut_ps_.bind_image("out_resolve_lut_img", &bokeh_resolve_lut_tx_); + bokeh_lut_ps_.dispatch(int3(1, 1, 1)); } void DepthOfField::setup_pass_sync() { RenderBuffers &render_buffers = inst_.render_buffers; - setup_ps_ = DRW_pass_create("Dof.setup_ps_", DRW_STATE_NO_DRAW); - GPUShader *sh = inst_.shaders.static_shader_get(DOF_SETUP); - DRWShadingGroup *grp = DRW_shgroup_create(sh, setup_ps_); - DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter); - DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &render_buffers.depth_tx, no_filter); - DRW_shgroup_uniform_block(grp, "dof_buf", data_); - DRW_shgroup_uniform_image_ref(grp, "out_color_img", &setup_color_tx_); - DRW_shgroup_uniform_image_ref(grp, "out_coc_img", &setup_coc_tx_); - DRW_shgroup_call_compute_ref(grp, dispatch_setup_size_); - DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + setup_ps_.init(); + setup_ps_.shader_set(inst_.shaders.static_shader_get(DOF_SETUP)); + setup_ps_.bind_texture("color_tx", &input_color_tx_, no_filter); + setup_ps_.bind_texture("depth_tx", &render_buffers.depth_tx, no_filter); + setup_ps_.bind_ubo("dof_buf", data_); + setup_ps_.bind_image("out_color_img", &setup_color_tx_); + setup_ps_.bind_image("out_coc_img", &setup_coc_tx_); + setup_ps_.dispatch(&dispatch_setup_size_); + setup_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH); } void DepthOfField::stabilize_pass_sync() @@ -273,214 +272,203 @@ void DepthOfField::stabilize_pass_sync() RenderBuffers &render_buffers = inst_.render_buffers; VelocityModule &velocity = inst_.velocity; - stabilize_ps_ = DRW_pass_create("Dof.stabilize_ps_", DRW_STATE_NO_DRAW); - GPUShader *sh = inst_.shaders.static_shader_get(DOF_STABILIZE); - DRWShadingGroup *grp = DRW_shgroup_create(sh, stabilize_ps_); - DRW_shgroup_uniform_block_ref(grp, "camera_prev", &(*velocity.camera_steps[STEP_PREVIOUS])); - DRW_shgroup_uniform_block_ref(grp, "camera_curr", &(*velocity.camera_steps[STEP_CURRENT])); + stabilize_ps_.init(); + stabilize_ps_.shader_set(inst_.shaders.static_shader_get(DOF_STABILIZE)); + stabilize_ps_.bind_ubo("camera_prev", &(*velocity.camera_steps[STEP_PREVIOUS])); + stabilize_ps_.bind_ubo("camera_curr", &(*velocity.camera_steps[STEP_CURRENT])); /* This is only for temporal stability. The next step is not needed. */ - DRW_shgroup_uniform_block_ref(grp, "camera_next", &(*velocity.camera_steps[STEP_PREVIOUS])); - DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter); - DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &setup_color_tx_, no_filter); - DRW_shgroup_uniform_texture_ref_ex(grp, "velocity_tx", &render_buffers.vector_tx, no_filter); - DRW_shgroup_uniform_texture_ref_ex(grp, "in_history_tx", &stabilize_input_, with_filter); - DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &render_buffers.depth_tx, no_filter); - DRW_shgroup_uniform_bool(grp, "use_history", &stabilize_valid_history_, 1); - DRW_shgroup_uniform_block(grp, "dof_buf", data_); - DRW_shgroup_uniform_image(grp, "out_coc_img", reduced_coc_tx_.mip_view(0)); - DRW_shgroup_uniform_image(grp, "out_color_img", reduced_color_tx_.mip_view(0)); - DRW_shgroup_uniform_image_ref(grp, "out_history_img", &stabilize_output_tx_); - DRW_shgroup_call_compute_ref(grp, dispatch_stabilize_size_); - DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS); + stabilize_ps_.bind_ubo("camera_next", &(*velocity.camera_steps[STEP_PREVIOUS])); + stabilize_ps_.bind_texture("coc_tx", &setup_coc_tx_, no_filter); + stabilize_ps_.bind_texture("color_tx", &setup_color_tx_, no_filter); + stabilize_ps_.bind_texture("velocity_tx", &render_buffers.vector_tx, no_filter); + stabilize_ps_.bind_texture("in_history_tx", &stabilize_input_, with_filter); + stabilize_ps_.bind_texture("depth_tx", &render_buffers.depth_tx, no_filter); + stabilize_ps_.bind_ubo("dof_buf", data_); + stabilize_ps_.push_constant("use_history", &stabilize_valid_history_, 1); + stabilize_ps_.bind_image("out_coc_img", reduced_coc_tx_.mip_view(0)); + stabilize_ps_.bind_image("out_color_img", reduced_color_tx_.mip_view(0)); + stabilize_ps_.bind_image("out_history_img", &stabilize_output_tx_); + stabilize_ps_.dispatch(&dispatch_stabilize_size_); + stabilize_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS); } void DepthOfField::downsample_pass_sync() { - downsample_ps_ = DRW_pass_create("Dof.downsample_ps_", DRW_STATE_NO_DRAW); - GPUShader *sh = inst_.shaders.static_shader_get(DOF_DOWNSAMPLE); - DRWShadingGroup *grp = DRW_shgroup_create(sh, downsample_ps_); - DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_.mip_view(0), no_filter); - DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_.mip_view(0), no_filter); - DRW_shgroup_uniform_image_ref(grp, "out_color_img", &downsample_tx_); - DRW_shgroup_call_compute_ref(grp, dispatch_downsample_size_); - DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + downsample_ps_.init(); + downsample_ps_.shader_set(inst_.shaders.static_shader_get(DOF_DOWNSAMPLE)); + downsample_ps_.bind_texture("color_tx", reduced_color_tx_.mip_view(0), no_filter); + downsample_ps_.bind_texture("coc_tx", reduced_coc_tx_.mip_view(0), no_filter); + downsample_ps_.bind_image("out_color_img", &downsample_tx_); + downsample_ps_.dispatch(&dispatch_downsample_size_); + downsample_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH); } void DepthOfField::reduce_pass_sync() { - reduce_ps_ = DRW_pass_create("Dof.reduce_ps_", DRW_STATE_NO_DRAW); - GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE); - DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_ps_); - DRW_shgroup_uniform_block(grp, "dof_buf", data_); - DRW_shgroup_uniform_texture_ref_ex(grp, "downsample_tx", &downsample_tx_, no_filter); - DRW_shgroup_storage_block(grp, "scatter_fg_list_buf", scatter_fg_list_buf_); - DRW_shgroup_storage_block(grp, "scatter_bg_list_buf", scatter_bg_list_buf_); - DRW_shgroup_storage_block(grp, "scatter_fg_indirect_buf", scatter_fg_indirect_buf_); - DRW_shgroup_storage_block(grp, "scatter_bg_indirect_buf", scatter_bg_indirect_buf_); - DRW_shgroup_uniform_image(grp, "inout_color_lod0_img", reduced_color_tx_.mip_view(0)); - DRW_shgroup_uniform_image(grp, "out_color_lod1_img", reduced_color_tx_.mip_view(1)); - DRW_shgroup_uniform_image(grp, "out_color_lod2_img", reduced_color_tx_.mip_view(2)); - DRW_shgroup_uniform_image(grp, "out_color_lod3_img", reduced_color_tx_.mip_view(3)); - DRW_shgroup_uniform_image(grp, "in_coc_lod0_img", reduced_coc_tx_.mip_view(0)); - DRW_shgroup_uniform_image(grp, "out_coc_lod1_img", reduced_coc_tx_.mip_view(1)); - DRW_shgroup_uniform_image(grp, "out_coc_lod2_img", reduced_coc_tx_.mip_view(2)); - DRW_shgroup_uniform_image(grp, "out_coc_lod3_img", reduced_coc_tx_.mip_view(3)); - DRW_shgroup_call_compute_ref(grp, dispatch_reduce_size_); + reduce_ps_.init(); + reduce_ps_.shader_set(inst_.shaders.static_shader_get(DOF_REDUCE)); + reduce_ps_.bind_ubo("dof_buf", data_); + reduce_ps_.bind_texture("downsample_tx", &downsample_tx_, no_filter); + reduce_ps_.bind_ssbo("scatter_fg_list_buf", scatter_fg_list_buf_); + reduce_ps_.bind_ssbo("scatter_bg_list_buf", scatter_bg_list_buf_); + reduce_ps_.bind_ssbo("scatter_fg_indirect_buf", scatter_fg_indirect_buf_); + reduce_ps_.bind_ssbo("scatter_bg_indirect_buf", scatter_bg_indirect_buf_); + reduce_ps_.bind_image("inout_color_lod0_img", reduced_color_tx_.mip_view(0)); + reduce_ps_.bind_image("out_color_lod1_img", reduced_color_tx_.mip_view(1)); + reduce_ps_.bind_image("out_color_lod2_img", reduced_color_tx_.mip_view(2)); + reduce_ps_.bind_image("out_color_lod3_img", reduced_color_tx_.mip_view(3)); + reduce_ps_.bind_image("in_coc_lod0_img", reduced_coc_tx_.mip_view(0)); + reduce_ps_.bind_image("out_coc_lod1_img", reduced_coc_tx_.mip_view(1)); + reduce_ps_.bind_image("out_coc_lod2_img", reduced_coc_tx_.mip_view(2)); + reduce_ps_.bind_image("out_coc_lod3_img", reduced_coc_tx_.mip_view(3)); + reduce_ps_.dispatch(&dispatch_reduce_size_); /* NOTE: Command buffer barrier is done automatically by the GPU backend. */ - DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_STORAGE); + reduce_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_STORAGE); } void DepthOfField::tiles_flatten_pass_sync() { - tiles_flatten_ps_ = DRW_pass_create("Dof.tiles_flatten_ps_", DRW_STATE_NO_DRAW); - GPUShader *sh = inst_.shaders.static_shader_get(DOF_TILES_FLATTEN); - DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_flatten_ps_); + tiles_flatten_ps_.init(); + tiles_flatten_ps_.shader_set(inst_.shaders.static_shader_get(DOF_TILES_FLATTEN)); /* NOTE(fclem): We should use the reduced_coc_tx_ as it is stable, but we need the slight focus * flag from the setup pass. A better way would be to do the brute-force in focus gather without * this. */ - DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter); - DRW_shgroup_uniform_image_ref(grp, "out_tiles_fg_img", &tiles_fg_tx_.current()); - DRW_shgroup_uniform_image_ref(grp, "out_tiles_bg_img", &tiles_bg_tx_.current()); - DRW_shgroup_call_compute_ref(grp, dispatch_tiles_flatten_size_); - DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + tiles_flatten_ps_.bind_texture("coc_tx", &setup_coc_tx_, no_filter); + tiles_flatten_ps_.bind_image("out_tiles_fg_img", &tiles_fg_tx_.current()); + tiles_flatten_ps_.bind_image("out_tiles_bg_img", &tiles_bg_tx_.current()); + tiles_flatten_ps_.dispatch(&dispatch_tiles_flatten_size_); + tiles_flatten_ps_.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); } void DepthOfField::tiles_dilate_pass_sync() { - tiles_dilate_minmax_ps_ = DRW_pass_create("Dof.tiles_dilate_minmax_ps_", DRW_STATE_NO_DRAW); - tiles_dilate_minabs_ps_ = DRW_pass_create("Dof.tiles_dilate_minabs_ps_", DRW_STATE_NO_DRAW); for (int pass = 0; pass < 2; pass++) { - DRWPass *drw_pass = (pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_; - GPUShader *sh = inst_.shaders.static_shader_get((pass == 0) ? DOF_TILES_DILATE_MINMAX : - DOF_TILES_DILATE_MINABS); - DRWShadingGroup *grp = DRW_shgroup_create(sh, drw_pass); - DRW_shgroup_uniform_image_ref(grp, "in_tiles_fg_img", &tiles_fg_tx_.previous()); - DRW_shgroup_uniform_image_ref(grp, "in_tiles_bg_img", &tiles_bg_tx_.previous()); - DRW_shgroup_uniform_image_ref(grp, "out_tiles_fg_img", &tiles_fg_tx_.current()); - DRW_shgroup_uniform_image_ref(grp, "out_tiles_bg_img", &tiles_bg_tx_.current()); - DRW_shgroup_uniform_int(grp, "ring_count", &tiles_dilate_ring_count_, 1); - DRW_shgroup_uniform_int(grp, "ring_width_multiplier", &tiles_dilate_ring_width_mul_, 1); - DRW_shgroup_call_compute_ref(grp, dispatch_tiles_dilate_size_); - DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + PassSimple &drw_pass = (pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_; + eShaderType sh_type = (pass == 0) ? DOF_TILES_DILATE_MINMAX : DOF_TILES_DILATE_MINABS; + drw_pass.init(); + drw_pass.shader_set(inst_.shaders.static_shader_get(sh_type)); + drw_pass.bind_image("in_tiles_fg_img", &tiles_fg_tx_.previous()); + drw_pass.bind_image("in_tiles_bg_img", &tiles_bg_tx_.previous()); + drw_pass.bind_image("out_tiles_fg_img", &tiles_fg_tx_.current()); + drw_pass.bind_image("out_tiles_bg_img", &tiles_bg_tx_.current()); + drw_pass.push_constant("ring_count", &tiles_dilate_ring_count_, 1); + drw_pass.push_constant("ring_width_multiplier", &tiles_dilate_ring_width_mul_, 1); + drw_pass.dispatch(&dispatch_tiles_dilate_size_); + drw_pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); } } void DepthOfField::gather_pass_sync() { - gather_fg_ps_ = DRW_pass_create("Dof.gather_fg_ps_", DRW_STATE_NO_DRAW); - gather_bg_ps_ = DRW_pass_create("Dof.gather_bg_ps_", DRW_STATE_NO_DRAW); for (int pass = 0; pass < 2; pass++) { + PassSimple &drw_pass = (pass == 0) ? gather_fg_ps_ : gather_bg_ps_; SwapChain<TextureFromPool, 2> &color_chain = (pass == 0) ? color_fg_tx_ : color_bg_tx_; SwapChain<TextureFromPool, 2> &weight_chain = (pass == 0) ? weight_fg_tx_ : weight_bg_tx_; - bool use_lut = bokeh_lut_ps_ != nullptr; eShaderType sh_type = (pass == 0) ? - (use_lut ? DOF_GATHER_FOREGROUND_LUT : DOF_GATHER_FOREGROUND) : - (use_lut ? DOF_GATHER_BACKGROUND_LUT : DOF_GATHER_BACKGROUND); - GPUShader *sh = inst_.shaders.static_shader_get(sh_type); - DRWShadingGroup *grp = DRW_shgroup_create(sh, (pass == 0) ? gather_fg_ps_ : gather_bg_ps_); - inst_.sampling.bind_resources(grp); - DRW_shgroup_uniform_block(grp, "dof_buf", data_); - DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, gather_bilinear); - DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, gather_nearest); - DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, gather_nearest); - DRW_shgroup_uniform_image_ref(grp, "in_tiles_fg_img", &tiles_fg_tx_.current()); - DRW_shgroup_uniform_image_ref(grp, "in_tiles_bg_img", &tiles_bg_tx_.current()); - DRW_shgroup_uniform_image_ref(grp, "out_color_img", &color_chain.current()); - DRW_shgroup_uniform_image_ref(grp, "out_weight_img", &weight_chain.current()); - DRW_shgroup_uniform_image_ref(grp, "out_occlusion_img", &occlusion_tx_); - DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_gather_lut_tx_); - DRW_shgroup_call_compute_ref(grp, dispatch_gather_size_); - DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + (use_bokeh_lut_ ? DOF_GATHER_FOREGROUND_LUT : + DOF_GATHER_FOREGROUND) : + (use_bokeh_lut_ ? DOF_GATHER_BACKGROUND_LUT : DOF_GATHER_BACKGROUND); + drw_pass.init(); + inst_.sampling.bind_resources(&drw_pass); + drw_pass.shader_set(inst_.shaders.static_shader_get(sh_type)); + drw_pass.bind_ubo("dof_buf", data_); + drw_pass.bind_texture("color_bilinear_tx", reduced_color_tx_, gather_bilinear); + drw_pass.bind_texture("color_tx", reduced_color_tx_, gather_nearest); + drw_pass.bind_texture("coc_tx", reduced_coc_tx_, gather_nearest); + drw_pass.bind_image("in_tiles_fg_img", &tiles_fg_tx_.current()); + drw_pass.bind_image("in_tiles_bg_img", &tiles_bg_tx_.current()); + drw_pass.bind_image("out_color_img", &color_chain.current()); + drw_pass.bind_image("out_weight_img", &weight_chain.current()); + drw_pass.bind_image("out_occlusion_img", &occlusion_tx_); + drw_pass.bind_texture("bokeh_lut_tx", &bokeh_gather_lut_tx_); + drw_pass.dispatch(&dispatch_gather_size_); + drw_pass.barrier(GPU_BARRIER_TEXTURE_FETCH); } } void DepthOfField::filter_pass_sync() { - filter_fg_ps_ = DRW_pass_create("Dof.filter_fg_ps_", DRW_STATE_NO_DRAW); - filter_bg_ps_ = DRW_pass_create("Dof.filter_bg_ps_", DRW_STATE_NO_DRAW); for (int pass = 0; pass < 2; pass++) { + PassSimple &drw_pass = (pass == 0) ? filter_fg_ps_ : filter_bg_ps_; SwapChain<TextureFromPool, 2> &color_chain = (pass == 0) ? color_fg_tx_ : color_bg_tx_; SwapChain<TextureFromPool, 2> &weight_chain = (pass == 0) ? weight_fg_tx_ : weight_bg_tx_; - GPUShader *sh = inst_.shaders.static_shader_get(DOF_FILTER); - DRWShadingGroup *grp = DRW_shgroup_create(sh, (pass == 0) ? filter_fg_ps_ : filter_bg_ps_); - DRW_shgroup_uniform_texture_ref(grp, "color_tx", &color_chain.previous()); - DRW_shgroup_uniform_texture_ref(grp, "weight_tx", &weight_chain.previous()); - DRW_shgroup_uniform_image_ref(grp, "out_color_img", &color_chain.current()); - DRW_shgroup_uniform_image_ref(grp, "out_weight_img", &weight_chain.current()); - DRW_shgroup_call_compute_ref(grp, dispatch_filter_size_); - DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + drw_pass.init(); + drw_pass.shader_set(inst_.shaders.static_shader_get(DOF_FILTER)); + drw_pass.bind_texture("color_tx", &color_chain.previous()); + drw_pass.bind_texture("weight_tx", &weight_chain.previous()); + drw_pass.bind_image("out_color_img", &color_chain.current()); + drw_pass.bind_image("out_weight_img", &weight_chain.current()); + drw_pass.dispatch(&dispatch_filter_size_); + drw_pass.barrier(GPU_BARRIER_TEXTURE_FETCH); } } void DepthOfField::scatter_pass_sync() { - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL; - scatter_fg_ps_ = DRW_pass_create("Dof.scatter_fg_ps_", state); - scatter_bg_ps_ = DRW_pass_create("Dof.scatter_bg_ps_", state); for (int pass = 0; pass < 2; pass++) { - GPUStorageBuf *scatter_buf = (pass == 0) ? scatter_fg_indirect_buf_ : scatter_bg_indirect_buf_; - GPUStorageBuf *rect_list_buf = (pass == 0) ? scatter_fg_list_buf_ : scatter_bg_list_buf_; - - GPUShader *sh = inst_.shaders.static_shader_get(DOF_SCATTER); - DRWShadingGroup *grp = DRW_shgroup_create(sh, (pass == 0) ? scatter_fg_ps_ : scatter_bg_ps_); - DRW_shgroup_uniform_bool_copy(grp, "use_bokeh_lut", bokeh_lut_ps_ != nullptr); - DRW_shgroup_storage_block(grp, "scatter_list_buf", rect_list_buf); - DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_scatter_lut_tx_); - DRW_shgroup_uniform_texture_ref(grp, "occlusion_tx", &occlusion_tx_); - DRW_shgroup_call_procedural_indirect(grp, GPU_PRIM_TRI_STRIP, nullptr, scatter_buf); + PassSimple &drw_pass = (pass == 0) ? scatter_fg_ps_ : scatter_bg_ps_; + drw_pass.init(); + drw_pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL); + drw_pass.shader_set(inst_.shaders.static_shader_get(DOF_SCATTER)); + drw_pass.push_constant("use_bokeh_lut", use_bokeh_lut_); + drw_pass.bind_texture("bokeh_lut_tx", &bokeh_scatter_lut_tx_); + drw_pass.bind_texture("occlusion_tx", &occlusion_tx_); if (pass == 0) { + drw_pass.bind_ssbo("scatter_list_buf", scatter_fg_list_buf_); + drw_pass.draw_procedural_indirect(GPU_PRIM_TRI_STRIP, scatter_fg_indirect_buf_); /* Avoid background gather pass writing to the occlusion_tx mid pass. */ - DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + drw_pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); + } + else { + drw_pass.bind_ssbo("scatter_list_buf", scatter_bg_list_buf_); + drw_pass.draw_procedural_indirect(GPU_PRIM_TRI_STRIP, scatter_bg_indirect_buf_); } } } void DepthOfField::hole_fill_pass_sync() { - hole_fill_ps_ = DRW_pass_create("Dof.hole_fill_ps_", DRW_STATE_NO_DRAW); - GPUShader *sh = inst_.shaders.static_shader_get(DOF_GATHER_HOLE_FILL); - DRWShadingGroup *grp = DRW_shgroup_create(sh, hole_fill_ps_); - inst_.sampling.bind_resources(grp); - DRW_shgroup_uniform_block(grp, "dof_buf", data_); - DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, gather_bilinear); - DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, gather_nearest); - DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, gather_nearest); - DRW_shgroup_uniform_image_ref(grp, "in_tiles_fg_img", &tiles_fg_tx_.current()); - DRW_shgroup_uniform_image_ref(grp, "in_tiles_bg_img", &tiles_bg_tx_.current()); - DRW_shgroup_uniform_image_ref(grp, "out_color_img", &hole_fill_color_tx_); - DRW_shgroup_uniform_image_ref(grp, "out_weight_img", &hole_fill_weight_tx_); - DRW_shgroup_call_compute_ref(grp, dispatch_gather_size_); - DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + hole_fill_ps_.init(); + inst_.sampling.bind_resources(&hole_fill_ps_); + hole_fill_ps_.shader_set(inst_.shaders.static_shader_get(DOF_GATHER_HOLE_FILL)); + hole_fill_ps_.bind_ubo("dof_buf", data_); + hole_fill_ps_.bind_texture("color_bilinear_tx", reduced_color_tx_, gather_bilinear); + hole_fill_ps_.bind_texture("color_tx", reduced_color_tx_, gather_nearest); + hole_fill_ps_.bind_texture("coc_tx", reduced_coc_tx_, gather_nearest); + hole_fill_ps_.bind_image("in_tiles_fg_img", &tiles_fg_tx_.current()); + hole_fill_ps_.bind_image("in_tiles_bg_img", &tiles_bg_tx_.current()); + hole_fill_ps_.bind_image("out_color_img", &hole_fill_color_tx_); + hole_fill_ps_.bind_image("out_weight_img", &hole_fill_weight_tx_); + hole_fill_ps_.dispatch(&dispatch_gather_size_); + hole_fill_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH); } void DepthOfField::resolve_pass_sync() { eGPUSamplerState with_filter = GPU_SAMPLER_FILTER; RenderBuffers &render_buffers = inst_.render_buffers; - - resolve_ps_ = DRW_pass_create("Dof.resolve_ps_", DRW_STATE_NO_DRAW); - bool use_lut = bokeh_lut_ps_ != nullptr; - eShaderType sh_type = use_lut ? DOF_RESOLVE_LUT : DOF_RESOLVE; - GPUShader *sh = inst_.shaders.static_shader_get(sh_type); - DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_); - inst_.sampling.bind_resources(grp); - DRW_shgroup_uniform_block(grp, "dof_buf", data_); - DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &render_buffers.depth_tx, no_filter); - DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter); - DRW_shgroup_uniform_texture_ref_ex(grp, "stable_color_tx", &resolve_stable_color_tx_, no_filter); - DRW_shgroup_uniform_texture_ref_ex(grp, "color_bg_tx", &color_bg_tx_.current(), with_filter); - DRW_shgroup_uniform_texture_ref_ex(grp, "color_fg_tx", &color_fg_tx_.current(), with_filter); - DRW_shgroup_uniform_image_ref(grp, "in_tiles_fg_img", &tiles_fg_tx_.current()); - DRW_shgroup_uniform_image_ref(grp, "in_tiles_bg_img", &tiles_bg_tx_.current()); - DRW_shgroup_uniform_texture_ref(grp, "weight_bg_tx", &weight_bg_tx_.current()); - DRW_shgroup_uniform_texture_ref(grp, "weight_fg_tx", &weight_fg_tx_.current()); - DRW_shgroup_uniform_texture_ref(grp, "color_hole_fill_tx", &hole_fill_color_tx_); - DRW_shgroup_uniform_texture_ref(grp, "weight_hole_fill_tx", &hole_fill_weight_tx_); - DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_resolve_lut_tx_); - DRW_shgroup_uniform_image_ref(grp, "out_color_img", &output_color_tx_); - DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); - DRW_shgroup_call_compute_ref(grp, dispatch_resolve_size_); - DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + eShaderType sh_type = use_bokeh_lut_ ? DOF_RESOLVE_LUT : DOF_RESOLVE; + + resolve_ps_.init(); + inst_.sampling.bind_resources(&resolve_ps_); + resolve_ps_.shader_set(inst_.shaders.static_shader_get(sh_type)); + resolve_ps_.bind_ubo("dof_buf", data_); + resolve_ps_.bind_texture("depth_tx", &render_buffers.depth_tx, no_filter); + resolve_ps_.bind_texture("color_tx", &input_color_tx_, no_filter); + resolve_ps_.bind_texture("stable_color_tx", &resolve_stable_color_tx_, no_filter); + resolve_ps_.bind_texture("color_bg_tx", &color_bg_tx_.current(), with_filter); + resolve_ps_.bind_texture("color_fg_tx", &color_fg_tx_.current(), with_filter); + resolve_ps_.bind_image("in_tiles_fg_img", &tiles_fg_tx_.current()); + resolve_ps_.bind_image("in_tiles_bg_img", &tiles_bg_tx_.current()); + resolve_ps_.bind_texture("weight_bg_tx", &weight_bg_tx_.current()); + resolve_ps_.bind_texture("weight_fg_tx", &weight_fg_tx_.current()); + resolve_ps_.bind_texture("color_hole_fill_tx", &hole_fill_color_tx_); + resolve_ps_.bind_texture("weight_hole_fill_tx", &hole_fill_weight_tx_); + resolve_ps_.bind_texture("bokeh_lut_tx", &bokeh_resolve_lut_tx_); + resolve_ps_.bind_image("out_color_img", &output_color_tx_); + resolve_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH); + resolve_ps_.dispatch(&dispatch_resolve_size_); + resolve_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH); } /** \} */ @@ -509,7 +497,8 @@ void DepthOfField::update_sample_table() data_.filter_center_weight = film_filter_weight(radius, math::length_squared(subpixel_offset)); } -void DepthOfField::render(GPUTexture **input_tx, +void DepthOfField::render(View &view, + GPUTexture **input_tx, GPUTexture **output_tx, DepthOfFieldBuffer &dof_buffer) { @@ -580,6 +569,8 @@ void DepthOfField::render(GPUTexture **input_tx, DRW_stats_group_start("Depth of Field"); + Manager &drw = *inst_.manager; + { DRW_stats_group_start("Setup"); { @@ -587,13 +578,15 @@ void DepthOfField::render(GPUTexture **input_tx, bokeh_scatter_lut_tx_.acquire(int2(DOF_BOKEH_LUT_SIZE), GPU_R16F); bokeh_resolve_lut_tx_.acquire(int2(DOF_MAX_SLIGHT_FOCUS_RADIUS * 2 + 1), GPU_R16F); - DRW_draw_pass(bokeh_lut_ps_); + if (use_bokeh_lut_) { + drw.submit(bokeh_lut_ps_, view); + } } { setup_color_tx_.acquire(half_res, GPU_RGBA16F); setup_coc_tx_.acquire(half_res, GPU_R16F); - DRW_draw_pass(setup_ps_); + drw.submit(setup_ps_, view); } { stabilize_output_tx_.acquire(half_res, GPU_RGBA16F); @@ -607,7 +600,7 @@ void DepthOfField::render(GPUTexture **input_tx, stabilize_input_ = dof_buffer.stabilize_history_tx_; /* Outputs to reduced_*_tx_ mip 0. */ - DRW_draw_pass(stabilize_ps_); + drw.submit(stabilize_ps_, view); /* WATCH(fclem): Swap Texture an TextureFromPool internal GPUTexture in order to reuse * the one that we just consumed. */ @@ -626,7 +619,7 @@ void DepthOfField::render(GPUTexture **input_tx, tiles_fg_tx_.current().acquire(tile_res, GPU_R11F_G11F_B10F); tiles_bg_tx_.current().acquire(tile_res, GPU_R11F_G11F_B10F); - DRW_draw_pass(tiles_flatten_ps_); + drw.submit(tiles_flatten_ps_, view); /* Used by tile_flatten and stabilize_ps pass. */ setup_coc_tx_.release(); @@ -655,7 +648,7 @@ void DepthOfField::render(GPUTexture **input_tx, tiles_fg_tx_.swap(); tiles_bg_tx_.swap(); - DRW_draw_pass((pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_); + drw.submit((pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_, view); } } @@ -667,12 +660,12 @@ void DepthOfField::render(GPUTexture **input_tx, downsample_tx_.acquire(quarter_res, GPU_RGBA16F); - DRW_draw_pass(downsample_ps_); + drw.submit(downsample_ps_, view); scatter_fg_indirect_buf_.clear_to_zero(); scatter_bg_indirect_buf_.clear_to_zero(); - DRW_draw_pass(reduce_ps_); + drw.submit(reduce_ps_, view); /* Used by reduce pass. */ downsample_tx_.release(); @@ -686,15 +679,15 @@ void DepthOfField::render(GPUTexture **input_tx, SwapChain<TextureFromPool, 2> &color_tx = is_background ? color_bg_tx_ : color_fg_tx_; SwapChain<TextureFromPool, 2> &weight_tx = is_background ? weight_bg_tx_ : weight_fg_tx_; Framebuffer &scatter_fb = is_background ? scatter_bg_fb_ : scatter_fg_fb_; - DRWPass *gather_ps = is_background ? gather_bg_ps_ : gather_fg_ps_; - DRWPass *filter_ps = is_background ? filter_bg_ps_ : filter_fg_ps_; - DRWPass *scatter_ps = is_background ? scatter_bg_ps_ : scatter_fg_ps_; + PassSimple &gather_ps = is_background ? gather_bg_ps_ : gather_fg_ps_; + PassSimple &filter_ps = is_background ? filter_bg_ps_ : filter_fg_ps_; + PassSimple &scatter_ps = is_background ? scatter_bg_ps_ : scatter_fg_ps_; color_tx.current().acquire(half_res, GPU_RGBA16F); weight_tx.current().acquire(half_res, GPU_R16F); occlusion_tx_.acquire(half_res, GPU_RG16F); - DRW_draw_pass(gather_ps); + drw.submit(gather_ps, view); { /* Filtering pass. */ @@ -704,7 +697,7 @@ void DepthOfField::render(GPUTexture **input_tx, color_tx.current().acquire(half_res, GPU_RGBA16F); weight_tx.current().acquire(half_res, GPU_R16F); - DRW_draw_pass(filter_ps); + drw.submit(filter_ps, view); color_tx.previous().release(); weight_tx.previous().release(); @@ -715,7 +708,7 @@ void DepthOfField::render(GPUTexture **input_tx, scatter_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_tx.current())); GPU_framebuffer_bind(scatter_fb); - DRW_draw_pass(scatter_ps); + drw.submit(scatter_ps, view); /* Used by scatter pass. */ occlusion_tx_.release(); @@ -731,7 +724,7 @@ void DepthOfField::render(GPUTexture **input_tx, hole_fill_color_tx_.acquire(half_res, GPU_RGBA16F); hole_fill_weight_tx_.acquire(half_res, GPU_R16F); - DRW_draw_pass(hole_fill_ps_); + drw.submit(hole_fill_ps_, view); /* NOTE: We do not filter the hole-fill pass as effect is likely to not be noticeable. */ @@ -742,7 +735,7 @@ void DepthOfField::render(GPUTexture **input_tx, resolve_stable_color_tx_ = dof_buffer.stabilize_history_tx_; - DRW_draw_pass(resolve_ps_); + drw.submit(resolve_ps_, view); color_bg_tx_.current().release(); color_fg_tx_.current().release(); diff --git a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh index 8c291b241bd..bac0e394d66 100644 --- a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh +++ b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh @@ -56,13 +56,13 @@ class DepthOfField { TextureFromPool bokeh_gather_lut_tx_ = {"dof_bokeh_gather_lut"}; TextureFromPool bokeh_resolve_lut_tx_ = {"dof_bokeh_resolve_lut"}; TextureFromPool bokeh_scatter_lut_tx_ = {"dof_bokeh_scatter_lut"}; - DRWPass *bokeh_lut_ps_ = nullptr; + PassSimple bokeh_lut_ps_ = {"BokehLut"}; /** Outputs half-resolution color and Circle Of Confusion. */ TextureFromPool setup_coc_tx_ = {"dof_setup_coc"}; TextureFromPool setup_color_tx_ = {"dof_setup_color"}; int3 dispatch_setup_size_ = int3(-1); - DRWPass *setup_ps_ = nullptr; + PassSimple setup_ps_ = {"Setup"}; /** Allocated because we need mip chain. Which isn't supported by TextureFromPool. */ Texture reduced_coc_tx_ = {"dof_reduced_coc"}; @@ -73,12 +73,12 @@ class DepthOfField { GPUTexture *stabilize_input_ = nullptr; bool1 stabilize_valid_history_ = false; int3 dispatch_stabilize_size_ = int3(-1); - DRWPass *stabilize_ps_ = nullptr; + PassSimple stabilize_ps_ = {"Stabilize"}; /** 1/4th res color buffer used to speedup the local contrast test in the first reduce pass. */ TextureFromPool downsample_tx_ = {"dof_downsample"}; int3 dispatch_downsample_size_ = int3(-1); - DRWPass *downsample_ps_ = nullptr; + PassSimple downsample_ps_ = {"Downsample"}; /** Create mip-mapped color & COC textures for gather passes as well as scatter rect list. */ DepthOfFieldScatterListBuf scatter_fg_list_buf_; @@ -86,20 +86,20 @@ class DepthOfField { DrawIndirectBuf scatter_fg_indirect_buf_; DrawIndirectBuf scatter_bg_indirect_buf_; int3 dispatch_reduce_size_ = int3(-1); - DRWPass *reduce_ps_ = nullptr; + PassSimple reduce_ps_ = {"Reduce"}; /** Outputs min & max COC in each 8x8 half res pixel tiles (so 1/16th of full resolution). */ SwapChain<TextureFromPool, 2> tiles_fg_tx_; SwapChain<TextureFromPool, 2> tiles_bg_tx_; int3 dispatch_tiles_flatten_size_ = int3(-1); - DRWPass *tiles_flatten_ps_ = nullptr; + PassSimple tiles_flatten_ps_ = {"TilesFlatten"}; /** Dilates the min & max CoCs to cover maximum COC values. */ int tiles_dilate_ring_count_ = -1; int tiles_dilate_ring_width_mul_ = -1; int3 dispatch_tiles_dilate_size_ = int3(-1); - DRWPass *tiles_dilate_minmax_ps_ = nullptr; - DRWPass *tiles_dilate_minabs_ps_ = nullptr; + PassSimple tiles_dilate_minmax_ps_ = {"TilesDilateMinmax"}; + PassSimple tiles_dilate_minabs_ps_ = {"TilesDilateMinabs"}; /** Gather convolution for low intensity pixels and low contrast areas. */ SwapChain<TextureFromPool, 2> color_bg_tx_; @@ -108,29 +108,29 @@ class DepthOfField { SwapChain<TextureFromPool, 2> weight_fg_tx_; TextureFromPool occlusion_tx_ = {"dof_occlusion"}; int3 dispatch_gather_size_ = int3(-1); - DRWPass *gather_fg_ps_ = nullptr; - DRWPass *gather_bg_ps_ = nullptr; + PassSimple gather_fg_ps_ = {"GatherFg"}; + PassSimple gather_bg_ps_ = {"GatherBg"}; /** Hole-fill convolution: Gather pass meant to fill areas of foreground dis-occlusion. */ TextureFromPool hole_fill_color_tx_ = {"dof_color_hole_fill"}; TextureFromPool hole_fill_weight_tx_ = {"dof_weight_hole_fill"}; - DRWPass *hole_fill_ps_ = nullptr; + PassSimple hole_fill_ps_ = {"HoleFill"}; /** Small Filter pass to reduce noise out of gather passes. */ int3 dispatch_filter_size_ = int3(-1); - DRWPass *filter_fg_ps_ = nullptr; - DRWPass *filter_bg_ps_ = nullptr; + PassSimple filter_fg_ps_ = {"FilterFg"}; + PassSimple filter_bg_ps_ = {"FilterBg"}; /** Scatter convolution: A quad is emitted for every 4 bright enough half pixels. */ Framebuffer scatter_fg_fb_ = {"dof_scatter_fg"}; Framebuffer scatter_bg_fb_ = {"dof_scatter_bg"}; - DRWPass *scatter_fg_ps_ = nullptr; - DRWPass *scatter_bg_ps_ = nullptr; + PassSimple scatter_fg_ps_ = {"ScatterFg"}; + PassSimple scatter_bg_ps_ = {"ScatterBg"}; /** Recombine the results and also perform a slight out of focus gather. */ GPUTexture *resolve_stable_color_tx_ = nullptr; int3 dispatch_resolve_size_ = int3(-1); - DRWPass *resolve_ps_ = nullptr; + PassSimple resolve_ps_ = {"Resolve"}; DepthOfFieldDataBuf data_; @@ -139,6 +139,8 @@ class DepthOfField { float fx_max_coc_; /** Use jittered depth of field where we randomize camera location. */ bool do_jitter_; + /** Enable bokeh lookup texture. */ + bool use_bokeh_lut_; /** Circle of Confusion radius for FX DoF passes. Is in view X direction in [0..1] range. */ float fx_radius_; @@ -166,7 +168,10 @@ class DepthOfField { * Will swap input and output texture if rendering happens. The actual output of this function * is in input_tx. */ - void render(GPUTexture **input_tx, GPUTexture **output_tx, DepthOfFieldBuffer &dof_buffer); + void render(View &view, + GPUTexture **input_tx, + GPUTexture **output_tx, + DepthOfFieldBuffer &dof_buffer); bool postfx_enabled() const { diff --git a/source/blender/draw/engines/eevee_next/eevee_film.cc b/source/blender/draw/engines/eevee_next/eevee_film.cc index ae41bd204d0..b0731ceec2f 100644 --- a/source/blender/draw/engines/eevee_next/eevee_film.cc +++ b/source/blender/draw/engines/eevee_next/eevee_film.cc @@ -377,48 +377,44 @@ void Film::sync() * Still bind previous step to avoid undefined behavior. */ eVelocityStep step_next = inst_.is_viewport() ? STEP_PREVIOUS : STEP_NEXT; - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS; - accumulate_ps_ = DRW_pass_create("Film.Accumulate", state); - GPUShader *sh = inst_.shaders.static_shader_get(shader); - DRWShadingGroup *grp = DRW_shgroup_create(sh, accumulate_ps_); - DRW_shgroup_uniform_block_ref(grp, "film_buf", &data_); - DRW_shgroup_uniform_block_ref(grp, "camera_prev", &(*velocity.camera_steps[STEP_PREVIOUS])); - DRW_shgroup_uniform_block_ref(grp, "camera_curr", &(*velocity.camera_steps[STEP_CURRENT])); - DRW_shgroup_uniform_block_ref(grp, "camera_next", &(*velocity.camera_steps[step_next])); - DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &rbuffers.depth_tx); - DRW_shgroup_uniform_texture_ref(grp, "combined_tx", &combined_final_tx_); - DRW_shgroup_uniform_texture_ref(grp, "normal_tx", &rbuffers.normal_tx); - DRW_shgroup_uniform_texture_ref(grp, "vector_tx", &rbuffers.vector_tx); - DRW_shgroup_uniform_texture_ref(grp, "light_tx", &rbuffers.light_tx); - DRW_shgroup_uniform_texture_ref(grp, "diffuse_color_tx", &rbuffers.diffuse_color_tx); - DRW_shgroup_uniform_texture_ref(grp, "specular_color_tx", &rbuffers.specular_color_tx); - DRW_shgroup_uniform_texture_ref(grp, "volume_light_tx", &rbuffers.volume_light_tx); - DRW_shgroup_uniform_texture_ref(grp, "emission_tx", &rbuffers.emission_tx); - DRW_shgroup_uniform_texture_ref(grp, "environment_tx", &rbuffers.environment_tx); - DRW_shgroup_uniform_texture_ref(grp, "shadow_tx", &rbuffers.shadow_tx); - DRW_shgroup_uniform_texture_ref(grp, "ambient_occlusion_tx", &rbuffers.ambient_occlusion_tx); - DRW_shgroup_uniform_texture_ref(grp, "aov_color_tx", &rbuffers.aov_color_tx); - DRW_shgroup_uniform_texture_ref(grp, "aov_value_tx", &rbuffers.aov_value_tx); + accumulate_ps_.init(); + accumulate_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS); + accumulate_ps_.shader_set(inst_.shaders.static_shader_get(shader)); + accumulate_ps_.bind_ubo("film_buf", &data_); + accumulate_ps_.bind_ubo("camera_prev", &(*velocity.camera_steps[STEP_PREVIOUS])); + accumulate_ps_.bind_ubo("camera_curr", &(*velocity.camera_steps[STEP_CURRENT])); + accumulate_ps_.bind_ubo("camera_next", &(*velocity.camera_steps[step_next])); + accumulate_ps_.bind_texture("depth_tx", &rbuffers.depth_tx); + accumulate_ps_.bind_texture("combined_tx", &combined_final_tx_); + accumulate_ps_.bind_texture("normal_tx", &rbuffers.normal_tx); + accumulate_ps_.bind_texture("vector_tx", &rbuffers.vector_tx); + accumulate_ps_.bind_texture("light_tx", &rbuffers.light_tx); + accumulate_ps_.bind_texture("diffuse_color_tx", &rbuffers.diffuse_color_tx); + accumulate_ps_.bind_texture("specular_color_tx", &rbuffers.specular_color_tx); + accumulate_ps_.bind_texture("volume_light_tx", &rbuffers.volume_light_tx); + accumulate_ps_.bind_texture("emission_tx", &rbuffers.emission_tx); + accumulate_ps_.bind_texture("environment_tx", &rbuffers.environment_tx); + accumulate_ps_.bind_texture("shadow_tx", &rbuffers.shadow_tx); + accumulate_ps_.bind_texture("ambient_occlusion_tx", &rbuffers.ambient_occlusion_tx); + accumulate_ps_.bind_texture("aov_color_tx", &rbuffers.aov_color_tx); + accumulate_ps_.bind_texture("aov_value_tx", &rbuffers.aov_value_tx); /* NOTE(@fclem): 16 is the max number of sampled texture in many implementations. * If we need more, we need to pack more of the similar passes in the same textures as arrays or * use image binding instead. */ - DRW_shgroup_uniform_image_ref(grp, "in_weight_img", &weight_tx_.current()); - DRW_shgroup_uniform_image_ref(grp, "out_weight_img", &weight_tx_.next()); - DRW_shgroup_uniform_texture_ref_ex(grp, "in_combined_tx", &combined_tx_.current(), filter); - DRW_shgroup_uniform_image_ref(grp, "out_combined_img", &combined_tx_.next()); - DRW_shgroup_uniform_image_ref(grp, "depth_img", &depth_tx_); - DRW_shgroup_uniform_image_ref(grp, "color_accum_img", &color_accum_tx_); - DRW_shgroup_uniform_image_ref(grp, "value_accum_img", &value_accum_tx_); + accumulate_ps_.bind_image("in_weight_img", &weight_tx_.current()); + accumulate_ps_.bind_image("out_weight_img", &weight_tx_.next()); + accumulate_ps_.bind_texture("in_combined_tx", &combined_tx_.current(), filter); + accumulate_ps_.bind_image("out_combined_img", &combined_tx_.next()); + accumulate_ps_.bind_image("depth_img", &depth_tx_); + accumulate_ps_.bind_image("color_accum_img", &color_accum_tx_); + accumulate_ps_.bind_image("value_accum_img", &value_accum_tx_); /* Sync with rendering passes. */ - DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); - /* Sync with rendering passes. */ - DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + accumulate_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS); if (use_compute) { - int2 dispatch_size = math::divide_ceil(data_.extent, int2(FILM_GROUP_SIZE)); - DRW_shgroup_call_compute(grp, UNPACK2(dispatch_size), 1); + accumulate_ps_.dispatch(int3(math::divide_ceil(data_.extent, int2(FILM_GROUP_SIZE)), 1)); } else { - DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + accumulate_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); } } @@ -565,8 +561,9 @@ void Film::accumulate(const DRWView *view, GPUTexture *combined_final_tx) data_.display_only = false; data_.push_update(); - DRW_view_set_active(view); - DRW_draw_pass(accumulate_ps_); + draw::View drw_view("MainView", view); + + DRW_manager_get()->submit(accumulate_ps_, drw_view); combined_tx_.swap(); weight_tx_.swap(); @@ -593,8 +590,9 @@ void Film::display() data_.display_only = true; data_.push_update(); - DRW_view_set_active(nullptr); - DRW_draw_pass(accumulate_ps_); + draw::View drw_view("MainView", DRW_view_default_get()); + + DRW_manager_get()->submit(accumulate_ps_, drw_view); inst_.render_buffers.release(); diff --git a/source/blender/draw/engines/eevee_next/eevee_film.hh b/source/blender/draw/engines/eevee_next/eevee_film.hh index 3e368782d31..796fcb24808 100644 --- a/source/blender/draw/engines/eevee_next/eevee_film.hh +++ b/source/blender/draw/engines/eevee_next/eevee_film.hh @@ -55,7 +55,7 @@ class Film { /** User setting to disable reprojection. Useful for debugging or have a more precise render. */ bool force_disable_reprojection_ = false; - DRWPass *accumulate_ps_ = nullptr; + PassSimple accumulate_ps_ = {"Film.Accumulate"}; FilmDataBuf data_; diff --git a/source/blender/draw/engines/eevee_next/eevee_hizbuffer.cc b/source/blender/draw/engines/eevee_next/eevee_hizbuffer.cc index e2022d74093..cf9049da514 100644 --- a/source/blender/draw/engines/eevee_next/eevee_hizbuffer.cc +++ b/source/blender/draw/engines/eevee_next/eevee_hizbuffer.cc @@ -32,36 +32,31 @@ void HiZBuffer::sync() data_.push_update(); { - hiz_update_ps_ = DRW_pass_create("HizUpdate", DRW_STATE_NO_DRAW); - GPUShader *sh = inst_.shaders.static_shader_get(HIZ_UPDATE); - DRWShadingGroup *grp = DRW_shgroup_create(sh, hiz_update_ps_); - DRW_shgroup_storage_block(grp, "finished_tile_counter", atomic_tile_counter_); - DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &render_buffers.depth_tx, with_filter); - DRW_shgroup_uniform_image(grp, "out_mip_0", hiz_tx_.mip_view(0)); - DRW_shgroup_uniform_image(grp, "out_mip_1", hiz_tx_.mip_view(1)); - DRW_shgroup_uniform_image(grp, "out_mip_2", hiz_tx_.mip_view(2)); - DRW_shgroup_uniform_image(grp, "out_mip_3", hiz_tx_.mip_view(3)); - DRW_shgroup_uniform_image(grp, "out_mip_4", hiz_tx_.mip_view(4)); - DRW_shgroup_uniform_image(grp, "out_mip_5", hiz_tx_.mip_view(5)); - DRW_shgroup_uniform_image(grp, "out_mip_6", hiz_tx_.mip_view(6)); - DRW_shgroup_uniform_image(grp, "out_mip_7", hiz_tx_.mip_view(7)); + hiz_update_ps_.init(); + hiz_update_ps_.shader_set(inst_.shaders.static_shader_get(HIZ_UPDATE)); + hiz_update_ps_.bind_ssbo("finished_tile_counter", atomic_tile_counter_); + hiz_update_ps_.bind_texture("depth_tx", &render_buffers.depth_tx, with_filter); + hiz_update_ps_.bind_image("out_mip_0", hiz_tx_.mip_view(0)); + hiz_update_ps_.bind_image("out_mip_1", hiz_tx_.mip_view(1)); + hiz_update_ps_.bind_image("out_mip_2", hiz_tx_.mip_view(2)); + hiz_update_ps_.bind_image("out_mip_3", hiz_tx_.mip_view(3)); + hiz_update_ps_.bind_image("out_mip_4", hiz_tx_.mip_view(4)); + hiz_update_ps_.bind_image("out_mip_5", hiz_tx_.mip_view(5)); + hiz_update_ps_.bind_image("out_mip_6", hiz_tx_.mip_view(6)); + hiz_update_ps_.bind_image("out_mip_7", hiz_tx_.mip_view(7)); /* TODO(@fclem): There might be occasions where we might not want to * copy mip 0 for performance reasons if there is no need for it. */ - DRW_shgroup_uniform_bool_copy(grp, "update_mip_0", true); - DRW_shgroup_call_compute(grp, UNPACK2(dispatch_size), 1); - DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + hiz_update_ps_.push_constant("update_mip_0", true); + hiz_update_ps_.dispatch(int3(dispatch_size, 1)); + hiz_update_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH); } if (inst_.debug_mode == eDebugMode::DEBUG_HIZ_VALIDATION) { - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM; - debug_draw_ps_ = DRW_pass_create("HizUpdate.Debug", state); - GPUShader *sh = inst_.shaders.static_shader_get(HIZ_DEBUG); - DRWShadingGroup *grp = DRW_shgroup_create(sh, debug_draw_ps_); - this->bind_resources(grp); - DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); - } - else { - debug_draw_ps_ = nullptr; + debug_draw_ps_.init(); + debug_draw_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM); + debug_draw_ps_.shader_set(inst_.shaders.static_shader_get(HIZ_DEBUG)); + this->bind_resources(&debug_draw_ps_); + debug_draw_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); } } @@ -79,22 +74,24 @@ void HiZBuffer::update() GPU_framebuffer_restore(); } - DRW_draw_pass(hiz_update_ps_); + inst_.manager->submit(hiz_update_ps_); if (G.debug & G_DEBUG_GPU) { GPU_framebuffer_bind(fb); } } -void HiZBuffer::debug_draw(GPUFrameBuffer *view_fb) +void HiZBuffer::debug_draw(View &view, GPUFrameBuffer *view_fb) { - if (debug_draw_ps_ == nullptr) { - return; + if (inst_.debug_mode == eDebugMode::DEBUG_HIZ_VALIDATION) { + inst_.info = + "Debug Mode: HiZ Validation\n" + " - Red: pixel in front of HiZ tile value.\n" + " - Blue: No error."; + inst_.hiz_buffer.update(); + GPU_framebuffer_bind(view_fb); + inst_.manager->submit(debug_draw_ps_, view); } - inst_.info = "Debug Mode: HiZ Validation"; - inst_.hiz_buffer.update(); - GPU_framebuffer_bind(view_fb); - DRW_draw_pass(debug_draw_ps_); } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_hizbuffer.hh b/source/blender/draw/engines/eevee_next/eevee_hizbuffer.hh index 039f7e4f16d..8b8e4de55b1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_hizbuffer.hh +++ b/source/blender/draw/engines/eevee_next/eevee_hizbuffer.hh @@ -36,9 +36,9 @@ class HiZBuffer { */ draw::StorageBuffer<uint4, true> atomic_tile_counter_ = {"atomic_tile_counter"}; /** Single pass recursive downsample. */ - DRWPass *hiz_update_ps_ = nullptr; + PassSimple hiz_update_ps_ = {"HizUpdate"}; /** Debug pass. */ - DRWPass *debug_draw_ps_ = nullptr; + PassSimple debug_draw_ps_ = {"HizUpdate.Debug"}; /** Dirty flag to check if the update is necessary. */ bool is_dirty_ = true; @@ -67,13 +67,20 @@ class HiZBuffer { */ void update(); - void debug_draw(GPUFrameBuffer *view_fb); + void debug_draw(View &view, GPUFrameBuffer *view_fb); void bind_resources(DRWShadingGroup *grp) { DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &hiz_tx_); DRW_shgroup_uniform_block_ref(grp, "hiz_buf", &data_); } + + /* TODO(fclem): Hardcoded bind slots. */ + template<typename T> void bind_resources(draw::detail::PassBase<T> *pass) + { + pass->bind_texture("hiz_tx", &hiz_tx_); + pass->bind_ubo("hiz_buf", &data_); + } }; /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index d28eb55c3b1..6150f32f150 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -52,6 +52,7 @@ void Instance::init(const int2 &output_res, drw_view = drw_view_; v3d = v3d_; rv3d = rv3d_; + manager = DRW_manager_get(); if (assign_if_different(debug_mode, (eDebugMode)G.debug_value)) { sampling.reset(); @@ -126,12 +127,16 @@ void Instance::object_sync(Object *ob) return; } + /* TODO cleanup. */ + ObjectRef ob_ref = DRW_object_ref_get(ob); + ResourceHandle res_handle = manager->resource_handle(ob_ref); + ObjectHandle &ob_handle = sync.sync_object(ob); if (partsys_is_visible && ob != DRW_context_state_get()->object_edit) { LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { if (md->type == eModifierType_ParticleSystem) { - sync.sync_curves(ob, ob_handle, md); + sync.sync_curves(ob, ob_handle, res_handle, md); } } } @@ -142,15 +147,15 @@ void Instance::object_sync(Object *ob) lights.sync_light(ob, ob_handle); break; case OB_MESH: - sync.sync_mesh(ob, ob_handle); + sync.sync_mesh(ob, ob_handle, res_handle, ob_ref); break; case OB_VOLUME: break; case OB_CURVES: - sync.sync_curves(ob, ob_handle); + sync.sync_curves(ob, ob_handle, res_handle); break; case OB_GPENCIL: - sync.sync_gpencil(ob, ob_handle); + sync.sync_gpencil(ob, ob_handle, res_handle); break; default: break; diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index cc3d1c32fde..4ab20d540bf 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -59,6 +59,7 @@ class Instance { /** Input data. */ Depsgraph *depsgraph; + Manager *manager; /** Evaluated IDs. */ Scene *scene; ViewLayer *view_layer; diff --git a/source/blender/draw/engines/eevee_next/eevee_light.cc b/source/blender/draw/engines/eevee_next/eevee_light.cc index 558a9846ced..b60246fa3ab 100644 --- a/source/blender/draw/engines/eevee_next/eevee_light.cc +++ b/source/blender/draw/engines/eevee_next/eevee_light.cc @@ -333,7 +333,7 @@ void LightModule::end_sync() /* This scene data buffer is then immutable after this point. */ light_buf_.push_update(); - for (auto key : deleted_keys) { + for (auto &key : deleted_keys) { light_map_.remove(key); } @@ -399,76 +399,70 @@ void LightModule::culling_pass_sync() uint culling_tile_dispatch_size = divide_ceil_u(total_word_count_, CULLING_TILE_GROUP_SIZE); /* NOTE: We reference the buffers that may be resized or updated later. */ + + culling_ps_.init(); { - DRW_PASS_CREATE(culling_select_ps_, DRW_STATE_NO_DRAW); - GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_SELECT); - DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_select_ps_); - DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_); - DRW_shgroup_storage_block(grp, "in_light_buf", light_buf_); - DRW_shgroup_storage_block(grp, "out_light_buf", culling_light_buf_); - DRW_shgroup_storage_block(grp, "out_zdist_buf", culling_zdist_buf_); - DRW_shgroup_storage_block(grp, "out_key_buf", culling_key_buf_); - DRW_shgroup_call_compute(grp, culling_select_dispatch_size, 1, 1); - DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE); + auto &sub = culling_ps_.sub("Select"); + sub.shader_set(inst_.shaders.static_shader_get(LIGHT_CULLING_SELECT)); + sub.bind_ssbo("light_cull_buf", &culling_data_buf_); + sub.bind_ssbo("in_light_buf", light_buf_); + sub.bind_ssbo("out_light_buf", culling_light_buf_); + sub.bind_ssbo("out_zdist_buf", culling_zdist_buf_); + sub.bind_ssbo("out_key_buf", culling_key_buf_); + sub.dispatch(int3(culling_select_dispatch_size, 1, 1)); + sub.barrier(GPU_BARRIER_SHADER_STORAGE); } { - DRW_PASS_CREATE(culling_sort_ps_, DRW_STATE_NO_DRAW); - GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_SORT); - DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_sort_ps_); - DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_); - DRW_shgroup_storage_block(grp, "in_light_buf", light_buf_); - DRW_shgroup_storage_block(grp, "out_light_buf", culling_light_buf_); - DRW_shgroup_storage_block(grp, "in_zdist_buf", culling_zdist_buf_); - DRW_shgroup_storage_block(grp, "in_key_buf", culling_key_buf_); - DRW_shgroup_call_compute(grp, culling_sort_dispatch_size, 1, 1); - DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE); + auto &sub = culling_ps_.sub("Sort"); + sub.shader_set(inst_.shaders.static_shader_get(LIGHT_CULLING_SORT)); + sub.bind_ssbo("light_cull_buf", &culling_data_buf_); + sub.bind_ssbo("in_light_buf", light_buf_); + sub.bind_ssbo("out_light_buf", culling_light_buf_); + sub.bind_ssbo("in_zdist_buf", culling_zdist_buf_); + sub.bind_ssbo("in_key_buf", culling_key_buf_); + sub.dispatch(int3(culling_sort_dispatch_size, 1, 1)); + sub.barrier(GPU_BARRIER_SHADER_STORAGE); } { - DRW_PASS_CREATE(culling_zbin_ps_, DRW_STATE_NO_DRAW); - GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_ZBIN); - DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_zbin_ps_); - DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_); - DRW_shgroup_storage_block(grp, "light_buf", culling_light_buf_); - DRW_shgroup_storage_block(grp, "out_zbin_buf", culling_zbin_buf_); - DRW_shgroup_call_compute(grp, 1, 1, 1); - DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE); + auto &sub = culling_ps_.sub("Zbin"); + sub.shader_set(inst_.shaders.static_shader_get(LIGHT_CULLING_ZBIN)); + sub.bind_ssbo("light_cull_buf", &culling_data_buf_); + sub.bind_ssbo("light_buf", culling_light_buf_); + sub.bind_ssbo("out_zbin_buf", culling_zbin_buf_); + sub.dispatch(int3(1, 1, 1)); + sub.barrier(GPU_BARRIER_SHADER_STORAGE); } { - DRW_PASS_CREATE(culling_tile_ps_, DRW_STATE_NO_DRAW); - GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_TILE); - DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_tile_ps_); - DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_); - DRW_shgroup_storage_block(grp, "light_buf", culling_light_buf_); - DRW_shgroup_storage_block(grp, "out_light_tile_buf", culling_tile_buf_); - DRW_shgroup_call_compute(grp, culling_tile_dispatch_size, 1, 1); - DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE); + auto &sub = culling_ps_.sub("Tiles"); + sub.shader_set(inst_.shaders.static_shader_get(LIGHT_CULLING_TILE)); + sub.bind_ssbo("light_cull_buf", &culling_data_buf_); + sub.bind_ssbo("light_buf", culling_light_buf_); + sub.bind_ssbo("out_light_tile_buf", culling_tile_buf_); + sub.dispatch(int3(culling_tile_dispatch_size, 1, 1)); + sub.barrier(GPU_BARRIER_SHADER_STORAGE); } } void LightModule::debug_pass_sync() { - if (inst_.debug_mode != eDebugMode::DEBUG_LIGHT_CULLING) { - debug_draw_ps_ = nullptr; - return; + if (inst_.debug_mode == eDebugMode::DEBUG_LIGHT_CULLING) { + debug_draw_ps_.init(); + debug_draw_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM); + debug_draw_ps_.shader_set(inst_.shaders.static_shader_get(LIGHT_CULLING_DEBUG)); + inst_.hiz_buffer.bind_resources(&debug_draw_ps_); + debug_draw_ps_.bind_ssbo("light_buf", &culling_light_buf_); + debug_draw_ps_.bind_ssbo("light_cull_buf", &culling_data_buf_); + debug_draw_ps_.bind_ssbo("light_zbin_buf", &culling_zbin_buf_); + debug_draw_ps_.bind_ssbo("light_tile_buf", &culling_tile_buf_); + debug_draw_ps_.bind_texture("depth_tx", &inst_.render_buffers.depth_tx); + debug_draw_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); } - - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM; - debug_draw_ps_ = DRW_pass_create("LightCulling.Debug", state); - GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_DEBUG); - DRWShadingGroup *grp = DRW_shgroup_create(sh, debug_draw_ps_); - inst_.hiz_buffer.bind_resources(grp); - DRW_shgroup_storage_block_ref(grp, "light_buf", &culling_light_buf_); - DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_); - DRW_shgroup_storage_block_ref(grp, "light_zbin_buf", &culling_zbin_buf_); - DRW_shgroup_storage_block_ref(grp, "light_tile_buf", &culling_tile_buf_); - DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &inst_.render_buffers.depth_tx); - DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); } -void LightModule::set_view(const DRWView *view, const int2 extent) +void LightModule::set_view(View &view, const int2 extent) { - float far_z = DRW_view_far_distance_get(view); - float near_z = DRW_view_near_distance_get(view); + float far_z = view.far_clip(); + float near_z = view.near_clip(); culling_data_buf_.zbin_scale = -CULLING_ZBIN_COUNT / fabsf(far_z - near_z); culling_data_buf_.zbin_bias = -near_z * culling_data_buf_.zbin_scale; @@ -476,26 +470,17 @@ void LightModule::set_view(const DRWView *view, const int2 extent) culling_data_buf_.visible_count = 0; culling_data_buf_.push_update(); - DRW_stats_group_start("Light Culling"); - - DRW_view_set_active(view); - DRW_draw_pass(culling_select_ps_); - DRW_draw_pass(culling_sort_ps_); - DRW_draw_pass(culling_zbin_ps_); - DRW_draw_pass(culling_tile_ps_); - - DRW_stats_group_end(); + inst_.manager->submit(culling_ps_, view); } -void LightModule::debug_draw(GPUFrameBuffer *view_fb) +void LightModule::debug_draw(View &view, GPUFrameBuffer *view_fb) { - if (debug_draw_ps_ == nullptr) { - return; + if (inst_.debug_mode == eDebugMode::DEBUG_LIGHT_CULLING) { + inst_.info = "Debug Mode: Light Culling Validation"; + inst_.hiz_buffer.update(); + GPU_framebuffer_bind(view_fb); + inst_.manager->submit(debug_draw_ps_, view); } - inst_.info = "Debug Mode: Light Culling Validation"; - inst_.hiz_buffer.update(); - GPU_framebuffer_bind(view_fb); - DRW_draw_pass(debug_draw_ps_); } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_light.hh b/source/blender/draw/engines/eevee_next/eevee_light.hh index aad798ccec2..9bacc180ea8 100644 --- a/source/blender/draw/engines/eevee_next/eevee_light.hh +++ b/source/blender/draw/engines/eevee_next/eevee_light.hh @@ -116,16 +116,12 @@ class LightModule { /** Bitmap of lights touching each tiles. */ LightCullingTileBuf culling_tile_buf_ = {"LightCull_tile"}; /** Culling compute passes. */ - DRWPass *culling_select_ps_ = nullptr; - DRWPass *culling_sort_ps_ = nullptr; - DRWPass *culling_zbin_ps_ = nullptr; - DRWPass *culling_tile_ps_ = nullptr; + PassSimple culling_ps_ = {"LightCulling"}; /** Total number of words the tile buffer needs to contain for the render resolution. */ uint total_word_count_ = 0; /** Debug Culling visualization. */ - DRWPass *debug_draw_ps_ = nullptr; - /* GPUTexture *input_depth_tx_ = nullptr; */ + PassSimple debug_draw_ps_ = {"LightCulling.Debug"}; public: LightModule(Instance &inst) : inst_(inst){}; @@ -138,9 +134,9 @@ class LightModule { /** * Update acceleration structure for the given view. */ - void set_view(const DRWView *view, const int2 extent); + void set_view(View &view, const int2 extent); - void debug_draw(GPUFrameBuffer *view_fb); + void debug_draw(View &view, GPUFrameBuffer *view_fb); void bind_resources(DRWShadingGroup *grp) { @@ -154,6 +150,15 @@ class LightModule { #endif } + template<typename T> void bind_resources(draw::detail::PassBase<T> *pass) + { + /* Storage Buf. */ + pass->bind_ssbo(LIGHT_CULL_BUF_SLOT, &culling_data_buf_); + pass->bind_ssbo(LIGHT_BUF_SLOT, &culling_light_buf_); + pass->bind_ssbo(LIGHT_ZBIN_BUF_SLOT, &culling_zbin_buf_); + pass->bind_ssbo(LIGHT_TILE_BUF_SLOT, &culling_tile_buf_); + } + private: void culling_pass_sync(); void debug_pass_sync(); diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc index b3161a67092..13efbd1bd1a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.cc +++ b/source/blender/draw/engines/eevee_next/eevee_material.cc @@ -145,9 +145,6 @@ MaterialModule::MaterialModule(Instance &inst) : inst_(inst) 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, error_mat_); @@ -157,13 +154,12 @@ void MaterialModule::begin_sync() { queued_shaders_count = 0; - for (Material *mat : material_map_.values()) { - mat->init = false; - } + material_map_.clear(); shader_map_.clear(); } -MaterialPass MaterialModule::material_pass_get(::Material *blender_mat, +MaterialPass MaterialModule::material_pass_get(Object *ob, + ::Material *blender_mat, eMaterialPipeline pipeline_type, eMaterialGeometry geometry_type) { @@ -203,35 +199,34 @@ MaterialPass MaterialModule::material_pass_get(::Material *blender_mat, pipeline_type = MAT_PIPE_FORWARD; } - if ((pipeline_type == MAT_PIPE_FORWARD) && + if (ELEM(pipeline_type, + MAT_PIPE_FORWARD, + MAT_PIPE_FORWARD_PREPASS, + MAT_PIPE_FORWARD_PREPASS_VELOCITY) && GPU_material_flag_get(matpass.gpumat, GPU_MATFLAG_TRANSPARENT)) { - /* Transparent needs to use one shgroup per object to support reordering. */ - matpass.shgrp = inst_.pipelines.material_add(blender_mat, matpass.gpumat, pipeline_type); + /* Transparent pass is generated later. */ + matpass.sub_pass = nullptr; } else { ShaderKey shader_key(matpass.gpumat, geometry_type, pipeline_type); - auto add_cb = [&]() -> DRWShadingGroup * { - /* First time encountering this shader. Create a shading group. */ - return inst_.pipelines.material_add(blender_mat, matpass.gpumat, pipeline_type); - }; - DRWShadingGroup *grp = shader_map_.lookup_or_add_cb(shader_key, add_cb); - - if (grp != nullptr) { - /* Shading group for this shader already exists. Create a sub one for this material. */ - /* 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. */ - matpass.shgrp = DRW_shgroup_create_sub(grp); - DRW_shgroup_add_material_resources(matpass.shgrp, matpass.gpumat); + PassMain::Sub *shader_sub = shader_map_.lookup_or_add_cb(shader_key, [&]() { + /* First time encountering this shader. Create a sub that will contain materials using it. */ + return inst_.pipelines.material_add(ob, blender_mat, matpass.gpumat, pipeline_type); + }); + + if (shader_sub != nullptr) { + /* Create a sub for this material as `shader_sub` is for sharing shader between materials. */ + matpass.sub_pass = &shader_sub->sub(GPU_material_get_name(matpass.gpumat)); + matpass.sub_pass->material_set(*inst_.manager, matpass.gpumat); } } return matpass; } -Material &MaterialModule::material_sync(::Material *blender_mat, +Material &MaterialModule::material_sync(Object *ob, + ::Material *blender_mat, eMaterialGeometry geometry_type, bool has_motion) { @@ -249,27 +244,32 @@ Material &MaterialModule::material_sync(::Material *blender_mat, MaterialKey material_key(blender_mat, geometry_type, surface_pipe); - /* 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); - - /* Forward pipeline needs to use one shgroup per object. */ - if (mat.init == false || (surface_pipe == MAT_PIPE_FORWARD)) { - mat.init = true; + Material &mat = material_map_.lookup_or_add_cb(material_key, [&]() { + Material mat; /* Order is important for transparent. */ - mat.prepass = material_pass_get(blender_mat, prepass_pipe, geometry_type); - mat.shading = material_pass_get(blender_mat, surface_pipe, geometry_type); + mat.prepass = material_pass_get(ob, blender_mat, prepass_pipe, geometry_type); + mat.shading = material_pass_get(ob, blender_mat, surface_pipe, geometry_type); if (blender_mat->blend_shadow == MA_BS_NONE) { mat.shadow = MaterialPass(); } else { - mat.shadow = material_pass_get(blender_mat, MAT_PIPE_SHADOW, geometry_type); + mat.shadow = material_pass_get(ob, blender_mat, MAT_PIPE_SHADOW, geometry_type); } - mat.is_alpha_blend_transparent = (blender_mat->blend_method == MA_BM_BLEND) && - GPU_material_flag_get(mat.prepass.gpumat, + GPU_material_flag_get(mat.shading.gpumat, GPU_MATFLAG_TRANSPARENT); + return mat; + }); + + if (mat.is_alpha_blend_transparent) { + /* Transparent needs to use one sub pass per object to support reordering. + * NOTE: Pre-pass needs to be created first in order to be sorted first. */ + mat.prepass.sub_pass = inst_.pipelines.forward.prepass_transparent_add( + ob, blender_mat, mat.shading.gpumat); + mat.shading.sub_pass = inst_.pipelines.forward.material_transparent_add( + ob, blender_mat, mat.shading.gpumat); } + return mat; } @@ -297,7 +297,7 @@ MaterialArray &MaterialModule::material_array_get(Object *ob, bool has_motion) for (auto i : IndexRange(materials_len)) { ::Material *blender_mat = material_from_slot(ob, i); - Material &mat = material_sync(blender_mat, to_material_geometry(ob), has_motion); + Material &mat = material_sync(ob, blender_mat, to_material_geometry(ob), has_motion); material_array_.materials.append(&mat); material_array_.gpu_materials.append(mat.shading.gpumat); } @@ -310,7 +310,7 @@ Material &MaterialModule::material_get(Object *ob, eMaterialGeometry geometry_type) { ::Material *blender_mat = material_from_slot(ob, mat_nr); - Material &mat = material_sync(blender_mat, geometry_type, has_motion); + Material &mat = material_sync(ob, blender_mat, geometry_type, has_motion); return mat; } diff --git a/source/blender/draw/engines/eevee_next/eevee_material.hh b/source/blender/draw/engines/eevee_next/eevee_material.hh index 23165a741b9..ad0c293926b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.hh +++ b/source/blender/draw/engines/eevee_next/eevee_material.hh @@ -203,12 +203,11 @@ class DefaultSurfaceNodeTree { * \{ */ struct MaterialPass { - GPUMaterial *gpumat = nullptr; - DRWShadingGroup *shgrp = nullptr; + GPUMaterial *gpumat; + PassMain::Sub *sub_pass; }; struct Material { - bool init = false; bool is_alpha_blend_transparent; MaterialPass shadow, shading, prepass; }; @@ -228,8 +227,8 @@ class MaterialModule { private: Instance &inst_; - Map<MaterialKey, Material *> material_map_; - Map<ShaderKey, DRWShadingGroup *> shader_map_; + Map<MaterialKey, Material> material_map_; + Map<ShaderKey, PassMain::Sub *> shader_map_; MaterialArray material_array_; @@ -254,13 +253,15 @@ class MaterialModule { Material &material_get(Object *ob, bool has_motion, int mat_nr, eMaterialGeometry geometry_type); private: - Material &material_sync(::Material *blender_mat, + Material &material_sync(Object *ob, + ::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, + MaterialPass material_pass_get(Object *ob, + ::Material *blender_mat, eMaterialPipeline pipeline_type, eMaterialGeometry geometry_type); }; diff --git a/source/blender/draw/engines/eevee_next/eevee_motion_blur.cc b/source/blender/draw/engines/eevee_next/eevee_motion_blur.cc index d9545e2e972..f68abafa3d4 100644 --- a/source/blender/draw/engines/eevee_next/eevee_motion_blur.cc +++ b/source/blender/draw/engines/eevee_next/eevee_motion_blur.cc @@ -135,53 +135,49 @@ void MotionBlurModule::sync() eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT; RenderBuffers &render_buffers = inst_.render_buffers; + motion_blur_ps_.init(); + inst_.velocity.bind_resources(&motion_blur_ps_); + inst_.sampling.bind_resources(&motion_blur_ps_); { /* Create max velocity tiles. */ - DRW_PASS_CREATE(tiles_flatten_ps_, DRW_STATE_NO_DRAW); + PassSimple::Sub &sub = motion_blur_ps_.sub("TilesFlatten"); eShaderType shader = (inst_.is_viewport()) ? MOTION_BLUR_TILE_FLATTEN_VIEWPORT : MOTION_BLUR_TILE_FLATTEN_RENDER; - GPUShader *sh = inst_.shaders.static_shader_get(shader); - DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_flatten_ps_); - inst_.velocity.bind_resources(grp); - DRW_shgroup_uniform_block(grp, "motion_blur_buf", data_); - DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &render_buffers.depth_tx); - DRW_shgroup_uniform_image_ref(grp, "velocity_img", &render_buffers.vector_tx); - DRW_shgroup_uniform_image_ref(grp, "out_tiles_img", &tiles_tx_); - - DRW_shgroup_call_compute_ref(grp, dispatch_flatten_size_); - DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_TEXTURE_FETCH); + sub.shader_set(inst_.shaders.static_shader_get(shader)); + sub.bind_ubo("motion_blur_buf", data_); + sub.bind_texture("depth_tx", &render_buffers.depth_tx); + sub.bind_image("velocity_img", &render_buffers.vector_tx); + sub.bind_image("out_tiles_img", &tiles_tx_); + sub.dispatch(&dispatch_flatten_size_); + sub.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_TEXTURE_FETCH); } { /* Expand max velocity tiles by spreading them in their neighborhood. */ - DRW_PASS_CREATE(tiles_dilate_ps_, DRW_STATE_NO_DRAW); - GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_TILE_DILATE); - DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_dilate_ps_); - DRW_shgroup_storage_block(grp, "tile_indirection_buf", tile_indirection_buf_); - DRW_shgroup_uniform_image_ref(grp, "in_tiles_img", &tiles_tx_); - - DRW_shgroup_call_compute_ref(grp, dispatch_dilate_size_); - DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE); + PassSimple::Sub &sub = motion_blur_ps_.sub("TilesDilate"); + sub.shader_set(inst_.shaders.static_shader_get(MOTION_BLUR_TILE_DILATE)); + sub.bind_ssbo("tile_indirection_buf", tile_indirection_buf_); + sub.bind_image("in_tiles_img", &tiles_tx_); + sub.dispatch(&dispatch_dilate_size_); + sub.barrier(GPU_BARRIER_SHADER_STORAGE); } { /* Do the motion blur gather algorithm. */ - DRW_PASS_CREATE(gather_ps_, DRW_STATE_NO_DRAW); - GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_GATHER); - DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_ps_); - inst_.sampling.bind_resources(grp); - DRW_shgroup_uniform_block(grp, "motion_blur_buf", data_); - DRW_shgroup_storage_block(grp, "tile_indirection_buf", tile_indirection_buf_); - DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &render_buffers.depth_tx, no_filter); - DRW_shgroup_uniform_texture_ref_ex(grp, "velocity_tx", &render_buffers.vector_tx, no_filter); - DRW_shgroup_uniform_texture_ref_ex(grp, "in_color_tx", &input_color_tx_, no_filter); - DRW_shgroup_uniform_image_ref(grp, "in_tiles_img", &tiles_tx_); - DRW_shgroup_uniform_image_ref(grp, "out_color_img", &output_color_tx_); - - DRW_shgroup_call_compute_ref(grp, dispatch_gather_size_); - DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + PassSimple::Sub &sub = motion_blur_ps_.sub("ConvolveGather"); + sub.shader_set(inst_.shaders.static_shader_get(MOTION_BLUR_GATHER)); + sub.bind_ubo("motion_blur_buf", data_); + sub.bind_ssbo("tile_indirection_buf", tile_indirection_buf_); + sub.bind_texture("depth_tx", &render_buffers.depth_tx, no_filter); + sub.bind_texture("velocity_tx", &render_buffers.vector_tx, no_filter); + sub.bind_texture("in_color_tx", &input_color_tx_, no_filter); + sub.bind_image("in_tiles_img", &tiles_tx_); + sub.bind_image("out_color_img", &output_color_tx_); + + sub.dispatch(&dispatch_gather_size_); + sub.barrier(GPU_BARRIER_TEXTURE_FETCH); } } -void MotionBlurModule::render(GPUTexture **input_tx, GPUTexture **output_tx) +void MotionBlurModule::render(View &view, GPUTexture **input_tx, GPUTexture **output_tx) { if (!motion_blur_fx_enabled_) { return; @@ -239,9 +235,7 @@ void MotionBlurModule::render(GPUTexture **input_tx, GPUTexture **output_tx) GPU_storagebuf_clear_to_zero(tile_indirection_buf_); - DRW_draw_pass(tiles_flatten_ps_); - DRW_draw_pass(tiles_dilate_ps_); - DRW_draw_pass(gather_ps_); + inst_.manager->submit(motion_blur_ps_, view); tiles_tx_.release(); diff --git a/source/blender/draw/engines/eevee_next/eevee_motion_blur.hh b/source/blender/draw/engines/eevee_next/eevee_motion_blur.hh index 310e94a702b..056c2e323d5 100644 --- a/source/blender/draw/engines/eevee_next/eevee_motion_blur.hh +++ b/source/blender/draw/engines/eevee_next/eevee_motion_blur.hh @@ -95,9 +95,7 @@ class MotionBlurModule { GPUTexture *input_color_tx_ = nullptr; GPUTexture *output_color_tx_ = nullptr; - DRWPass *tiles_flatten_ps_ = nullptr; - DRWPass *tiles_dilate_ps_ = nullptr; - DRWPass *gather_ps_ = nullptr; + PassSimple motion_blur_ps_ = {"MotionBlur"}; MotionBlurTileIndirectionBuf tile_indirection_buf_; MotionBlurDataBuf data_; @@ -121,7 +119,7 @@ class MotionBlurModule { return motion_blur_fx_enabled_; } - void render(GPUTexture **input_tx, GPUTexture **output_tx); + void render(View &view, GPUTexture **input_tx, GPUTexture **output_tx); private: float shutter_time_to_scene_time(float time); diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index d9ac39f4fb9..16bdfb04d14 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -24,36 +24,35 @@ namespace blender::eevee { void WorldPipeline::sync(GPUMaterial *gpumat) { + Manager &manager = *inst_.manager; RenderBuffers &rbufs = inst_.render_buffers; - DRWState state = DRW_STATE_WRITE_COLOR; - world_ps_ = DRW_pass_create("World", state); + ResourceHandle handle = manager.resource_handle(float4x4::identity().ptr()); - /* Push a matrix at the same location as the camera. */ - float4x4 camera_mat = float4x4::identity(); - // copy_v3_v3(camera_mat[3], inst_.camera.data_get().viewinv[3]); - - DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, world_ps_); - DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.pipelines.utility_tx); - DRW_shgroup_call_obmat(grp, DRW_cache_fullscreen_quad_get(), camera_mat.ptr()); - DRW_shgroup_uniform_float_copy(grp, "world_opacity_fade", inst_.film.background_opacity_get()); + world_ps_.init(); + world_ps_.state_set(DRW_STATE_WRITE_COLOR); + world_ps_.material_set(manager, gpumat); + world_ps_.push_constant("world_opacity_fade", inst_.film.background_opacity_get()); + world_ps_.bind_texture("utility_tx", inst_.pipelines.utility_tx); /* AOVs. */ - DRW_shgroup_uniform_image_ref(grp, "aov_color_img", &rbufs.aov_color_tx); - DRW_shgroup_uniform_image_ref(grp, "aov_value_img", &rbufs.aov_value_tx); - DRW_shgroup_storage_block_ref(grp, "aov_buf", &inst_.film.aovs_info); + world_ps_.bind_image("aov_color_img", &rbufs.aov_color_tx); + world_ps_.bind_image("aov_value_img", &rbufs.aov_value_tx); + world_ps_.bind_ssbo("aov_buf", &inst_.film.aovs_info); /* RenderPasses. Cleared by background (even if bad practice). */ - DRW_shgroup_uniform_image_ref(grp, "rp_normal_img", &rbufs.normal_tx); - DRW_shgroup_uniform_image_ref(grp, "rp_light_img", &rbufs.light_tx); - DRW_shgroup_uniform_image_ref(grp, "rp_diffuse_color_img", &rbufs.diffuse_color_tx); - DRW_shgroup_uniform_image_ref(grp, "rp_specular_color_img", &rbufs.specular_color_tx); - DRW_shgroup_uniform_image_ref(grp, "rp_emission_img", &rbufs.emission_tx); + world_ps_.bind_image("rp_normal_img", &rbufs.normal_tx); + world_ps_.bind_image("rp_light_img", &rbufs.light_tx); + world_ps_.bind_image("rp_diffuse_color_img", &rbufs.diffuse_color_tx); + world_ps_.bind_image("rp_specular_color_img", &rbufs.specular_color_tx); + world_ps_.bind_image("rp_emission_img", &rbufs.emission_tx); + + world_ps_.draw(DRW_cache_fullscreen_quad_get(), handle); /* To allow opaque pass rendering over it. */ - DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + world_ps_.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); } -void WorldPipeline::render() +void WorldPipeline::render(View &view) { - DRW_draw_pass(world_ps_); + inst_.manager->submit(world_ps_, view); } /** \} */ @@ -66,194 +65,150 @@ void WorldPipeline::render() void ForwardPipeline::sync() { + camera_forward_ = inst_.camera.forward(); + + DRWState state_depth_only = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; + DRWState state_depth_color = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | + DRW_STATE_WRITE_COLOR; { - 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); + prepass_ps_.init(); - 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); + { + /* Common resources. */ - 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; - opaque_ps_ = DRW_pass_create("Forward.Opaque", state); + /* Textures. */ + prepass_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); - state |= DRW_STATE_CULL_BACK; - opaque_culled_ps_ = DRW_pass_create("Forward.Opaque.Culled", state); + inst_.velocity.bind_resources(&prepass_ps_); + inst_.sampling.bind_resources(&prepass_ps_); + } + + prepass_double_sided_static_ps_ = &prepass_ps_.sub("DoubleSided.Static"); + prepass_double_sided_static_ps_->state_set(state_depth_only); + + prepass_single_sided_static_ps_ = &prepass_ps_.sub("SingleSided.Static"); + prepass_single_sided_static_ps_->state_set(state_depth_only | DRW_STATE_CULL_BACK); + + prepass_double_sided_moving_ps_ = &prepass_ps_.sub("DoubleSided.Moving"); + prepass_double_sided_moving_ps_->state_set(state_depth_color); - DRW_pass_link(opaque_ps_, opaque_culled_ps_); + prepass_single_sided_moving_ps_ = &prepass_ps_.sub("SingleSided.Moving"); + prepass_single_sided_moving_ps_->state_set(state_depth_color | DRW_STATE_CULL_BACK); } { - DRWState state = DRW_STATE_DEPTH_LESS_EQUAL; - transparent_ps_ = DRW_pass_create("Forward.Transparent", state); + opaque_ps_.init(); + + { + /* Common resources. */ + + /* RenderPasses. */ + opaque_ps_.bind_image(RBUFS_NORMAL_SLOT, &inst_.render_buffers.normal_tx); + opaque_ps_.bind_image(RBUFS_LIGHT_SLOT, &inst_.render_buffers.light_tx); + opaque_ps_.bind_image(RBUFS_DIFF_COLOR_SLOT, &inst_.render_buffers.diffuse_color_tx); + opaque_ps_.bind_image(RBUFS_SPEC_COLOR_SLOT, &inst_.render_buffers.specular_color_tx); + opaque_ps_.bind_image(RBUFS_EMISSION_SLOT, &inst_.render_buffers.emission_tx); + /* AOVs. */ + opaque_ps_.bind_image(RBUFS_AOV_COLOR_SLOT, &inst_.render_buffers.aov_color_tx); + opaque_ps_.bind_image(RBUFS_AOV_VALUE_SLOT, &inst_.render_buffers.aov_value_tx); + /* Storage Buf. */ + opaque_ps_.bind_ssbo(RBUFS_AOV_BUF_SLOT, &inst_.film.aovs_info); + /* Textures. */ + opaque_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + + inst_.lights.bind_resources(&opaque_ps_); + inst_.sampling.bind_resources(&opaque_ps_); + } + + opaque_single_sided_ps_ = &opaque_ps_.sub("SingleSided"); + opaque_single_sided_ps_->state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | + DRW_STATE_CULL_BACK); + + opaque_double_sided_ps_ = &opaque_ps_.sub("DoubleSided"); + opaque_double_sided_ps_->state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); } -} + { + transparent_ps_.init(); + /* Workaround limitation of PassSortable. Use dummy pass that will be sorted first in all + * circumstances. */ + PassMain::Sub &sub = transparent_ps_.sub("ResourceBind", -FLT_MAX); -DRWShadingGroup *ForwardPipeline::material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat) -{ - RenderBuffers &rbufs = inst_.render_buffers; - DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? opaque_culled_ps_ : opaque_ps_; - LightModule &lights = inst_.lights; - Sampling &sampling = inst_.sampling; - // LightProbeModule &lightprobes = inst_.lightprobes; - // RaytracingModule &raytracing = inst_.raytracing; - // eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT; - DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass); - lights.bind_resources(grp); - sampling.bind_resources(grp); - // DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get()); - // DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get()); - // DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get()); - // DRW_shgroup_uniform_block(grp, "probes_buf", lightprobes.info_ubo_get()); - // DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get()); - // DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get()); - DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.pipelines.utility_tx); - /* AOVs. */ - DRW_shgroup_uniform_image_ref(grp, "aov_color_img", &rbufs.aov_color_tx); - DRW_shgroup_uniform_image_ref(grp, "aov_value_img", &rbufs.aov_value_tx); - DRW_shgroup_storage_block_ref(grp, "aov_buf", &inst_.film.aovs_info); - /* RenderPasses. */ - DRW_shgroup_uniform_image_ref(grp, "rp_normal_img", &rbufs.normal_tx); - DRW_shgroup_uniform_image_ref(grp, "rp_light_img", &rbufs.light_tx); - DRW_shgroup_uniform_image_ref(grp, "rp_diffuse_color_img", &rbufs.diffuse_color_tx); - DRW_shgroup_uniform_image_ref(grp, "rp_specular_color_img", &rbufs.specular_color_tx); - DRW_shgroup_uniform_image_ref(grp, "rp_emission_img", &rbufs.emission_tx); - - /* TODO(fclem): Make this only needed if material uses it ... somehow. */ - // if (true) { - // DRW_shgroup_uniform_texture_ref( - // grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get()); - // } - // if (raytracing.enabled()) { - // DRW_shgroup_uniform_block(grp, "rt_diffuse_buf", raytracing.diffuse_data); - // DRW_shgroup_uniform_block(grp, "rt_reflection_buf", raytracing.reflection_data); - // DRW_shgroup_uniform_block(grp, "rt_refraction_buf", raytracing.refraction_data); - // DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_screen_radiance_tx_, - // no_interp); - // } - // if (raytracing.enabled()) { - // DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get()); - // DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", inst_.hiz_front.texture_ref_get()); - // } - return grp; -} + /* Common resources. */ -DRWShadingGroup *ForwardPipeline::prepass_opaque_add(::Material *blender_mat, - GPUMaterial *gpumat, - bool has_motion) -{ - 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); + /* Textures. */ + sub.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + + inst_.lights.bind_resources(&sub); + inst_.sampling.bind_resources(&sub); } - return grp; } -DRWShadingGroup *ForwardPipeline::material_transparent_add(::Material *blender_mat, - GPUMaterial *gpumat) +PassMain::Sub *ForwardPipeline::prepass_opaque_add(::Material *blender_mat, + GPUMaterial *gpumat, + bool has_motion) { - RenderBuffers &rbufs = inst_.render_buffers; - LightModule &lights = inst_.lights; - Sampling &sampling = inst_.sampling; - // LightProbeModule &lightprobes = inst_.lightprobes; - // RaytracingModule &raytracing = inst_.raytracing; - // eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT; - DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_); - lights.bind_resources(grp); - sampling.bind_resources(grp); - // DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get()); - // DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get()); - // DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get()); - // DRW_shgroup_uniform_block(grp, "probes_buf", lightprobes.info_ubo_get()); - // DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get()); - // DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get()); - DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.pipelines.utility_tx); - /* TODO(fclem): Make this only needed if material uses it ... somehow. */ - // if (true) { - // DRW_shgroup_uniform_texture_ref( - // grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get()); - // } - // if (raytracing.enabled()) { - // DRW_shgroup_uniform_block(grp, "rt_diffuse_buf", raytracing.diffuse_data); - // DRW_shgroup_uniform_block(grp, "rt_reflection_buf", raytracing.reflection_data); - // DRW_shgroup_uniform_block(grp, "rt_refraction_buf", raytracing.refraction_data); - // DRW_shgroup_uniform_texture_ref_ex( - // grp, "rt_radiance_tx", &input_screen_radiance_tx_, no_interp); - // } - // if (raytracing.enabled()) { - // DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get()); - // DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", inst_.hiz_front.texture_ref_get()); - // } - { - /* TODO(fclem): This is not needed. This is only to please the OpenGL debug Layer. - * If we are to introduce transparency render-passes support, it would be through a separate - * pass. */ - /* AOVs. */ - DRW_shgroup_uniform_image_ref(grp, "aov_color_img", &rbufs.aov_color_tx); - DRW_shgroup_uniform_image_ref(grp, "aov_value_img", &rbufs.aov_value_tx); - DRW_shgroup_storage_block_ref(grp, "aov_buf", &inst_.film.aovs_info); - /* RenderPasses. */ - DRW_shgroup_uniform_image_ref(grp, "rp_normal_img", &rbufs.normal_tx); - DRW_shgroup_uniform_image_ref(grp, "rp_light_img", &rbufs.light_tx); - DRW_shgroup_uniform_image_ref(grp, "rp_diffuse_color_img", &rbufs.diffuse_color_tx); - DRW_shgroup_uniform_image_ref(grp, "rp_specular_color_img", &rbufs.specular_color_tx); - DRW_shgroup_uniform_image_ref(grp, "rp_emission_img", &rbufs.emission_tx); - } + PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? + (has_motion ? prepass_single_sided_moving_ps_ : + prepass_single_sided_static_ps_) : + (has_motion ? prepass_double_sided_moving_ps_ : + prepass_double_sided_static_ps_); + return &pass->sub(GPU_material_get_name(gpumat)); +} - DRWState state_disable = DRW_STATE_WRITE_DEPTH; - DRWState state_enable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM; - if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) { - state_enable |= DRW_STATE_CULL_BACK; - } - DRW_shgroup_state_disable(grp, state_disable); - DRW_shgroup_state_enable(grp, state_enable); - return grp; +PassMain::Sub *ForwardPipeline::material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat) +{ + PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? opaque_single_sided_ps_ : + opaque_double_sided_ps_; + return &pass->sub(GPU_material_get_name(gpumat)); } -DRWShadingGroup *ForwardPipeline::prepass_transparent_add(::Material *blender_mat, - GPUMaterial *gpumat) +PassMain::Sub *ForwardPipeline::prepass_transparent_add(const Object *ob, + ::Material *blender_mat, + GPUMaterial *gpumat) { if ((blender_mat->blend_flag & MA_BL_HIDE_BACKFACE) == 0) { return nullptr; } + DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; + if ((blender_mat->blend_flag & MA_BL_CULL_BACKFACE)) { + state |= DRW_STATE_CULL_BACK; + } + float sorting_value = math::dot(float3(ob->obmat[3]), camera_forward_); + PassMain::Sub *pass = &transparent_ps_.sub(GPU_material_get_name(gpumat), sorting_value); + pass->state_set(state); + pass->material_set(*inst_.manager, gpumat); + return pass; +} - DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_); - - DRWState state_disable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM; - DRWState state_enable = DRW_STATE_WRITE_DEPTH; - if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) { - state_enable |= DRW_STATE_CULL_BACK; +PassMain::Sub *ForwardPipeline::material_transparent_add(const Object *ob, + ::Material *blender_mat, + GPUMaterial *gpumat) +{ + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM | DRW_STATE_DEPTH_LESS_EQUAL; + if ((blender_mat->blend_flag & MA_BL_CULL_BACKFACE)) { + state |= DRW_STATE_CULL_BACK; } - DRW_shgroup_state_disable(grp, state_disable); - DRW_shgroup_state_enable(grp, state_enable); - return grp; + float sorting_value = math::dot(float3(ob->obmat[3]), camera_forward_); + PassMain::Sub *pass = &transparent_ps_.sub(GPU_material_get_name(gpumat), sorting_value); + pass->state_set(state); + pass->material_set(*inst_.manager, gpumat); + return pass; } -void ForwardPipeline::render(const DRWView *view, +void ForwardPipeline::render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, GPUTexture *UNUSED(combined_tx)) { UNUSED_VARS(view); - DRW_stats_group_start("ForwardOpaque"); + DRW_stats_group_start("Forward.Opaque"); GPU_framebuffer_bind(prepass_fb); - DRW_draw_pass(prepass_ps_); + inst_.manager->submit(prepass_ps_, view); - if (!DRW_pass_is_empty(prepass_ps_)) { - inst_.hiz_buffer.set_dirty(); - } + // if (!DRW_pass_is_empty(prepass_ps_)) { + inst_.hiz_buffer.set_dirty(); + // } // if (inst_.raytracing.enabled()) { // rt_buffer.radiance_copy(combined_tx); @@ -263,17 +218,11 @@ void ForwardPipeline::render(const DRWView *view, // inst_.shadows.set_view(view, depth_tx); GPU_framebuffer_bind(combined_fb); - DRW_draw_pass(opaque_ps_); + inst_.manager->submit(opaque_ps_, view); DRW_stats_group_end(); - DRW_stats_group_start("ForwardTransparent"); - /* TODO(fclem) This is suboptimal. We could sort during sync. */ - /* FIXME(fclem) This wont work for panoramic, where we need - * to sort by distance to camera, not by z. */ - DRW_pass_sort_shgroup_z(transparent_ps_); - DRW_draw_pass(transparent_ps_); - DRW_stats_group_end(); + inst_.manager->submit(transparent_ps_, view); // if (inst_.raytracing.enabled()) { // gbuffer.ray_radiance_tx.release(); diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index ed6986b9b61..0614a963dec 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -13,6 +13,7 @@ #pragma once #include "DRW_render.h" +#include "draw_shader_shared.h" /* TODO(fclem): Move it to GPU/DRAW. */ #include "../eevee/eevee_lut.h" @@ -31,13 +32,13 @@ class WorldPipeline { private: Instance &inst_; - DRWPass *world_ps_ = nullptr; + PassSimple world_ps_ = {"World.Background"}; public: WorldPipeline(Instance &inst) : inst_(inst){}; void sync(GPUMaterial *gpumat); - void render(); + void render(View &view); }; /** \} */ @@ -52,13 +53,18 @@ class ForwardPipeline { private: 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; + PassMain prepass_ps_ = {"Prepass"}; + PassMain::Sub *prepass_single_sided_static_ps_ = nullptr; + PassMain::Sub *prepass_single_sided_moving_ps_ = nullptr; + PassMain::Sub *prepass_double_sided_static_ps_ = nullptr; + PassMain::Sub *prepass_double_sided_moving_ps_ = nullptr; + + PassMain opaque_ps_ = {"Shading"}; + PassMain::Sub *opaque_single_sided_ps_ = nullptr; + PassMain::Sub *opaque_double_sided_ps_ = nullptr; + + PassSortable transparent_ps_ = {"Forward.Transparent"}; + float3 camera_forward_; // GPUTexture *input_screen_radiance_tx_ = nullptr; @@ -67,28 +73,17 @@ class ForwardPipeline { void sync(); - DRWShadingGroup *material_add(::Material *blender_mat, GPUMaterial *gpumat) - { - return (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) ? - material_transparent_add(blender_mat, gpumat) : - material_opaque_add(blender_mat, gpumat); - } + PassMain::Sub *prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat, bool has_motion); + PassMain::Sub *material_opaque_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, has_motion); - } - - DRWShadingGroup *material_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); + PassMain::Sub *prepass_transparent_add(const Object *ob, + ::Material *blender_mat, + GPUMaterial *gpumat); + PassMain::Sub *material_transparent_add(const Object *ob, + ::Material *blender_mat, + GPUMaterial *gpumat); - void render(const DRWView *view, + void render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, GPUTexture *combined_tx); @@ -192,26 +187,36 @@ class PipelineModule { // velocity.sync(); } - DRWShadingGroup *material_add(::Material *blender_mat, - GPUMaterial *gpumat, - eMaterialPipeline pipeline_type) + PassMain::Sub *material_add(Object *ob, + ::Material *blender_mat, + GPUMaterial *gpumat, + eMaterialPipeline pipeline_type) { switch (pipeline_type) { case MAT_PIPE_DEFERRED_PREPASS: // return deferred.prepass_add(blender_mat, gpumat, false); - break; + case MAT_PIPE_FORWARD_PREPASS: + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) { + return forward.prepass_transparent_add(ob, blender_mat, gpumat); + } + return forward.prepass_opaque_add(blender_mat, gpumat, false); + 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, false); case MAT_PIPE_FORWARD_PREPASS_VELOCITY: - return forward.prepass_add(blender_mat, gpumat, true); + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) { + return forward.prepass_transparent_add(ob, blender_mat, gpumat); + } + return forward.prepass_opaque_add(blender_mat, gpumat, true); + case MAT_PIPE_DEFERRED: // return deferred.material_add(blender_mat, gpumat); - break; case MAT_PIPE_FORWARD: - return forward.material_add(blender_mat, gpumat); + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) { + return forward.material_transparent_add(ob, blender_mat, gpumat); + } + return forward.material_opaque_add(blender_mat, gpumat); + case MAT_PIPE_VOLUME: /* TODO(fclem) volume pass. */ return nullptr; diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.hh b/source/blender/draw/engines/eevee_next/eevee_sampling.hh index be87ee74886..c2bf23d20fc 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sampling.hh +++ b/source/blender/draw/engines/eevee_next/eevee_sampling.hh @@ -87,6 +87,12 @@ class Sampling { DRW_shgroup_storage_block_ref(grp, "sampling_buf", &data_); } + template<typename T> void bind_resources(draw::detail::PassBase<T> *pass) + { + /* Storage Buf. */ + pass->bind_ssbo(SAMPLING_BUF_SLOT, &data_); + } + /* Returns a pseudo random number in [0..1] range. Each dimension are de-correlated. */ float rng_get(eSamplingDimension dimension) const { diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 0e49b195ea2..7ff343d14a8 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -9,6 +9,8 @@ * and static shader usage. */ +#include "GPU_capabilities.h" + #include "gpu_shader_create_info.hh" #include "eevee_shader.hh" @@ -180,11 +182,41 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu GPUCodegenOutput &codegen = *codegen_; ShaderCreateInfo &info = *reinterpret_cast<ShaderCreateInfo *>(codegen.create_info); - info.auto_resource_location(true); + /* WORKAROUND: Replace by new ob info. */ + int64_t ob_info_index = info.additional_infos_.first_index_of_try("draw_object_infos"); + if (ob_info_index != -1) { + info.additional_infos_[ob_info_index] = "draw_object_infos_new"; + } + + /* WORKAROUND: Add new ob attr buffer. */ + if (GPU_material_uniform_attributes(gpumat) != nullptr) { + info.additional_info("draw_object_attribute_new"); + } + + /* WORKAROUND: Avoid utility texture merge error. TODO: find a cleaner fix. */ + for (auto &resource : info.batch_resources_) { + if (resource.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) { + if (resource.slot == RBUFS_UTILITY_TEX_SLOT) { + resource.slot = GPU_max_textures_frag() - 1; + } + } + } if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) { info.define("MAT_TRANSPARENT"); + /* Transparent material do not have any velocity specific pipeline. */ + if (pipeline_type == MAT_PIPE_FORWARD_PREPASS_VELOCITY) { + pipeline_type = MAT_PIPE_FORWARD_PREPASS; + } } + + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT) == false && + pipeline_type == MAT_PIPE_FORWARD) { + /* Opaque forward do support AOVs and render pass. */ + info.additional_info("eevee_aov_out"); + info.additional_info("eevee_render_pass_out"); + } + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_BARYCENTRIC)) { switch (geometry_type) { case MAT_GEOM_MESH: 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 73c090386c9..bcdb42c0093 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -12,16 +12,16 @@ # include "BLI_memory_utils.hh" # include "DRW_gpu_wrapper.hh" +# include "draw_manager.hh" +# include "draw_pass.hh" + # include "eevee_defines.hh" # include "GPU_shader_shared.h" namespace blender::eevee { -using draw::Framebuffer; -using draw::SwapChain; -using draw::Texture; -using draw::TextureFromPool; +using namespace draw; constexpr eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT; constexpr eGPUSamplerState with_filter = GPU_SAMPLER_FILTER; diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.cc b/source/blender/draw/engines/eevee_next/eevee_sync.cc index e2d4b0ac1c2..6f1725a7120 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sync.cc @@ -74,25 +74,12 @@ WorldHandle &SyncModule::sync_world(::World *world) /** \name Common * \{ */ -static inline void shgroup_geometry_call(DRWShadingGroup *grp, - Object *ob, - GPUBatch *geom, - int v_first = -1, - int v_count = -1, - bool use_instancing = false) +static inline void geometry_call(PassMain::Sub *sub_pass, + GPUBatch *geom, + ResourceHandle resource_handle) { - if (grp == nullptr) { - return; - } - - if (v_first == -1) { - DRW_shgroup_call(grp, geom, ob); - } - else if (use_instancing) { - DRW_shgroup_call_instance_range(grp, ob, geom, v_first, v_count); - } - else { - DRW_shgroup_call_range(grp, ob, geom, v_first, v_count); + if (sub_pass != nullptr) { + sub_pass->draw(geom, resource_handle); } } @@ -102,9 +89,13 @@ static inline void shgroup_geometry_call(DRWShadingGroup *grp, /** \name Mesh * \{ */ -void SyncModule::sync_mesh(Object *ob, ObjectHandle &ob_handle) +void SyncModule::sync_mesh(Object *ob, + ObjectHandle &ob_handle, + ResourceHandle res_handle, + const ObjectRef &ob_ref) { - bool has_motion = inst_.velocity.step_object_sync(ob, ob_handle.object_key, ob_handle.recalc); + bool has_motion = inst_.velocity.step_object_sync( + ob, ob_handle.object_key, res_handle, ob_handle.recalc); MaterialArray &material_array = inst_.materials.material_array_get(ob, has_motion); @@ -123,14 +114,16 @@ void SyncModule::sync_mesh(Object *ob, ObjectHandle &ob_handle) continue; } Material *material = material_array.materials[i]; - shgroup_geometry_call(material->shading.shgrp, ob, geom); - shgroup_geometry_call(material->prepass.shgrp, ob, geom); - shgroup_geometry_call(material->shadow.shgrp, ob, geom); + geometry_call(material->shading.sub_pass, geom, res_handle); + geometry_call(material->prepass.sub_pass, geom, res_handle); + geometry_call(material->shadow.sub_pass, geom, res_handle); - is_shadow_caster = is_shadow_caster || material->shadow.shgrp != nullptr; + is_shadow_caster = is_shadow_caster || material->shadow.sub_pass != nullptr; is_alpha_blend = is_alpha_blend || material->is_alpha_blend_transparent; } + inst_.manager->extract_object_attributes(res_handle, ob_ref, material_array.gpu_materials); + // shadows.sync_object(ob, ob_handle, is_shadow_caster, is_alpha_blend); } @@ -155,11 +148,13 @@ struct gpIterData { int vcount = 0; bool instancing = false; - gpIterData(Instance &inst_, Object *ob_, ObjectHandle &ob_handle) + gpIterData(Instance &inst_, Object *ob_, ObjectHandle &ob_handle, ResourceHandle resource_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))) + ob_, + inst_.velocity.step_object_sync( + ob, ob_handle.object_key, resource_handle, ob_handle.recalc))) { cfra = DEG_get_ctime(inst.depsgraph); }; @@ -167,26 +162,28 @@ struct gpIterData { static void gpencil_drawcall_flush(gpIterData &iter) { +#if 0 /* Incompatible with new darw manager. */ if (iter.geom != nullptr) { - shgroup_geometry_call(iter.material->shading.shgrp, + geometry_call(iter.material->shading.sub_pass, iter.ob, iter.geom, iter.vfirst, iter.vcount, iter.instancing); - shgroup_geometry_call(iter.material->prepass.shgrp, + geometry_call(iter.material->prepass.sub_pass, iter.ob, iter.geom, iter.vfirst, iter.vcount, iter.instancing); - shgroup_geometry_call(iter.material->shadow.shgrp, + geometry_call(iter.material->shadow.sub_pass, iter.ob, iter.geom, iter.vfirst, iter.vcount, iter.instancing); } +#endif iter.geom = nullptr; iter.vfirst = -1; iter.vcount = 0; @@ -250,21 +247,22 @@ static void gpencil_stroke_sync(bGPDlayer *UNUSED(gpl), } } -void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle) +void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandle res_handle) { /* TODO(fclem): Waiting for a user option to use the render engine instead of gpencil engine. */ if (true) { inst_.gpencil_engine_enabled = true; return; } + UNUSED_VARS(res_handle); - gpIterData iter(inst_, ob, ob_handle); + gpIterData iter(inst_, ob, ob_handle, res_handle); BKE_gpencil_visible_stroke_iter((bGPdata *)ob->data, nullptr, gpencil_stroke_sync, &iter); gpencil_drawcall_flush(iter); - // bool is_caster = true; /* TODO material.shadow.shgrp. */ + // bool is_caster = true; /* TODO material.shadow.sub_pass. */ // bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */ // shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend); } @@ -280,19 +278,24 @@ static void shgroup_curves_call(MaterialPass &matpass, ParticleSystem *part_sys = nullptr, ModifierData *modifier_data = nullptr) { - if (matpass.shgrp == nullptr) { + UNUSED_VARS(ob, modifier_data); + if (matpass.sub_pass == nullptr) { return; } if (part_sys != nullptr) { - DRW_shgroup_hair_create_sub(ob, part_sys, modifier_data, matpass.shgrp, matpass.gpumat); + // DRW_shgroup_hair_create_sub(ob, part_sys, modifier_data, matpass.sub_pass, matpass.gpumat); } else { - DRW_shgroup_curves_create_sub(ob, matpass.shgrp, matpass.gpumat); + // DRW_shgroup_curves_create_sub(ob, matpass.sub_pass, matpass.gpumat); } } -void SyncModule::sync_curves(Object *ob, ObjectHandle &ob_handle, ModifierData *modifier_data) +void SyncModule::sync_curves(Object *ob, + ObjectHandle &ob_handle, + ResourceHandle res_handle, + ModifierData *modifier_data) { + UNUSED_VARS(res_handle); int mat_nr = CURVES_MATERIAL_NR; ParticleSystem *part_sys = nullptr; @@ -320,7 +323,7 @@ void SyncModule::sync_curves(Object *ob, ObjectHandle &ob_handle, ModifierData * /* TODO(fclem) Hair velocity. */ // shading_passes.velocity.gpencil_add(ob, ob_handle); - // bool is_caster = material.shadow.shgrp != nullptr; + // bool is_caster = material.shadow.sub_pass != nullptr; // bool is_alpha_blend = material.is_alpha_blend_transparent; // shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend); } diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.hh b/source/blender/draw/engines/eevee_next/eevee_sync.hh index bd8147a2882..ab883ce44c2 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.hh +++ b/source/blender/draw/engines/eevee_next/eevee_sync.hh @@ -150,9 +150,15 @@ class SyncModule { ObjectHandle &sync_object(Object *ob); WorldHandle &sync_world(::World *world); - void sync_mesh(Object *ob, ObjectHandle &ob_handle); - void sync_gpencil(Object *ob, ObjectHandle &ob_handle); - void sync_curves(Object *ob, ObjectHandle &ob_handle, ModifierData *modifier_data = nullptr); + void sync_mesh(Object *ob, + ObjectHandle &ob_handle, + ResourceHandle res_handle, + const ObjectRef &ob_ref); + void sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandle res_handle); + void sync_curves(Object *ob, + ObjectHandle &ob_handle, + ResourceHandle res_handle, + ModifierData *modifier_data = nullptr); }; /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.cc b/source/blender/draw/engines/eevee_next/eevee_velocity.cc index 36734f0c28c..7af311a8ccc 100644 --- a/source/blender/draw/engines/eevee_next/eevee_velocity.cc +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.cc @@ -43,6 +43,10 @@ void VelocityModule::init() step_ = STEP_CURRENT; /* Let the main sync loop handle the current step. */ } + + /* For viewport, only previous motion is supported. + * Still bind previous step to avoid undefined behavior. */ + next_step_ = inst_.is_viewport() ? STEP_PREVIOUS : STEP_NEXT; } static void step_object_sync_render(void *velocity, @@ -51,7 +55,9 @@ static void step_object_sync_render(void *velocity, Depsgraph *UNUSED(depsgraph)) { ObjectKey object_key(ob); - reinterpret_cast<VelocityModule *>(velocity)->step_object_sync(ob, object_key); + /* NOTE: Dummy resource handle since this will not be used for drawing. */ + ResourceHandle resource_handle(0); + reinterpret_cast<VelocityModule *>(velocity)->step_object_sync(ob, object_key, resource_handle); } void VelocityModule::step_sync(eVelocityStep step, float time) @@ -78,6 +84,7 @@ void VelocityModule::step_camera_sync() bool VelocityModule::step_object_sync(Object *ob, ObjectKey &object_key, + ResourceHandle resource_handle, int /*IDRecalcFlag*/ recalc) { bool has_motion = object_has_velocity(ob) || (recalc & ID_RECALC_TRANSFORM); @@ -89,8 +96,6 @@ bool VelocityModule::step_object_sync(Object *ob, 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 @@ -99,7 +104,7 @@ bool VelocityModule::step_object_sync(Object *ob, * 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.obj.resource_id = resource_handle.resource_index(); vel.id = (ID *)ob->data; object_steps[step_]->get_or_resize(vel.obj.ofs[step_]) = ob->obmat; if (step_ == STEP_CURRENT) { @@ -257,7 +262,7 @@ void VelocityModule::end_sync() uint32_t max_resource_id_ = 0u; for (Map<ObjectKey, VelocityObjectData>::Item item : velocity_map.items()) { - if (item.value.obj.resource_id == (uint)-1) { + if (item.value.obj.resource_id == (uint32_t)-1) { deleted_obj.append(item.key); } else { @@ -273,11 +278,11 @@ void VelocityModule::end_sync() inst_.sampling.reset(); } - for (auto key : deleted_obj) { + for (auto &key : deleted_obj) { velocity_map.remove(key); } - indirection_buf.resize(power_of_2_max_u(max_resource_id_ + 1)); + indirection_buf.resize(ceil_to_multiple_u(max_resource_id_, 128)); /* 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. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.hh b/source/blender/draw/engines/eevee_next/eevee_velocity.hh index 01b8a5fb8c1..6f18b05d476 100644 --- a/source/blender/draw/engines/eevee_next/eevee_velocity.hh +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.hh @@ -67,7 +67,10 @@ class VelocityModule { private: Instance &inst_; + /** Step being synced. */ eVelocityStep step_ = STEP_CURRENT; + /** Step referenced as next step. */ + eVelocityStep next_step_ = STEP_NEXT; public: VelocityModule(Instance &inst) : inst_(inst) @@ -102,7 +105,10 @@ class VelocityModule { 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 &object_key, int recalc = 0); + bool step_object_sync(Object *ob, + ObjectKey &object_key, + ResourceHandle resource_handle, + int recalc = 0); /* Moves next frame data to previous frame data. Nullify next frame data. */ void step_swap(); @@ -112,6 +118,20 @@ class VelocityModule { void bind_resources(DRWShadingGroup *grp); + template<typename T> void bind_resources(draw::detail::Pass<T> *pass) + { + /* Storage Buf. */ + pass->bind_ssbo(VELOCITY_OBJ_PREV_BUF_SLOT, &(*object_steps[STEP_PREVIOUS])); + pass->bind_ssbo(VELOCITY_OBJ_NEXT_BUF_SLOT, &(*object_steps[next_step_])); + pass->bind_ssbo(VELOCITY_GEO_PREV_BUF_SLOT, &(*geometry_steps[STEP_PREVIOUS])); + pass->bind_ssbo(VELOCITY_GEO_NEXT_BUF_SLOT, &(*geometry_steps[next_step_])); + pass->bind_ssbo(VELOCITY_INDIRECTION_BUF_SLOT, &indirection_buf); + /* Uniform Buf. */ + pass->bind_ubo(VELOCITY_CAMERA_PREV_BUF, &(*camera_steps[STEP_PREVIOUS])); + pass->bind_ubo(VELOCITY_CAMERA_CURR_BUF, &(*camera_steps[STEP_CURRENT])); + pass->bind_ubo(VELOCITY_CAMERA_NEXT_BUF, &(*camera_steps[next_step_])); + } + bool camera_has_motion() const; bool camera_changed_projection() const; diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index 44067aff9ca..48951c2bae7 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -118,10 +118,10 @@ void ShadingView::render() GPU_framebuffer_bind(combined_fb_); GPU_framebuffer_clear_color_depth(combined_fb_, clear_color, 1.0f); - inst_.pipelines.world.render(); + inst_.pipelines.world.render(render_view_new_); /* TODO(fclem): Move it after the first prepass (and hiz update) once pipeline is stabilized. */ - inst_.lights.set_view(render_view_, extent_); + inst_.lights.set_view(render_view_new_, extent_); // inst_.pipelines.deferred.render( // render_view_, rt_buffer_opaque_, rt_buffer_refract_, depth_tx_, combined_tx_); @@ -130,10 +130,10 @@ void ShadingView::render() // inst_.lookdev.render_overlay(view_fb_); - inst_.pipelines.forward.render(render_view_, prepass_fb_, combined_fb_, rbufs.combined_tx); + inst_.pipelines.forward.render(render_view_new_, prepass_fb_, combined_fb_, rbufs.combined_tx); - inst_.lights.debug_draw(combined_fb_); - inst_.hiz_buffer.debug_draw(combined_fb_); + inst_.lights.debug_draw(render_view_new_, combined_fb_); + inst_.hiz_buffer.debug_draw(render_view_new_, combined_fb_); GPUTexture *combined_final_tx = render_postfx(rbufs.combined_tx); @@ -157,8 +157,8 @@ GPUTexture *ShadingView::render_postfx(GPUTexture *input_tx) GPUTexture *output_tx = postfx_tx_; /* Swapping is done internally. Actual output is set to the next input. */ - inst_.depth_of_field.render(&input_tx, &output_tx, dof_buffer_); - inst_.motion_blur.render(&input_tx, &output_tx); + inst_.depth_of_field.render(render_view_new_, &input_tx, &output_tx, dof_buffer_); + inst_.motion_blur.render(render_view_new_, &input_tx, &output_tx); return input_tx; } @@ -186,6 +186,8 @@ void ShadingView::update_view() * out of the blurring radius. To fix this, use custom enlarged culling matrix. */ inst_.depth_of_field.jitter_apply(winmat, viewmat); DRW_view_update_sub(render_view_, viewmat.ptr(), winmat.ptr()); + + render_view_new_.sync(viewmat, winmat); } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh index 65f27aba795..74e513357cd 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.hh +++ b/source/blender/draw/engines/eevee_next/eevee_view.hh @@ -57,6 +57,7 @@ class ShadingView { DRWView *sub_view_ = nullptr; /** Same as sub_view_ but has Depth Of Field jitter applied. */ DRWView *render_view_ = nullptr; + View render_view_new_; /** Render size of the view. Can change between scene sample eval. */ int2 extent_ = {-1, -1}; @@ -65,7 +66,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), render_view_new_(name){}; ~ShadingView(){}; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl index 80555367478..a6426cd06e4 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl @@ -133,9 +133,9 @@ void main() /* Issue a sprite for each field if any CoC matches. */ if (any(lessThan(do_scatter4 * sign(coc4), vec4(0.0)))) { /* Same value for all threads. Not an issue if we don't sync access to it. */ - scatter_fg_indirect_buf.v_count = 4u; + scatter_fg_indirect_buf.vertex_len = 4u; /* Issue 1 strip instance per sprite. */ - uint rect_id = atomicAdd(scatter_fg_indirect_buf.i_count, 1u); + uint rect_id = atomicAdd(scatter_fg_indirect_buf.instance_len, 1u); if (rect_id < dof_buf.scatter_max_rect) { vec4 coc4_fg = max(vec4(0.0), -coc4); @@ -166,9 +166,9 @@ void main() } if (any(greaterThan(do_scatter4 * sign(coc4), vec4(0.0)))) { /* Same value for all threads. Not an issue if we don't sync access to it. */ - scatter_bg_indirect_buf.v_count = 4u; + scatter_bg_indirect_buf.vertex_len = 4u; /* Issue 1 strip instance per sprite. */ - uint rect_id = atomicAdd(scatter_bg_indirect_buf.i_count, 1u); + uint rect_id = atomicAdd(scatter_bg_indirect_buf.instance_len, 1u); if (rect_id < dof_buf.scatter_max_rect) { vec4 coc4_bg = max(vec4(0.0), coc4); vec4 bg_weights = dof_layer_weight(coc4_bg) * dof_sample_weight(coc4_bg) * do_scatter4; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl index 13ad387289d..dd047709afd 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl @@ -409,3 +409,31 @@ vec4 attr_load_color_post(vec4 attr) #endif /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Uniform Attributes + * + * TODO(@fclem): These implementation details should concern the DRWManager and not be a fix on + * the engine side. But as of now, the engines are responsible for loading the attributes. + * + * \{ */ + +vec4 attr_load_uniform(vec4 attr, const uint attr_hash) +{ +#if defined(OBATTR_LIB) + uint index = floatBitsToUint(ObjectAttributeStart); + for (uint i = 0; i < floatBitsToUint(ObjectAttributeLen); i++, index++) { + if (drw_attrs[index].hash_code == attr_hash) { + return vec4(drw_attrs[index].data_x, + drw_attrs[index].data_y, + drw_attrs[index].data_z, + drw_attrs[index].data_w); + } + } + return vec4(0.0); +#else + return attr; +#endif +} + +/** \} */ 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 bd32215ddc2..183aac1e546 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 @@ -6,6 +6,7 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(common_math_lib.glsl) #pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) #pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) @@ -73,8 +74,7 @@ void main() nodetree_surface(); - // float noise_offset = sampling_rng_1D_get(SAMPLING_TRANSPARENCY); - float noise_offset = 0.5; + float noise_offset = sampling_rng_1D_get(SAMPLING_TRANSPARENCY); float random_threshold = hashed_alpha_threshold(1.0, noise_offset, g_data.P); float transparency = avg(g_transmittance); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl index 3f2349b30a1..39758c0dfc1 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl @@ -97,6 +97,7 @@ void main() out_normal += g_refraction_data.N * g_refraction_data.weight; out_normal = safe_normalize(out_normal); +#ifdef MAT_RENDER_PASS_SUPPORT ivec2 out_texel = ivec2(gl_FragCoord.xy); imageStore(rp_normal_img, out_texel, vec4(out_normal, 1.0)); imageStore( @@ -106,6 +107,7 @@ void main() imageStore(rp_diffuse_color_img, out_texel, vec4(g_diffuse_data.color, 1.0)); imageStore(rp_specular_color_img, out_texel, vec4(specular_color, 1.0)); imageStore(rp_emission_img, out_texel, vec4(g_emission, 1.0)); +#endif out_radiance.rgb *= 1.0 - g_holdout; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl index 30b48edaa78..18e748596d5 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl @@ -40,7 +40,7 @@ void init_globals_curves() /* Shade as a cylinder. */ float cos_theta = interp.curves_time_width / interp.curves_thickness; float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); - g_data.N = normalize(interp.N * sin_theta + interp.curves_binormal * cos_theta); + g_data.N = g_data.Ni = normalize(interp.N * sin_theta + interp.curves_binormal * cos_theta); /* Costly, but follows cycles per pixel tangent space (not following curve shape). */ vec3 V = cameraVec(g_data.P); @@ -67,6 +67,7 @@ void init_globals() { /* Default values. */ g_data.P = interp.P; + g_data.Ni = interp.N; g_data.N = safe_normalize(interp.N); g_data.Ng = g_data.N; g_data.is_strand = false; diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh index b398a6cc4e7..b689a7f53a2 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh @@ -11,7 +11,7 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_bokeh_lut) .do_static_compilation(true) .local_group_size(DOF_BOKEH_LUT_SIZE, DOF_BOKEH_LUT_SIZE) .additional_info("eevee_shared", "draw_view") - .uniform_buf(1, "DepthOfFieldData", "dof_buf") + .uniform_buf(6, "DepthOfFieldData", "dof_buf") .image(0, GPU_RG16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_gather_lut_img") .image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_scatter_lut_img") .image(2, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_resolve_lut_img") @@ -21,7 +21,7 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_setup) .do_static_compilation(true) .local_group_size(DOF_DEFAULT_GROUP_SIZE, DOF_DEFAULT_GROUP_SIZE) .additional_info("eevee_shared", "draw_view") - .uniform_buf(1, "DepthOfFieldData", "dof_buf") + .uniform_buf(6, "DepthOfFieldData", "dof_buf") .sampler(0, ImageType::FLOAT_2D, "color_tx") .sampler(1, ImageType::DEPTH_2D, "depth_tx") .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img") @@ -32,7 +32,7 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_stabilize) .do_static_compilation(true) .local_group_size(DOF_STABILIZE_GROUP_SIZE, DOF_STABILIZE_GROUP_SIZE) .additional_info("eevee_shared", "draw_view", "eevee_velocity_camera") - .uniform_buf(4, "DepthOfFieldData", "dof_buf") + .uniform_buf(6, "DepthOfFieldData", "dof_buf") .sampler(0, ImageType::FLOAT_2D, "coc_tx") .sampler(1, ImageType::FLOAT_2D, "color_tx") .sampler(2, ImageType::FLOAT_2D, "velocity_tx") @@ -57,7 +57,7 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_reduce) .do_static_compilation(true) .local_group_size(DOF_REDUCE_GROUP_SIZE, DOF_REDUCE_GROUP_SIZE) .additional_info("eevee_shared", "draw_view") - .uniform_buf(1, "DepthOfFieldData", "dof_buf") + .uniform_buf(6, "DepthOfFieldData", "dof_buf") .sampler(0, ImageType::FLOAT_2D, "downsample_tx") .storage_buf(0, Qualifier::WRITE, "ScatterRect", "scatter_fg_list_buf[]") .storage_buf(1, Qualifier::WRITE, "ScatterRect", "scatter_bg_list_buf[]") @@ -154,7 +154,7 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_gather_common) "draw_view", "eevee_depth_of_field_tiles_common", "eevee_sampling_data") - .uniform_buf(2, "DepthOfFieldData", "dof_buf") + .uniform_buf(6, "DepthOfFieldData", "dof_buf") .local_group_size(DOF_GATHER_GROUP_SIZE, DOF_GATHER_GROUP_SIZE) .sampler(0, ImageType::FLOAT_2D, "color_tx") .sampler(1, ImageType::FLOAT_2D, "color_bilinear_tx") @@ -229,7 +229,7 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_resolve) "draw_view", "eevee_depth_of_field_tiles_common", "eevee_sampling_data") - .uniform_buf(2, "DepthOfFieldData", "dof_buf") + .uniform_buf(6, "DepthOfFieldData", "dof_buf") .sampler(0, ImageType::DEPTH_2D, "depth_tx") .sampler(1, ImageType::FLOAT_2D, "color_tx") .sampler(2, ImageType::FLOAT_2D, "color_bg_tx") diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh index c94171db6a9..db82a3265d7 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh @@ -4,7 +4,7 @@ #include "gpu_shader_create_info.hh" GPU_SHADER_CREATE_INFO(eevee_film) - .uniform_buf(4, "FilmData", "film_buf") + .uniform_buf(6, "FilmData", "film_buf") .sampler(0, ImageType::DEPTH_2D, "depth_tx") .sampler(1, ImageType::FLOAT_2D, "combined_tx") .sampler(2, ImageType::FLOAT_2D, "normal_tx") diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh index c54f05719d3..41602426a1d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh @@ -8,10 +8,10 @@ * \{ */ GPU_SHADER_CREATE_INFO(eevee_light_data) - .storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf") - .storage_buf(1, Qualifier::READ, "LightData", "light_buf[]") - .storage_buf(2, Qualifier::READ, "uint", "light_zbin_buf[]") - .storage_buf(3, Qualifier::READ, "uint", "light_tile_buf[]"); + .storage_buf(LIGHT_CULL_BUF_SLOT, Qualifier::READ, "LightCullingData", "light_cull_buf") + .storage_buf(LIGHT_BUF_SLOT, Qualifier::READ, "LightData", "light_buf[]") + .storage_buf(LIGHT_ZBIN_BUF_SLOT, Qualifier::READ, "uint", "light_zbin_buf[]") + .storage_buf(LIGHT_TILE_BUF_SLOT, Qualifier::READ, "uint", "light_tile_buf[]"); /** \} */ 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 dad1f28ef8e..9abdd1f8adf 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 @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "eevee_defines.hh" #include "gpu_shader_create_info.hh" /* -------------------------------------------------------------------- */ @@ -14,9 +15,10 @@ GPU_SHADER_CREATE_INFO(eevee_shared) GPU_SHADER_CREATE_INFO(eevee_sampling_data) .define("EEVEE_SAMPLING_DATA") .additional_info("eevee_shared") - .storage_buf(14, Qualifier::READ, "SamplingData", "sampling_buf"); + .storage_buf(6, Qualifier::READ, "SamplingData", "sampling_buf"); -GPU_SHADER_CREATE_INFO(eevee_utility_texture).sampler(8, ImageType::FLOAT_2D_ARRAY, "utility_tx"); +GPU_SHADER_CREATE_INFO(eevee_utility_texture) + .sampler(RBUFS_UTILITY_TEX_SLOT, ImageType::FLOAT_2D_ARRAY, "utility_tx"); /** \} */ @@ -30,7 +32,7 @@ GPU_SHADER_CREATE_INFO(eevee_geom_mesh) .vertex_in(0, Type::VEC3, "pos") .vertex_in(1, Type::VEC3, "nor") .vertex_source("eevee_geom_mesh_vert.glsl") - .additional_info("draw_mesh", "draw_resource_id_varying", "draw_resource_handle"); + .additional_info("draw_modelmat_new", "draw_resource_id_varying", "draw_view"); GPU_SHADER_CREATE_INFO(eevee_geom_gpencil) .additional_info("eevee_shared") @@ -52,7 +54,7 @@ GPU_SHADER_CREATE_INFO(eevee_geom_world) .define("MAT_GEOM_WORLD") .builtins(BuiltinBits::VERTEX_ID) .vertex_source("eevee_geom_world_vert.glsl") - .additional_info("draw_modelmat", "draw_resource_id_varying", "draw_resource_handle"); + .additional_info("draw_modelmat_new", "draw_resource_id_varying", "draw_view"); /** \} */ @@ -78,9 +80,17 @@ GPU_SHADER_INTERFACE_INFO(eevee_surf_iface, "interp") GPU_SHADER_CREATE_INFO(eevee_aov_out) .define("MAT_AOV_SUPPORT") - .image_array_out(5, Qualifier::WRITE, GPU_RGBA16F, "aov_color_img") - .image_array_out(6, Qualifier::WRITE, GPU_R16F, "aov_value_img") - .storage_buf(7, Qualifier::READ, "AOVsInfoData", "aov_buf"); + .image_array_out(RBUFS_AOV_COLOR_SLOT, Qualifier::WRITE, GPU_RGBA16F, "aov_color_img") + .image_array_out(RBUFS_AOV_VALUE_SLOT, Qualifier::WRITE, GPU_R16F, "aov_value_img") + .storage_buf(RBUFS_AOV_BUF_SLOT, Qualifier::READ, "AOVsInfoData", "aov_buf"); + +GPU_SHADER_CREATE_INFO(eevee_render_pass_out) + .define("MAT_RENDER_PASS_SUPPORT") + .image_out(RBUFS_NORMAL_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_normal_img") + .image_array_out(RBUFS_LIGHT_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_light_img") + .image_out(RBUFS_DIFF_COLOR_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_diffuse_color_img") + .image_out(RBUFS_SPEC_COLOR_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_color_img") + .image_out(RBUFS_EMISSION_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_emission_img"); GPU_SHADER_CREATE_INFO(eevee_surf_deferred) .vertex_out(eevee_surf_iface) @@ -104,7 +114,6 @@ GPU_SHADER_CREATE_INFO(eevee_surf_deferred) ; GPU_SHADER_CREATE_INFO(eevee_surf_forward) - .auto_resource_location(true) .vertex_out(eevee_surf_iface) /* Early fragment test is needed for render passes support for forward surfaces. */ /* NOTE: This removes the possibility of using gl_FragDepth. */ @@ -112,41 +121,27 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward) .fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0) .fragment_out(0, Type::VEC4, "out_transmittance", DualBlend::SRC_1) .fragment_source("eevee_surf_forward_frag.glsl") - .image_out(0, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_normal_img") - .image_array_out(1, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_light_img") - .image_out(2, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_diffuse_color_img") - .image_out(3, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_color_img") - .image_out(4, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_emission_img") - .additional_info("eevee_aov_out", - "eevee_light_data", - "eevee_utility_texture", - "eevee_sampling_data" - // "eevee_lightprobe_data", + .additional_info("eevee_light_data", "eevee_utility_texture", "eevee_sampling_data" + // "eevee_lightprobe_data", + // "eevee_shadow_data" /* Optionally added depending on the material. */ // "eevee_raytrace_data", // "eevee_transmittance_data", - // "eevee_shadow_data" + // "eevee_aov_out", + // "eevee_render_pass_out", ); GPU_SHADER_CREATE_INFO(eevee_surf_depth) .vertex_out(eevee_surf_iface) .fragment_source("eevee_surf_depth_frag.glsl") - // .additional_info("eevee_sampling_data", "eevee_utility_texture") - ; + .additional_info("eevee_sampling_data", "eevee_utility_texture"); GPU_SHADER_CREATE_INFO(eevee_surf_world) .vertex_out(eevee_surf_iface) - .image_out(0, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_normal_img") - .image_array_out(1, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_light_img") - .image_out(2, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_diffuse_color_img") - .image_out(3, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_color_img") - .image_out(4, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_emission_img") .push_constant(Type::FLOAT, "world_opacity_fade") .fragment_out(0, Type::VEC4, "out_background") .fragment_source("eevee_surf_world_frag.glsl") - .additional_info("eevee_aov_out" - //"eevee_utility_texture" - ); + .additional_info("eevee_aov_out", "eevee_render_pass_out", "eevee_utility_texture"); #undef image_out #undef image_array_out @@ -188,10 +183,7 @@ GPU_SHADER_CREATE_INFO(eevee_volume_deferred) GPU_SHADER_CREATE_INFO(eevee_material_stub).define("EEVEE_MATERIAL_STUBS"); # define EEVEE_MAT_FINAL_VARIATION(name, ...) \ - GPU_SHADER_CREATE_INFO(name) \ - .additional_info(__VA_ARGS__) \ - .auto_resource_location(true) \ - .do_static_compilation(true); + GPU_SHADER_CREATE_INFO(name).additional_info(__VA_ARGS__).do_static_compilation(true); # define EEVEE_MAT_GEOM_VARIATIONS(prefix, ...) \ EEVEE_MAT_FINAL_VARIATION(prefix##_world, "eevee_geom_world", __VA_ARGS__) \ diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh index d6ff34b0ed2..ec302ec6770 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh @@ -6,7 +6,7 @@ GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten) .local_group_size(MOTION_BLUR_GROUP_SIZE, MOTION_BLUR_GROUP_SIZE) .additional_info("eevee_shared", "draw_view", "eevee_velocity_camera") - .uniform_buf(4, "MotionBlurData", "motion_blur_buf") + .uniform_buf(6, "MotionBlurData", "motion_blur_buf") .sampler(0, ImageType::DEPTH_2D, "depth_tx") .image(1, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_img") .compute_source("eevee_motion_blur_flatten_comp.glsl"); @@ -35,7 +35,7 @@ GPU_SHADER_CREATE_INFO(eevee_motion_blur_gather) .do_static_compilation(true) .local_group_size(MOTION_BLUR_GROUP_SIZE, MOTION_BLUR_GROUP_SIZE) .additional_info("eevee_shared", "draw_view", "eevee_sampling_data") - .uniform_buf(4, "MotionBlurData", "motion_blur_buf") + .uniform_buf(6, "MotionBlurData", "motion_blur_buf") .sampler(0, ImageType::DEPTH_2D, "depth_tx") .sampler(1, ImageType::FLOAT_2D, "velocity_tx") .sampler(2, ImageType::FLOAT_2D, "in_color_tx") 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 index 6e8e8fb020a..0a1c2721c61 100644 --- 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 @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "eevee_defines.hh" #include "gpu_shader_create_info.hh" /* -------------------------------------------------------------------- */ @@ -17,19 +18,20 @@ GPU_SHADER_INTERFACE_INFO(eevee_velocity_surface_iface, "motion") 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"); + .uniform_buf(VELOCITY_CAMERA_PREV_BUF, "CameraData", "camera_prev") + .uniform_buf(VELOCITY_CAMERA_CURR_BUF, "CameraData", "camera_curr") + .uniform_buf(VELOCITY_CAMERA_NEXT_BUF, "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) + .storage_buf(VELOCITY_OBJ_PREV_BUF_SLOT, Qualifier::READ, "mat4", "velocity_obj_prev_buf[]") + .storage_buf(VELOCITY_OBJ_NEXT_BUF_SLOT, Qualifier::READ, "mat4", "velocity_obj_next_buf[]") + .storage_buf(VELOCITY_GEO_PREV_BUF_SLOT, Qualifier::READ, "vec4", "velocity_geo_prev_buf[]") + .storage_buf(VELOCITY_GEO_NEXT_BUF_SLOT, Qualifier::READ, "vec4", "velocity_geo_next_buf[]") + .storage_buf(VELOCITY_INDIRECTION_BUF_SLOT, + Qualifier::READ, + "VelocityIndex", + "velocity_indirection_buf[]") .vertex_out(eevee_velocity_surface_iface) .fragment_out(0, Type::VEC4, "out_velocity") .additional_info("eevee_velocity_camera"); diff --git a/source/blender/draw/engines/overlay/overlay_edit_text.c b/source/blender/draw/engines/overlay/overlay_edit_text.c index dfef5b3c241..bd8720042f1 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_text.c +++ b/source/blender/draw/engines/overlay/overlay_edit_text.c @@ -7,6 +7,8 @@ #include "DRW_render.h" +#include "UI_resources.h" + #include "BKE_vfont.h" #include "DNA_curve_types.h" @@ -38,17 +40,24 @@ void OVERLAY_edit_text_cache_init(OVERLAY_Data *vedata) DRW_shgroup_uniform_vec4_copy(grp, "color", G_draw.block.color_wire); } { + /* Cursor (text caret). */ state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA; - DRW_PASS_CREATE(psl->edit_text_overlay_ps, state | pd->clipping_state); - + DRW_PASS_CREATE(psl->edit_text_cursor_ps, state | pd->clipping_state); sh = OVERLAY_shader_uniform_color(); - pd->edit_text_overlay_grp = grp = DRW_shgroup_create(sh, psl->edit_text_overlay_ps); + pd->edit_text_cursor_grp = grp = DRW_shgroup_create(sh, psl->edit_text_cursor_ps); + DRW_shgroup_uniform_vec4(grp, "color", pd->edit_text.cursor_color, 1); - DRW_shgroup_uniform_vec4(grp, "color", pd->edit_text.overlay_color, 1); + /* Selection boxes. */ + state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->edit_text_selection_ps, state | pd->clipping_state); + sh = OVERLAY_shader_uniform_color(); + pd->edit_text_selection_grp = grp = DRW_shgroup_create(sh, psl->edit_text_selection_ps); + DRW_shgroup_uniform_vec4(grp, "color", pd->edit_text.selection_color, 1); - state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_MUL | DRW_STATE_DEPTH_GREATER_EQUAL | + /* Highlight text within selection boxes. */ + state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA | DRW_STATE_DEPTH_GREATER_EQUAL | pd->clipping_state; - DRW_PASS_INSTANCE_CREATE(psl->edit_text_darken_ps, psl->edit_text_overlay_ps, state); + DRW_PASS_INSTANCE_CREATE(psl->edit_text_highlight_ps, psl->edit_text_selection_ps, state); } { /* Create view which will render everything (hopefully) behind the text geometry. */ @@ -112,7 +121,7 @@ static void edit_text_cache_populate_select(OVERLAY_Data *vedata, Object *ob) v2_quad_corners_to_mat4(box, final_mat); mul_m4_m4m4(final_mat, ob->obmat, final_mat); - DRW_shgroup_call_obmat(pd->edit_text_overlay_grp, geom, final_mat); + DRW_shgroup_call_obmat(pd->edit_text_selection_grp, geom, final_mat); } } @@ -128,7 +137,7 @@ static void edit_text_cache_populate_cursor(OVERLAY_Data *vedata, Object *ob) mul_m4_m4m4(mat, ob->obmat, mat); struct GPUBatch *geom = DRW_cache_quad_get(); - DRW_shgroup_call_obmat(pd->edit_text_overlay_grp, geom, mat); + DRW_shgroup_call_obmat(pd->edit_text_cursor_grp, geom, mat); } static void edit_text_cache_populate_boxes(OVERLAY_Data *vedata, Object *ob) @@ -193,11 +202,18 @@ void OVERLAY_edit_text_draw(OVERLAY_Data *vedata) DRW_view_set_active(pd->view_edit_text); - /* Alpha blended. */ - copy_v4_fl4(pd->edit_text.overlay_color, 0.8f, 0.8f, 0.8f, 0.5f); - DRW_draw_pass(psl->edit_text_overlay_ps); + /* Selection Boxes. */ + UI_GetThemeColor4fv(TH_WIDGET_TEXT_SELECTION, pd->edit_text.selection_color); + srgb_to_linearrgb_v4(pd->edit_text.selection_color, pd->edit_text.selection_color); + DRW_draw_pass(psl->edit_text_selection_ps); + + /* Highlight text within selection boxes. */ + UI_GetThemeColor4fv(TH_WIDGET_TEXT_HIGHLIGHT, pd->edit_text.selection_color); + srgb_to_linearrgb_v4(pd->edit_text.selection_color, pd->edit_text.selection_color); + DRW_draw_pass(psl->edit_text_highlight_ps); - /* Multiply previous result where depth test fail. */ - copy_v4_fl4(pd->edit_text.overlay_color, 0.0f, 0.0f, 0.0f, 1.0f); - DRW_draw_pass(psl->edit_text_darken_ps); + /* Cursor (text caret). */ + UI_GetThemeColor4fv(TH_WIDGET_TEXT_CURSOR, pd->edit_text.cursor_color); + srgb_to_linearrgb_v4(pd->edit_text.cursor_color, pd->edit_text.cursor_color); + DRW_draw_pass(psl->edit_text_cursor_ps); } diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index 06c6ce42fd8..0a783c44029 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -78,8 +78,9 @@ typedef struct OVERLAY_PassList { DRWPass *edit_mesh_analysis_ps; DRWPass *edit_mesh_normals_ps; DRWPass *edit_particle_ps; - DRWPass *edit_text_overlay_ps; - DRWPass *edit_text_darken_ps; + DRWPass *edit_text_cursor_ps; + DRWPass *edit_text_selection_ps; + DRWPass *edit_text_highlight_ps; DRWPass *edit_text_wire_ps[2]; DRWPass *edit_uv_edges_ps; DRWPass *edit_uv_verts_ps; @@ -252,7 +253,8 @@ typedef struct OVERLAY_PrivateData { DRWShadingGroup *edit_mesh_analysis_grp; DRWShadingGroup *edit_particle_strand_grp; DRWShadingGroup *edit_particle_point_grp; - DRWShadingGroup *edit_text_overlay_grp; + DRWShadingGroup *edit_text_cursor_grp; + DRWShadingGroup *edit_text_selection_grp; DRWShadingGroup *edit_text_wire_grp[2]; DRWShadingGroup *edit_uv_verts_grp; DRWShadingGroup *edit_uv_edges_grp; @@ -338,7 +340,8 @@ typedef struct OVERLAY_PrivateData { int handle_display; } edit_curve; struct { - float overlay_color[4]; + float cursor_color[4]; + float selection_color[4]; } edit_text; struct { bool do_zbufclip; diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index 8ed6594c31e..d9122657144 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -238,6 +238,11 @@ class StorageCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable GPU_storagebuf_clear_to_zero(ssbo_); } + void read() + { + GPU_storagebuf_read(ssbo_, this->data_); + } + operator GPUStorageBuf *() const { return ssbo_; @@ -850,6 +855,32 @@ class TextureFromPool : public Texture, NonMovable { GPUTexture *stencil_view() = delete; }; +/** + * Dummy type to bind texture as image. + * It is just a GPUTexture in disguise. + */ +class Image {}; + +static inline Image *as_image(GPUTexture *tex) +{ + return reinterpret_cast<Image *>(tex); +} + +static inline Image **as_image(GPUTexture **tex) +{ + return reinterpret_cast<Image **>(tex); +} + +static inline GPUTexture *as_texture(Image *img) +{ + return reinterpret_cast<GPUTexture *>(img); +} + +static inline GPUTexture **as_texture(Image **img) +{ + return reinterpret_cast<GPUTexture **>(img); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 30c1144739e..7b80ffd2b88 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -41,6 +41,7 @@ #include "draw_debug.h" #include "draw_manager_profiling.h" +#include "draw_state.h" #include "draw_view_data.h" #include "MEM_guardedalloc.h" @@ -288,83 +289,6 @@ void DRW_shader_library_free(DRWShaderLibrary *lib); /* Batches */ -/** - * DRWState is a bit-mask that stores the current render state and the desired render state. Based - * on the differences the minimum state changes can be invoked to setup the desired render state. - * - * The Write Stencil, Stencil test, Depth test and Blend state options are mutual exclusive - * 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), - /* Write Stencil. These options are mutual exclusive and packed into 2 bits */ - DRW_STATE_WRITE_STENCIL = (1 << 2), - DRW_STATE_WRITE_STENCIL_SHADOW_PASS = (2 << 2), - DRW_STATE_WRITE_STENCIL_SHADOW_FAIL = (3 << 2), - /** Depth test. These options are mutual exclusive and packed into 3 bits */ - DRW_STATE_DEPTH_ALWAYS = (1 << 4), - DRW_STATE_DEPTH_LESS = (2 << 4), - DRW_STATE_DEPTH_LESS_EQUAL = (3 << 4), - DRW_STATE_DEPTH_EQUAL = (4 << 4), - DRW_STATE_DEPTH_GREATER = (5 << 4), - DRW_STATE_DEPTH_GREATER_EQUAL = (6 << 4), - /** Culling test */ - DRW_STATE_CULL_BACK = (1 << 7), - DRW_STATE_CULL_FRONT = (1 << 8), - /** Stencil test. These options are mutually exclusive and packed into 2 bits. */ - DRW_STATE_STENCIL_ALWAYS = (1 << 9), - DRW_STATE_STENCIL_EQUAL = (2 << 9), - DRW_STATE_STENCIL_NEQUAL = (3 << 9), - - /** Blend state. These options are mutual exclusive and packed into 4 bits */ - DRW_STATE_BLEND_ADD = (1 << 11), - /** Same as additive but let alpha accumulate without pre-multiply. */ - DRW_STATE_BLEND_ADD_FULL = (2 << 11), - /** Standard alpha blending. */ - DRW_STATE_BLEND_ALPHA = (3 << 11), - /** Use that if color is already pre-multiply by alpha. */ - DRW_STATE_BLEND_ALPHA_PREMUL = (4 << 11), - DRW_STATE_BLEND_BACKGROUND = (5 << 11), - DRW_STATE_BLEND_OIT = (6 << 11), - DRW_STATE_BLEND_MUL = (7 << 11), - DRW_STATE_BLEND_SUB = (8 << 11), - /** Use dual source blending. WARNING: Only one color buffer allowed. */ - DRW_STATE_BLEND_CUSTOM = (9 << 11), - DRW_STATE_LOGIC_INVERT = (10 << 11), - DRW_STATE_BLEND_ALPHA_UNDER_PREMUL = (11 << 11), - - DRW_STATE_IN_FRONT_SELECT = (1 << 27), - DRW_STATE_SHADOW_OFFSET = (1 << 28), - DRW_STATE_CLIP_PLANES = (1 << 29), - DRW_STATE_FIRST_VERTEX_CONVENTION = (1 << 30), - /** DO NOT USE. Assumed always enabled. Only used internally. */ - DRW_STATE_PROGRAM_POINT_SIZE = (1u << 31), -} DRWState; - -ENUM_OPERATORS(DRWState, DRW_STATE_PROGRAM_POINT_SIZE); - -#define DRW_STATE_DEFAULT \ - (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL) -#define DRW_STATE_BLEND_ENABLED \ - (DRW_STATE_BLEND_ADD | DRW_STATE_BLEND_ADD_FULL | DRW_STATE_BLEND_ALPHA | \ - DRW_STATE_BLEND_ALPHA_PREMUL | DRW_STATE_BLEND_BACKGROUND | DRW_STATE_BLEND_OIT | \ - DRW_STATE_BLEND_MUL | DRW_STATE_BLEND_SUB | DRW_STATE_BLEND_CUSTOM | DRW_STATE_LOGIC_INVERT) -#define DRW_STATE_RASTERIZER_ENABLED \ - (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_STENCIL | \ - DRW_STATE_WRITE_STENCIL_SHADOW_PASS | DRW_STATE_WRITE_STENCIL_SHADOW_FAIL) -#define DRW_STATE_DEPTH_TEST_ENABLED \ - (DRW_STATE_DEPTH_ALWAYS | DRW_STATE_DEPTH_LESS | DRW_STATE_DEPTH_LESS_EQUAL | \ - DRW_STATE_DEPTH_EQUAL | DRW_STATE_DEPTH_GREATER | DRW_STATE_DEPTH_GREATER_EQUAL) -#define DRW_STATE_STENCIL_TEST_ENABLED \ - (DRW_STATE_STENCIL_ALWAYS | DRW_STATE_STENCIL_EQUAL | DRW_STATE_STENCIL_NEQUAL) -#define DRW_STATE_WRITE_STENCIL_ENABLED \ - (DRW_STATE_WRITE_STENCIL | DRW_STATE_WRITE_STENCIL_SHADOW_PASS | \ - DRW_STATE_WRITE_STENCIL_SHADOW_FAIL) - typedef enum { DRW_ATTR_INT, DRW_ATTR_FLOAT, diff --git a/source/blender/draw/intern/draw_command.cc b/source/blender/draw/intern/draw_command.cc new file mode 100644 index 00000000000..7d5ea5c2048 --- /dev/null +++ b/source/blender/draw/intern/draw_command.cc @@ -0,0 +1,600 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +/** \file + * \ingroup draw + */ + +#include "GPU_batch.h" +#include "GPU_capabilities.h" +#include "GPU_compute.h" +#include "GPU_debug.h" + +#include "draw_command.hh" +#include "draw_shader.h" +#include "draw_view.hh" + +#include <bitset> +#include <sstream> + +namespace blender::draw::command { + +/* -------------------------------------------------------------------- */ +/** \name Commands Execution + * \{ */ + +void ShaderBind::execute(RecordingState &state) const +{ + if (assign_if_different(state.shader, shader)) { + GPU_shader_bind(shader); + } +} + +void ResourceBind::execute() const +{ + if (slot == -1) { + return; + } + switch (type) { + case ResourceBind::Type::Sampler: + GPU_texture_bind_ex(is_reference ? *texture_ref : texture, sampler, slot, false); + break; + case ResourceBind::Type::Image: + GPU_texture_image_bind(is_reference ? *texture_ref : texture, slot); + break; + case ResourceBind::Type::UniformBuf: + GPU_uniformbuf_bind(is_reference ? *uniform_buf_ref : uniform_buf, slot); + break; + case ResourceBind::Type::StorageBuf: + GPU_storagebuf_bind(is_reference ? *storage_buf_ref : storage_buf, slot); + break; + } +} + +void PushConstant::execute(RecordingState &state) const +{ + if (location == -1) { + return; + } + switch (type) { + case PushConstant::Type::IntValue: + GPU_shader_uniform_vector_int(state.shader, location, comp_len, array_len, int4_value); + break; + case PushConstant::Type::IntReference: + GPU_shader_uniform_vector_int(state.shader, location, comp_len, array_len, int_ref); + break; + case PushConstant::Type::FloatValue: + GPU_shader_uniform_vector(state.shader, location, comp_len, array_len, float4_value); + break; + case PushConstant::Type::FloatReference: + GPU_shader_uniform_vector(state.shader, location, comp_len, array_len, float_ref); + break; + } +} + +void Draw::execute(RecordingState &state) const +{ + state.front_facing_set(handle.has_inverted_handedness()); + + if (GPU_shader_draw_parameters_support() == false) { + GPU_batch_resource_id_buf_set(batch, state.resource_id_buf); + } + + GPU_batch_set_shader(batch, state.shader); + GPU_batch_draw_advanced(batch, vertex_first, vertex_len, 0, instance_len); +} + +void DrawMulti::execute(RecordingState &state) const +{ + DrawMultiBuf::DrawCommandBuf &indirect_buf = multi_draw_buf->command_buf_; + DrawMultiBuf::DrawGroupBuf &groups = multi_draw_buf->group_buf_; + + uint group_index = this->group_first; + while (group_index != (uint)-1) { + const DrawGroup &group = groups[group_index]; + + if (group.vertex_len > 0) { + if (GPU_shader_draw_parameters_support() == false) { + GPU_batch_resource_id_buf_set(group.gpu_batch, state.resource_id_buf); + } + + GPU_batch_set_shader(group.gpu_batch, state.shader); + + constexpr intptr_t stride = sizeof(DrawCommand); + /* We have 2 indirect command reserved per draw group. */ + intptr_t offset = stride * group_index * 2; + + /* Draw negatively scaled geometry first. */ + if (group.len - group.front_facing_len > 0) { + state.front_facing_set(true); + GPU_batch_draw_indirect(group.gpu_batch, indirect_buf, offset); + } + + if (group.front_facing_len > 0) { + state.front_facing_set(false); + GPU_batch_draw_indirect(group.gpu_batch, indirect_buf, offset + stride); + } + } + + group_index = group.next; + } +} + +void DrawIndirect::execute(RecordingState &state) const +{ + state.front_facing_set(handle.has_inverted_handedness()); + + GPU_batch_draw_indirect(batch, *indirect_buf, 0); +} + +void Dispatch::execute(RecordingState &state) const +{ + if (is_reference) { + GPU_compute_dispatch(state.shader, size_ref->x, size_ref->y, size_ref->z); + } + else { + GPU_compute_dispatch(state.shader, size.x, size.y, size.z); + } +} + +void DispatchIndirect::execute(RecordingState &state) const +{ + GPU_compute_dispatch_indirect(state.shader, *indirect_buf); +} + +void Barrier::execute() const +{ + GPU_memory_barrier(type); +} + +void Clear::execute() const +{ + GPUFrameBuffer *fb = GPU_framebuffer_active_get(); + GPU_framebuffer_clear(fb, (eGPUFrameBufferBits)clear_channels, color, depth, stencil); +} + +void StateSet::execute(RecordingState &recording_state) const +{ + /** + * Does not support locked state for the moment and never should. + * Better implement a less hacky selection! + */ + BLI_assert(DST.state_lock == 0); + + if (!assign_if_different(recording_state.pipeline_state, new_state)) { + return; + } + + /* Keep old API working. Keep the state tracking in sync. */ + /* TODO(fclem): Move at the end of a pass. */ + DST.state = new_state; + + GPU_state_set(to_write_mask(new_state), + to_blend(new_state), + to_face_cull_test(new_state), + to_depth_test(new_state), + to_stencil_test(new_state), + to_stencil_op(new_state), + to_provoking_vertex(new_state)); + + if (new_state & DRW_STATE_SHADOW_OFFSET) { + GPU_shadow_offset(true); + } + else { + GPU_shadow_offset(false); + } + + /* TODO: this should be part of shader state. */ + if (new_state & DRW_STATE_CLIP_PLANES) { + GPU_clip_distances(recording_state.view_clip_plane_count); + } + else { + GPU_clip_distances(0); + } + + if (new_state & DRW_STATE_IN_FRONT_SELECT) { + /* XXX `GPU_depth_range` is not a perfect solution + * since very distant geometries can still be occluded. + * Also the depth test precision of these geometries is impaired. + * However, it solves the selection for the vast majority of cases. */ + GPU_depth_range(0.0f, 0.01f); + } + else { + GPU_depth_range(0.0f, 1.0f); + } + + if (new_state & DRW_STATE_PROGRAM_POINT_SIZE) { + GPU_program_point_size(true); + } + else { + GPU_program_point_size(false); + } +} + +void StencilSet::execute() const +{ + GPU_stencil_write_mask_set(write_mask); + GPU_stencil_compare_mask_set(compare_mask); + GPU_stencil_reference_set(reference); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Commands Serialization for debugging + * \{ */ + +std::string ShaderBind::serialize() const +{ + return std::string(".shader_bind(") + GPU_shader_get_name(shader) + ")"; +} + +std::string ResourceBind::serialize() const +{ + switch (type) { + case Type::Sampler: + return std::string(".bind_texture") + (is_reference ? "_ref" : "") + "(" + + std::to_string(slot) + + (sampler != GPU_SAMPLER_MAX ? ", sampler=" + std::to_string(sampler) : "") + ")"; + case Type::Image: + return std::string(".bind_image") + (is_reference ? "_ref" : "") + "(" + + std::to_string(slot) + ")"; + case Type::UniformBuf: + return std::string(".bind_uniform_buf") + (is_reference ? "_ref" : "") + "(" + + std::to_string(slot) + ")"; + case Type::StorageBuf: + return std::string(".bind_storage_buf") + (is_reference ? "_ref" : "") + "(" + + std::to_string(slot) + ")"; + default: + BLI_assert_unreachable(); + return ""; + } +} + +std::string PushConstant::serialize() const +{ + std::stringstream ss; + for (int i = 0; i < array_len; i++) { + switch (comp_len) { + case 1: + switch (type) { + case Type::IntValue: + ss << int1_value; + break; + case Type::IntReference: + ss << int_ref[i]; + break; + case Type::FloatValue: + ss << float1_value; + break; + case Type::FloatReference: + ss << float_ref[i]; + break; + } + break; + case 2: + switch (type) { + case Type::IntValue: + ss << int2_value; + break; + case Type::IntReference: + ss << int2_ref[i]; + break; + case Type::FloatValue: + ss << float2_value; + break; + case Type::FloatReference: + ss << float2_ref[i]; + break; + } + break; + case 3: + switch (type) { + case Type::IntValue: + ss << int3_value; + break; + case Type::IntReference: + ss << int3_ref[i]; + break; + case Type::FloatValue: + ss << float3_value; + break; + case Type::FloatReference: + ss << float3_ref[i]; + break; + } + break; + case 4: + switch (type) { + case Type::IntValue: + ss << int4_value; + break; + case Type::IntReference: + ss << int4_ref[i]; + break; + case Type::FloatValue: + ss << float4_value; + break; + case Type::FloatReference: + ss << float4_ref[i]; + break; + } + break; + case 16: + switch (type) { + case Type::IntValue: + case Type::IntReference: + BLI_assert_unreachable(); + break; + case Type::FloatValue: + ss << *reinterpret_cast<const float4x4 *>(&float4_value); + break; + case Type::FloatReference: + ss << *float4x4_ref; + break; + } + break; + } + if (i < array_len - 1) { + ss << ", "; + } + } + + return std::string(".push_constant(") + std::to_string(location) + ", data=" + ss.str() + ")"; +} + +std::string Draw::serialize() const +{ + std::string inst_len = (instance_len == (uint)-1) ? "from_batch" : std::to_string(instance_len); + std::string vert_len = (vertex_len == (uint)-1) ? "from_batch" : std::to_string(vertex_len); + std::string vert_first = (vertex_first == (uint)-1) ? "from_batch" : + std::to_string(vertex_first); + return std::string(".draw(inst_len=") + inst_len + ", vert_len=" + vert_len + + ", vert_first=" + vert_first + ", res_id=" + std::to_string(handle.resource_index()) + + ")"; +} + +std::string DrawMulti::serialize(std::string line_prefix) const +{ + DrawMultiBuf::DrawGroupBuf &groups = multi_draw_buf->group_buf_; + + MutableSpan<DrawPrototype> prototypes(multi_draw_buf->prototype_buf_.data(), + multi_draw_buf->prototype_count_); + + /* This emulates the GPU sorting but without the unstable draw order. */ + std::sort( + prototypes.begin(), prototypes.end(), [](const DrawPrototype &a, const DrawPrototype &b) { + return (a.group_id < b.group_id) || + (a.group_id == b.group_id && a.resource_handle > b.resource_handle); + }); + + /* Compute prefix sum to have correct offsets. */ + uint prefix_sum = 0u; + for (DrawGroup &group : groups) { + group.start = prefix_sum; + prefix_sum += group.front_proto_len + group.back_proto_len; + } + + std::stringstream ss; + + uint group_len = 0; + uint group_index = this->group_first; + while (group_index != (uint)-1) { + const DrawGroup &grp = groups[group_index]; + + ss << std::endl << line_prefix << " .group(id=" << group_index << ", len=" << grp.len << ")"; + + intptr_t offset = grp.start; + + if (grp.back_proto_len > 0) { + for (DrawPrototype &proto : prototypes.slice({offset, grp.back_proto_len})) { + BLI_assert(proto.group_id == group_index); + ResourceHandle handle(proto.resource_handle); + BLI_assert(handle.has_inverted_handedness()); + ss << std::endl + << line_prefix << " .proto(instance_len=" << std::to_string(proto.instance_len) + << ", resource_id=" << std::to_string(handle.resource_index()) << ", back_face)"; + } + offset += grp.back_proto_len; + } + + if (grp.front_proto_len > 0) { + for (DrawPrototype &proto : prototypes.slice({offset, grp.front_proto_len})) { + BLI_assert(proto.group_id == group_index); + ResourceHandle handle(proto.resource_handle); + BLI_assert(!handle.has_inverted_handedness()); + ss << std::endl + << line_prefix << " .proto(instance_len=" << std::to_string(proto.instance_len) + << ", resource_id=" << std::to_string(handle.resource_index()) << ", front_face)"; + } + } + + group_index = grp.next; + group_len++; + } + + ss << std::endl; + + return line_prefix + ".draw_multi(" + std::to_string(group_len) + ")" + ss.str(); +} + +std::string DrawIndirect::serialize() const +{ + return std::string(".draw_indirect()"); +} + +std::string Dispatch::serialize() const +{ + int3 sz = is_reference ? *size_ref : size; + return std::string(".dispatch") + (is_reference ? "_ref" : "") + "(" + std::to_string(sz.x) + + ", " + std::to_string(sz.y) + ", " + std::to_string(sz.z) + ")"; +} + +std::string DispatchIndirect::serialize() const +{ + return std::string(".dispatch_indirect()"); +} + +std::string Barrier::serialize() const +{ + /* TOOD(fclem): Better serialization... */ + return std::string(".barrier(") + std::to_string(type) + ")"; +} + +std::string Clear::serialize() const +{ + std::stringstream ss; + if (eGPUFrameBufferBits(clear_channels) & GPU_COLOR_BIT) { + ss << "color=" << color; + if (eGPUFrameBufferBits(clear_channels) & (GPU_DEPTH_BIT | GPU_STENCIL_BIT)) { + ss << ", "; + } + } + if (eGPUFrameBufferBits(clear_channels) & GPU_DEPTH_BIT) { + ss << "depth=" << depth; + if (eGPUFrameBufferBits(clear_channels) & GPU_STENCIL_BIT) { + ss << ", "; + } + } + if (eGPUFrameBufferBits(clear_channels) & GPU_STENCIL_BIT) { + ss << "stencil=0b" << std::bitset<8>(stencil) << ")"; + } + return std::string(".clear(") + ss.str() + ")"; +} + +std::string StateSet::serialize() const +{ + /* TOOD(fclem): Better serialization... */ + return std::string(".state_set(") + std::to_string(new_state) + ")"; +} + +std::string StencilSet::serialize() const +{ + std::stringstream ss; + ss << ".stencil_set(write_mask=0b" << std::bitset<8>(write_mask) << ", compare_mask=0b" + << std::bitset<8>(compare_mask) << ", reference=0b" << std::bitset<8>(reference); + return ss.str(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Commands buffers binding / command / resource ID generation + * \{ */ + +void DrawCommandBuf::bind(RecordingState &state, + Vector<Header, 0> &headers, + Vector<Undetermined, 0> &commands) +{ + UNUSED_VARS(headers, commands); + + resource_id_count_ = 0; + + for (const Header &header : headers) { + if (header.type != Type::Draw) { + continue; + } + + Draw &cmd = commands[header.index].draw; + + int batch_vert_len, batch_vert_first, batch_base_index, batch_inst_len; + /* Now that GPUBatches are guaranteed to be finished, extract their parameters. */ + GPU_batch_draw_parameter_get( + cmd.batch, &batch_vert_len, &batch_vert_first, &batch_base_index, &batch_inst_len); + /* Instancing attributes are not supported using the new pipeline since we use the base + * instance to set the correct resource_id. Workaround is a storage_buf + gl_InstanceID. */ + BLI_assert(batch_inst_len == 1); + + if (cmd.vertex_len == (uint)-1) { + cmd.vertex_len = batch_vert_len; + } + + if (cmd.handle.raw > 0) { + /* Save correct offset to start of resource_id buffer region for this draw. */ + uint instance_first = resource_id_count_; + resource_id_count_ += cmd.instance_len; + /* Ensure the buffer is big enough. */ + resource_id_buf_.get_or_resize(resource_id_count_ - 1); + + /* Copy the resource id for all instances. */ + uint index = cmd.handle.resource_index(); + for (int i = instance_first; i < (instance_first + cmd.instance_len); i++) { + resource_id_buf_[i] = index; + } + } + } + + resource_id_buf_.push_update(); + + if (GPU_shader_draw_parameters_support() == false) { + state.resource_id_buf = resource_id_buf_; + } + else { + GPU_storagebuf_bind(resource_id_buf_, DRW_RESOURCE_ID_SLOT); + } +} + +void DrawMultiBuf::bind(RecordingState &state, + Vector<Header, 0> &headers, + Vector<Undetermined, 0> &commands, + VisibilityBuf &visibility_buf) +{ + UNUSED_VARS(headers, commands); + + GPU_debug_group_begin("DrawMultiBuf.bind"); + + resource_id_count_ = 0u; + for (DrawGroup &group : MutableSpan<DrawGroup>(group_buf_.data(), group_count_)) { + /* Compute prefix sum of all instance of previous group. */ + group.start = resource_id_count_; + resource_id_count_ += group.len; + + int batch_inst_len; + /* Now that GPUBatches are guaranteed to be finished, extract their parameters. */ + GPU_batch_draw_parameter_get(group.gpu_batch, + &group.vertex_len, + &group.vertex_first, + &group.base_index, + &batch_inst_len); + + /* Instancing attributes are not supported using the new pipeline since we use the base + * instance to set the correct resource_id. Workaround is a storage_buf + gl_InstanceID. */ + BLI_assert(batch_inst_len == 1); + UNUSED_VARS_NDEBUG(batch_inst_len); + + /* Now that we got the batch infos, we can set the counters to 0. */ + group.total_counter = group.front_facing_counter = group.back_facing_counter = 0; + } + + group_buf_.push_update(); + prototype_buf_.push_update(); + /* Allocate enough for the expansion pass. */ + resource_id_buf_.get_or_resize(resource_id_count_); + /* Two command per group. */ + command_buf_.get_or_resize(group_count_ * 2); + + if (prototype_count_ > 0) { + GPUShader *shader = DRW_shader_draw_command_generate_get(); + GPU_shader_bind(shader); + GPU_shader_uniform_1i(shader, "prototype_len", prototype_count_); + GPU_storagebuf_bind(group_buf_, GPU_shader_get_ssbo(shader, "group_buf")); + GPU_storagebuf_bind(visibility_buf, GPU_shader_get_ssbo(shader, "visibility_buf")); + GPU_storagebuf_bind(prototype_buf_, GPU_shader_get_ssbo(shader, "prototype_buf")); + GPU_storagebuf_bind(command_buf_, GPU_shader_get_ssbo(shader, "command_buf")); + GPU_storagebuf_bind(resource_id_buf_, DRW_RESOURCE_ID_SLOT); + GPU_compute_dispatch(shader, divide_ceil_u(prototype_count_, DRW_COMMAND_GROUP_SIZE), 1, 1); + if (GPU_shader_draw_parameters_support() == false) { + GPU_memory_barrier(GPU_BARRIER_VERTEX_ATTRIB_ARRAY); + state.resource_id_buf = resource_id_buf_; + } + else { + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + } + } + + GPU_debug_group_end(); +} + +/** \} */ + +}; // namespace blender::draw::command diff --git a/source/blender/draw/intern/draw_command.hh b/source/blender/draw/intern/draw_command.hh new file mode 100644 index 00000000000..b9117580d91 --- /dev/null +++ b/source/blender/draw/intern/draw_command.hh @@ -0,0 +1,534 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +#pragma once + +/** \file + * \ingroup draw + * + * Commands stored inside draw passes. Converted into GPU commands upon pass submission. + * + * Draw calls (primitive rendering commands) are managed by either `DrawCommandBuf` or + * `DrawMultiBuf`. See implementation details at their definition. + */ + +#include "BKE_global.h" +#include "BLI_map.hh" +#include "DRW_gpu_wrapper.hh" + +#include "draw_command_shared.hh" +#include "draw_handle.hh" +#include "draw_state.h" +#include "draw_view.hh" + +namespace blender::draw::command { + +class DrawCommandBuf; +class DrawMultiBuf; + +/* -------------------------------------------------------------------- */ +/** \name Recording State + * \{ */ + +/** + * Command recording state. + * Keep track of several states and avoid redundant state changes. + */ +struct RecordingState { + GPUShader *shader = nullptr; + bool front_facing = true; + bool inverted_view = false; + DRWState pipeline_state = DRW_STATE_NO_DRAW; + int view_clip_plane_count = 0; + /** Used for gl_BaseInstance workaround. */ + GPUStorageBuf *resource_id_buf = nullptr; + + void front_facing_set(bool facing) + { + /* Facing is inverted if view is not in expected handedness. */ + facing = this->inverted_view == facing; + /* Remove redundant changes. */ + if (assign_if_different(this->front_facing, facing)) { + GPU_front_facing(!facing); + } + } + + void cleanup() + { + if (front_facing == false) { + GPU_front_facing(false); + } + + if (G.debug & G_DEBUG_GPU) { + GPU_storagebuf_unbind_all(); + GPU_texture_image_unbind_all(); + GPU_texture_unbind_all(); + GPU_uniformbuf_unbind_all(); + } + } +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Regular Commands + * \{ */ + +enum class Type : uint8_t { + /** + * None Type commands are either uninitialized or are repurposed as data storage. + * They are skipped during submission. + */ + None = 0, + + /** Commands stored as Undetermined in regular command buffer. */ + Barrier, + Clear, + Dispatch, + DispatchIndirect, + Draw, + DrawIndirect, + PushConstant, + ResourceBind, + ShaderBind, + StateSet, + StencilSet, + + /** Special commands stored in separate buffers. */ + SubPass, + DrawMulti, +}; + +/** + * The index of the group is implicit since it is known by the one who want to + * access it. This also allows to have an indexed object to split the command + * stream. + */ +struct Header { + /** Command type. */ + Type type; + /** Command index in command heap of this type. */ + uint index; +}; + +struct ShaderBind { + GPUShader *shader; + + void execute(RecordingState &state) const; + std::string serialize() const; +}; + +struct ResourceBind { + eGPUSamplerState sampler; + int slot; + bool is_reference; + + enum class Type : uint8_t { + Sampler = 0, + Image, + UniformBuf, + StorageBuf, + } type; + + union { + /** TODO: Use draw::Texture|StorageBuffer|UniformBuffer as resources as they will give more + * debug info. */ + GPUUniformBuf *uniform_buf; + GPUUniformBuf **uniform_buf_ref; + GPUStorageBuf *storage_buf; + GPUStorageBuf **storage_buf_ref; + /** NOTE: Texture is used for both Sampler and Image binds. */ + GPUTexture *texture; + GPUTexture **texture_ref; + }; + + ResourceBind() = default; + + ResourceBind(int slot_, GPUUniformBuf *res) + : slot(slot_), is_reference(false), type(Type::UniformBuf), uniform_buf(res){}; + ResourceBind(int slot_, GPUUniformBuf **res) + : slot(slot_), is_reference(true), type(Type::UniformBuf), uniform_buf_ref(res){}; + ResourceBind(int slot_, GPUStorageBuf *res) + : slot(slot_), is_reference(false), type(Type::StorageBuf), storage_buf(res){}; + ResourceBind(int slot_, GPUStorageBuf **res) + : slot(slot_), is_reference(true), type(Type::StorageBuf), storage_buf_ref(res){}; + ResourceBind(int slot_, draw::Image *res) + : slot(slot_), is_reference(false), type(Type::Image), texture(draw::as_texture(res)){}; + ResourceBind(int slot_, draw::Image **res) + : slot(slot_), is_reference(true), type(Type::Image), texture_ref(draw::as_texture(res)){}; + ResourceBind(int slot_, GPUTexture *res, eGPUSamplerState state) + : sampler(state), slot(slot_), is_reference(false), type(Type::Sampler), texture(res){}; + ResourceBind(int slot_, GPUTexture **res, eGPUSamplerState state) + : sampler(state), slot(slot_), is_reference(true), type(Type::Sampler), texture_ref(res){}; + + void execute() const; + std::string serialize() const; +}; + +struct PushConstant { + int location; + uint8_t array_len; + uint8_t comp_len; + enum class Type : uint8_t { + IntValue = 0, + FloatValue, + IntReference, + FloatReference, + } type; + /** + * IMPORTANT: Data is at the end of the struct as it can span over the next commands. + * These next commands are not real commands but just memory to hold the data and are not + * referenced by any Command::Header. + * This is a hack to support float4x4 copy. + */ + union { + int int1_value; + int2 int2_value; + int3 int3_value; + int4 int4_value; + float float1_value; + float2 float2_value; + float3 float3_value; + float4 float4_value; + const int *int_ref; + const int2 *int2_ref; + const int3 *int3_ref; + const int4 *int4_ref; + const float *float_ref; + const float2 *float2_ref; + const float3 *float3_ref; + const float4 *float4_ref; + const float4x4 *float4x4_ref; + }; + + PushConstant() = default; + + PushConstant(int loc, const float &val) + : location(loc), array_len(1), comp_len(1), type(Type::FloatValue), float1_value(val){}; + PushConstant(int loc, const float2 &val) + : location(loc), array_len(1), comp_len(2), type(Type::FloatValue), float2_value(val){}; + PushConstant(int loc, const float3 &val) + : location(loc), array_len(1), comp_len(3), type(Type::FloatValue), float3_value(val){}; + PushConstant(int loc, const float4 &val) + : location(loc), array_len(1), comp_len(4), type(Type::FloatValue), float4_value(val){}; + + PushConstant(int loc, const int &val) + : location(loc), array_len(1), comp_len(1), type(Type::IntValue), int1_value(val){}; + PushConstant(int loc, const int2 &val) + : location(loc), array_len(1), comp_len(2), type(Type::IntValue), int2_value(val){}; + PushConstant(int loc, const int3 &val) + : location(loc), array_len(1), comp_len(3), type(Type::IntValue), int3_value(val){}; + PushConstant(int loc, const int4 &val) + : location(loc), array_len(1), comp_len(4), type(Type::IntValue), int4_value(val){}; + + PushConstant(int loc, const float *val, int arr) + : location(loc), array_len(arr), comp_len(1), type(Type::FloatReference), float_ref(val){}; + PushConstant(int loc, const float2 *val, int arr) + : location(loc), array_len(arr), comp_len(2), type(Type::FloatReference), float2_ref(val){}; + PushConstant(int loc, const float3 *val, int arr) + : location(loc), array_len(arr), comp_len(3), type(Type::FloatReference), float3_ref(val){}; + PushConstant(int loc, const float4 *val, int arr) + : location(loc), array_len(arr), comp_len(4), type(Type::FloatReference), float4_ref(val){}; + PushConstant(int loc, const float4x4 *val) + : location(loc), array_len(1), comp_len(16), type(Type::FloatReference), float4x4_ref(val){}; + + PushConstant(int loc, const int *val, int arr) + : location(loc), array_len(arr), comp_len(1), type(Type::IntReference), int_ref(val){}; + PushConstant(int loc, const int2 *val, int arr) + : location(loc), array_len(arr), comp_len(2), type(Type::IntReference), int2_ref(val){}; + PushConstant(int loc, const int3 *val, int arr) + : location(loc), array_len(arr), comp_len(3), type(Type::IntReference), int3_ref(val){}; + PushConstant(int loc, const int4 *val, int arr) + : location(loc), array_len(arr), comp_len(4), type(Type::IntReference), int4_ref(val){}; + + void execute(RecordingState &state) const; + std::string serialize() const; +}; + +struct Draw { + GPUBatch *batch; + uint instance_len; + uint vertex_len; + uint vertex_first; + ResourceHandle handle; + + void execute(RecordingState &state) const; + std::string serialize() const; +}; + +struct DrawMulti { + GPUBatch *batch; + DrawMultiBuf *multi_draw_buf; + uint group_first; + uint uuid; + + void execute(RecordingState &state) const; + std::string serialize(std::string line_prefix) const; +}; + +struct DrawIndirect { + GPUBatch *batch; + GPUStorageBuf **indirect_buf; + ResourceHandle handle; + + void execute(RecordingState &state) const; + std::string serialize() const; +}; + +struct Dispatch { + bool is_reference; + union { + int3 size; + int3 *size_ref; + }; + + Dispatch() = default; + + Dispatch(int3 group_len) : is_reference(false), size(group_len){}; + Dispatch(int3 *group_len) : is_reference(true), size_ref(group_len){}; + + void execute(RecordingState &state) const; + std::string serialize() const; +}; + +struct DispatchIndirect { + GPUStorageBuf **indirect_buf; + + void execute(RecordingState &state) const; + std::string serialize() const; +}; + +struct Barrier { + eGPUBarrier type; + + void execute() const; + std::string serialize() const; +}; + +struct Clear { + uint8_t clear_channels; /* #eGPUFrameBufferBits. But want to save some bits. */ + uint8_t stencil; + float depth; + float4 color; + + void execute() const; + std::string serialize() const; +}; + +struct StateSet { + DRWState new_state; + + void execute(RecordingState &state) const; + std::string serialize() const; +}; + +struct StencilSet { + uint write_mask; + uint compare_mask; + uint reference; + + void execute() const; + std::string serialize() const; +}; + +union Undetermined { + ShaderBind shader_bind; + ResourceBind resource_bind; + PushConstant push_constant; + Draw draw; + DrawMulti draw_multi; + DrawIndirect draw_indirect; + Dispatch dispatch; + DispatchIndirect dispatch_indirect; + Barrier barrier; + Clear clear; + StateSet state_set; + StencilSet stencil_set; +}; + +/** Try to keep the command size as low as possible for performance. */ +BLI_STATIC_ASSERT(sizeof(Undetermined) <= 24, "One of the command type is too large.") + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Commands + * + * A draw command buffer used to issue single draw commands without instance merging or any + * other optimizations. + * + * It still uses a ResourceIdBuf to keep the same shader interface as multi draw commands. + * + * \{ */ + +class DrawCommandBuf { + friend Manager; + + private: + using ResourceIdBuf = StorageArrayBuffer<uint, 128, false>; + + /** Array of resource id. One per instance. Generated on GPU and send to GPU. */ + ResourceIdBuf resource_id_buf_; + /** Used items in the resource_id_buf_. Not it's allocated length. */ + uint resource_id_count_ = 0; + + public: + void clear(){}; + + void append_draw(Vector<Header, 0> &headers, + Vector<Undetermined, 0> &commands, + GPUBatch *batch, + uint instance_len, + uint vertex_len, + uint vertex_first, + ResourceHandle handle) + { + vertex_first = vertex_first != -1 ? vertex_first : 0; + instance_len = instance_len != -1 ? instance_len : 1; + + int64_t index = commands.append_and_get_index({}); + headers.append({Type::Draw, static_cast<uint>(index)}); + commands[index].draw = {batch, instance_len, vertex_len, vertex_first, handle}; + } + + void bind(RecordingState &state, Vector<Header, 0> &headers, Vector<Undetermined, 0> &commands); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Multi Draw Commands + * + * For efficient rendering of large scene we strive to minimize the number of draw call and state + * changes. To this end, we group many rendering commands and sort them per render state using + * `DrawGroup` as a container. This is done automatically for any successive commands with the + * same state. + * + * A `DrawGroup` is the combination of a `GPUBatch` (VBO state) and a `command::DrawMulti` + * (Pipeline State). + * + * Inside each `DrawGroup` all instances of a same `GPUBatch` is merged into a single indirect + * command. + * + * To support this arbitrary reordering, we only need to know the offset of all the commands for a + * specific `DrawGroup`. This is done on CPU by doing a simple prefix sum. The result is pushed to + * GPU and used on CPU to issue the right command indirect. + * + * Each draw command is stored in an unsorted array of `DrawPrototype` and sent directly to the + * GPU. + * + * A command generation compute shader then go over each `DrawPrototype`. For each it adds it (or + * not depending on visibility) to the correct draw command using the offset of the `DrawGroup` + * computed on CPU. After that, it also outputs one resource ID for each instance inside a + * `DrawPrototype`. + * + * \{ */ + +class DrawMultiBuf { + friend Manager; + friend DrawMulti; + + private: + using DrawGroupBuf = StorageArrayBuffer<DrawGroup, 16>; + using DrawPrototypeBuf = StorageArrayBuffer<DrawPrototype, 16>; + using DrawCommandBuf = StorageArrayBuffer<DrawCommand, 16, true>; + using ResourceIdBuf = StorageArrayBuffer<uint, 128, true>; + + using DrawGroupKey = std::pair<uint, GPUBatch *>; + using DrawGroupMap = Map<DrawGroupKey, uint>; + /** Maps a DrawMulti command and a gpu batch to their unique DrawGroup command. */ + DrawGroupMap group_ids_; + + /** DrawGroup Command heap. Uploaded to GPU for sorting. */ + DrawGroupBuf group_buf_ = {"DrawGroupBuf"}; + /** Command Prototypes. Unsorted */ + DrawPrototypeBuf prototype_buf_ = {"DrawPrototypeBuf"}; + /** Command list generated by the sorting / compaction steps. Lives on GPU. */ + DrawCommandBuf command_buf_ = {"DrawCommandBuf"}; + /** Array of resource id. One per instance. Lives on GPU. */ + ResourceIdBuf resource_id_buf_ = {"ResourceIdBuf"}; + /** Give unique ID to each header so we can use that as hash key. */ + uint header_id_counter_ = 0; + /** Number of groups inside group_buf_. */ + uint group_count_ = 0; + /** Number of prototype command inside prototype_buf_. */ + uint prototype_count_ = 0; + /** Used items in the resource_id_buf_. Not it's allocated length. */ + uint resource_id_count_ = 0; + + public: + void clear() + { + header_id_counter_ = 0; + group_count_ = 0; + prototype_count_ = 0; + group_ids_.clear(); + } + + void append_draw(Vector<Header, 0> &headers, + Vector<Undetermined, 0> &commands, + GPUBatch *batch, + uint instance_len, + uint vertex_len, + uint vertex_first, + ResourceHandle handle) + { + /* Unsupported for now. Use PassSimple. */ + BLI_assert(vertex_first == 0 || vertex_first == -1); + BLI_assert(vertex_len == -1); + UNUSED_VARS_NDEBUG(vertex_len, vertex_first); + + instance_len = instance_len != -1 ? instance_len : 1; + + /* If there was some state changes since previous call, we have to create another command. */ + if (headers.is_empty() || headers.last().type != Type::DrawMulti) { + uint index = commands.append_and_get_index({}); + headers.append({Type::DrawMulti, index}); + commands[index].draw_multi = {batch, this, (uint)-1, header_id_counter_++}; + } + + DrawMulti &cmd = commands.last().draw_multi; + + uint &group_id = group_ids_.lookup_or_add(DrawGroupKey(cmd.uuid, batch), (uint)-1); + + bool inverted = handle.has_inverted_handedness(); + + if (group_id == (uint)-1) { + uint new_group_id = group_count_++; + + DrawGroup &group = group_buf_.get_or_resize(new_group_id); + group.next = cmd.group_first; + group.len = instance_len; + group.front_facing_len = inverted ? 0 : instance_len; + group.gpu_batch = batch; + group.front_proto_len = 0; + group.back_proto_len = 0; + /* For serialization only. */ + (inverted ? group.back_proto_len : group.front_proto_len)++; + /* Append to list. */ + cmd.group_first = new_group_id; + group_id = new_group_id; + } + else { + DrawGroup &group = group_buf_[group_id]; + group.len += instance_len; + group.front_facing_len += inverted ? 0 : instance_len; + /* For serialization only. */ + (inverted ? group.back_proto_len : group.front_proto_len)++; + } + + DrawPrototype &draw = prototype_buf_.get_or_resize(prototype_count_++); + draw.group_id = group_id; + draw.resource_handle = handle.raw; + draw.instance_len = instance_len; + } + + void bind(RecordingState &state, + Vector<Header, 0> &headers, + Vector<Undetermined, 0> &commands, + VisibilityBuf &visibility_buf); +}; + +/** \} */ + +}; // namespace blender::draw::command
\ No newline at end of file diff --git a/source/blender/draw/intern/draw_command_shared.hh b/source/blender/draw/intern/draw_command_shared.hh new file mode 100644 index 00000000000..22d1facfb09 --- /dev/null +++ b/source/blender/draw/intern/draw_command_shared.hh @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +/** \file + * \ingroup draw + */ + +#ifndef GPU_SHADER +# include "BLI_span.hh" +# include "GPU_shader_shared_utils.h" + +namespace blender::draw::command { + +struct RecordingState; + +#endif + +/* -------------------------------------------------------------------- */ +/** \name Multi Draw + * \{ */ + +/** + * A DrawGroup allow to split the command stream into batch-able chunks of commands with + * the same render state. + */ +struct DrawGroup { + /** Index of next DrawGroup from the same header. */ + uint next; + + /** Index of the first instances after sorting. */ + uint start; + /** Total number of instances (including inverted facing). Needed to issue the draw call. */ + uint len; + /** Number of non inverted scaling instances in this Group. */ + uint front_facing_len; + + /** GPUBatch values to be copied to DrawCommand after sorting (if not overriden). */ + int vertex_len; + int vertex_first; + int base_index; + + /** Atomic counters used during command sorting. */ + uint total_counter; + +#ifndef GPU_SHADER + /* NOTE: Union just to make sure the struct has always the same size on all platform. */ + union { + struct { + /** For debug printing only. */ + uint front_proto_len; + uint back_proto_len; + /** Needed to create the correct draw call. */ + GPUBatch *gpu_batch; + }; + struct { +#endif + uint front_facing_counter; + uint back_facing_counter; + uint _pad0, _pad1; +#ifndef GPU_SHADER + }; + }; +#endif +}; +BLI_STATIC_ASSERT_ALIGN(DrawGroup, 16) + +/** + * Representation of a future draw call inside a DrawGroup. This #DrawPrototype is then + * converted into #DrawCommand on GPU after visibility and compaction. Multiple + * #DrawPrototype might get merged into the same final #DrawCommand. + */ +struct DrawPrototype { + /* Reference to parent DrawGroup to get the GPUBatch vertex / instance count. */ + uint group_id; + /* Resource handle associated with this call. Also reference visibility. */ + uint resource_handle; + /* Number of instances. */ + uint instance_len; + uint _pad0; +}; +BLI_STATIC_ASSERT_ALIGN(DrawPrototype, 16) + +/** \} */ + +#ifndef GPU_SHADER +}; // namespace blender::draw::command +#endif diff --git a/source/blender/draw/intern/draw_common_shader_shared.h b/source/blender/draw/intern/draw_common_shader_shared.h index c9819d9da87..57cb7880ce6 100644 --- a/source/blender/draw/intern/draw_common_shader_shared.h +++ b/source/blender/draw/intern/draw_common_shader_shared.h @@ -19,7 +19,7 @@ typedef struct GlobalsUboStorage GlobalsUboStorage; #define UBO_LAST_COLOR color_uv_shadow /* Used as ubo but colors can be directly referenced as well */ -/* NOTE: Also keep all color as vec4 and between #UBO_FIRST_COLOR and #UBO_LAST_COLOR. */ +/* \note Also keep all color as vec4 and between #UBO_FIRST_COLOR and #UBO_LAST_COLOR. */ struct GlobalsUboStorage { /* UBOs data needs to be 16 byte aligned (size of vec4) */ float4 color_wire; diff --git a/source/blender/draw/intern/draw_debug.cc b/source/blender/draw/intern/draw_debug.cc index ab78db5d913..9cb79d73812 100644 --- a/source/blender/draw/intern/draw_debug.cc +++ b/source/blender/draw/intern/draw_debug.cc @@ -63,26 +63,26 @@ DebugDraw::DebugDraw() void DebugDraw::init() { - cpu_print_buf_.command.v_count = 0; - cpu_print_buf_.command.v_first = 0; - cpu_print_buf_.command.i_count = 1; - cpu_print_buf_.command.i_first = 0; - - cpu_draw_buf_.command.v_count = 0; - cpu_draw_buf_.command.v_first = 0; - cpu_draw_buf_.command.i_count = 1; - cpu_draw_buf_.command.i_first = 0; - - gpu_print_buf_.command.v_count = 0; - gpu_print_buf_.command.v_first = 0; - gpu_print_buf_.command.i_count = 1; - gpu_print_buf_.command.i_first = 0; + cpu_print_buf_.command.vertex_len = 0; + cpu_print_buf_.command.vertex_first = 0; + cpu_print_buf_.command.instance_len = 1; + cpu_print_buf_.command.instance_first_array = 0; + + cpu_draw_buf_.command.vertex_len = 0; + cpu_draw_buf_.command.vertex_first = 0; + cpu_draw_buf_.command.instance_len = 1; + cpu_draw_buf_.command.instance_first_array = 0; + + gpu_print_buf_.command.vertex_len = 0; + gpu_print_buf_.command.vertex_first = 0; + gpu_print_buf_.command.instance_len = 1; + gpu_print_buf_.command.instance_first_array = 0; gpu_print_buf_used = false; - gpu_draw_buf_.command.v_count = 0; - gpu_draw_buf_.command.v_first = 0; - gpu_draw_buf_.command.i_count = 1; - gpu_draw_buf_.command.i_first = 0; + gpu_draw_buf_.command.vertex_len = 0; + gpu_draw_buf_.command.vertex_first = 0; + gpu_draw_buf_.command.instance_len = 1; + gpu_draw_buf_.command.instance_first_array = 0; gpu_draw_buf_used = false; modelmat_reset(); @@ -323,11 +323,11 @@ template<> void DebugDraw::print_value<uint4>(const uint4 &value) void DebugDraw::draw_line(float3 v1, float3 v2, uint color) { DebugDrawBuf &buf = cpu_draw_buf_; - uint index = buf.command.v_count; + uint index = buf.command.vertex_len; if (index + 2 < DRW_DEBUG_DRAW_VERT_MAX) { buf.verts[index + 0] = vert_pack(model_mat_ * v1, color); buf.verts[index + 1] = vert_pack(model_mat_ * v2, color); - buf.command.v_count += 2; + buf.command.vertex_len += 2; } } @@ -356,7 +356,7 @@ DRWDebugVert DebugDraw::vert_pack(float3 pos, uint color) void DebugDraw::print_newline() { print_col_ = 0u; - print_row_ = ++cpu_print_buf_.command.i_first; + print_row_ = ++cpu_print_buf_.command.instance_first_array; } void DebugDraw::print_string_start(uint len) @@ -406,7 +406,7 @@ void DebugDraw::print_char4(uint data) break; } /* NOTE: Do not skip the header manually like in GPU. */ - uint cursor = cpu_print_buf_.command.v_count++; + uint cursor = cpu_print_buf_.command.vertex_len++; if (cursor < DRW_DEBUG_PRINT_MAX) { /* For future usage. (i.e: Color) */ uint flags = 0u; @@ -504,7 +504,7 @@ void DebugDraw::print_value_uint(uint value, void DebugDraw::display_lines() { - if (cpu_draw_buf_.command.v_count == 0 && gpu_draw_buf_used == false) { + if (cpu_draw_buf_.command.vertex_len == 0 && gpu_draw_buf_used == false) { return; } GPU_debug_group_begin("Lines"); @@ -541,7 +541,7 @@ void DebugDraw::display_lines() void DebugDraw::display_prints() { - if (cpu_print_buf_.command.v_count == 0 && gpu_print_buf_used == false) { + if (cpu_print_buf_.command.vertex_len == 0 && gpu_print_buf_used == false) { return; } GPU_debug_group_begin("Prints"); diff --git a/source/blender/draw/intern/draw_defines.h b/source/blender/draw/intern/draw_defines.h new file mode 100644 index 00000000000..3df7e47cffb --- /dev/null +++ b/source/blender/draw/intern/draw_defines.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. + */ + +/** \file + * \ingroup draw + * + * List of defines that are shared with the GPUShaderCreateInfos. We do this to avoid + * dragging larger headers into the createInfo pipeline which would cause problems. + */ + +#pragma once + +#define DRW_VIEW_UBO_SLOT 0 + +#define DRW_RESOURCE_ID_SLOT 11 +#define DRW_OBJ_MAT_SLOT 10 +#define DRW_OBJ_INFOS_SLOT 9 +#define DRW_OBJ_ATTR_SLOT 8 + +#define DRW_DEBUG_PRINT_SLOT 15 +#define DRW_DEBUG_DRAW_SLOT 14 + +#define DRW_COMMAND_GROUP_SIZE 64 +#define DRW_FINALIZE_GROUP_SIZE 64 +/* Must be multiple of 32. Set to 32 for shader simplicity. */ +#define DRW_VISIBILITY_GROUP_SIZE 32 diff --git a/source/blender/draw/intern/draw_handle.hh b/source/blender/draw/intern/draw_handle.hh new file mode 100644 index 00000000000..5f96bfa5dcd --- /dev/null +++ b/source/blender/draw/intern/draw_handle.hh @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +#pragma once + +/** \file + * \ingroup draw + * + * A unique identifier for each object component. + * It is used to access each component data such as matrices and object attributes. + * It is valid only for the current draw, it is not persistent. + * + * The most significant bit is used to encode if the object needs to invert the front face winding + * because of its object matrix handedness. This is handy because this means sorting inside + * #DrawGroup command will put all inverted commands last. + * + * Default value of 0 points toward an non-cull-able object with unit bounding box centered at + * the origin. + */ + +#include "draw_shader_shared.h" + +struct Object; +struct DupliObject; + +namespace blender::draw { + +struct ResourceHandle { + uint raw; + + ResourceHandle() = default; + ResourceHandle(uint raw_) : raw(raw_){}; + ResourceHandle(uint index, bool inverted_handedness) + { + raw = index; + SET_FLAG_FROM_TEST(raw, inverted_handedness, 0x80000000u); + } + + bool has_inverted_handedness() const + { + return (raw & 0x80000000u) != 0; + } + + uint resource_index() const + { + return (raw & 0x7FFFFFFFu); + } +}; + +/* TODO(fclem): Move to somewhere more appropriated after cleaning up the header dependencies. */ +struct ObjectRef { + Object *object; + /** Dupli object that corresponds to the current object. */ + DupliObject *dupli_object; + /** Object that created the dupli-list the current object is part of. */ + Object *dupli_parent; +}; + +}; // namespace blender::draw diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c index 8754a75d346..ac2aea4524d 100644 --- a/source/blender/draw/intern/draw_instance_data.c +++ b/source/blender/draw/intern/draw_instance_data.c @@ -564,7 +564,8 @@ typedef struct DRWUniformAttrBuf { struct DRWUniformAttrBuf *next_empty; } DRWUniformAttrBuf; -static DRWUniformAttrBuf *drw_uniform_attrs_pool_ensure(GHash *table, GPUUniformAttrList *key) +static DRWUniformAttrBuf *drw_uniform_attrs_pool_ensure(GHash *table, + const GPUUniformAttrList *key) { void **pkey, **pval; @@ -669,7 +670,7 @@ static void drw_uniform_attribute_lookup(GPUUniformAttr *attr, } void drw_uniform_attrs_pool_update(GHash *table, - GPUUniformAttrList *key, + const GPUUniformAttrList *key, DRWResourceHandle *handle, Object *ob, Object *dupli_parent, @@ -690,7 +691,8 @@ void drw_uniform_attrs_pool_update(GHash *table, } } -DRWSparseUniformBuf *DRW_uniform_attrs_pool_find_ubo(GHash *table, struct GPUUniformAttrList *key) +DRWSparseUniformBuf *DRW_uniform_attrs_pool_find_ubo(GHash *table, + const struct GPUUniformAttrList *key) { DRWUniformAttrBuf *buffer = BLI_ghash_lookup(table, key); return buffer ? &buffer->ubos : NULL; diff --git a/source/blender/draw/intern/draw_instance_data.h b/source/blender/draw/intern/draw_instance_data.h index 4b5cf63bb3b..9053544d98a 100644 --- a/source/blender/draw/intern/draw_instance_data.h +++ b/source/blender/draw/intern/draw_instance_data.h @@ -106,4 +106,4 @@ struct GHash *DRW_uniform_attrs_pool_new(void); void DRW_uniform_attrs_pool_flush_all(struct GHash *table); void DRW_uniform_attrs_pool_clear_all(struct GHash *table); struct DRWSparseUniformBuf *DRW_uniform_attrs_pool_find_ubo(struct GHash *table, - struct GPUUniformAttrList *key); + const struct GPUUniformAttrList *key); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index f44cd33fb2b..799d0544e34 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1001,6 +1001,8 @@ static void drw_engines_init(void) static void drw_engines_cache_init(void) { + DRW_manager_begin_sync(); + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { if (data->text_draw_cache) { DRW_text_cache_destroy(data->text_draw_cache); @@ -1072,6 +1074,8 @@ static void drw_engines_cache_finish(void) engine->cache_finish(data); } } + + DRW_manager_end_sync(); } static void drw_engines_draw_scene(void) diff --git a/source/blender/draw/intern/draw_manager.cc b/source/blender/draw/intern/draw_manager.cc new file mode 100644 index 00000000000..2841abb53e7 --- /dev/null +++ b/source/blender/draw/intern/draw_manager.cc @@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +/** \file + * \ingroup draw + */ + +#include "BKE_global.h" +#include "GPU_compute.h" + +#include "draw_debug.hh" +#include "draw_defines.h" +#include "draw_manager.h" +#include "draw_manager.hh" +#include "draw_pass.hh" +#include "draw_shader.h" + +namespace blender::draw { + +Manager::~Manager() +{ + for (GPUTexture *texture : acquired_textures) { + /* Decrease refcount and free if 0. */ + GPU_texture_free(texture); + } +} + +void Manager::begin_sync() +{ + /* TODO: This means the reference is kept until further redraw or manager teardown. Instead, they + * should be released after each draw loop. But for now, mimics old DRW behavior. */ + for (GPUTexture *texture : acquired_textures) { + /* Decrease refcount and free if 0. */ + GPU_texture_free(texture); + } + + acquired_textures.clear(); + +#ifdef DEBUG + /* Detect non-init data. */ + memset(matrix_buf.data(), 0xF0, resource_len_ * sizeof(*matrix_buf.data())); + memset(bounds_buf.data(), 0xF0, resource_len_ * sizeof(*bounds_buf.data())); + memset(infos_buf.data(), 0xF0, resource_len_ * sizeof(*infos_buf.data())); +#endif + resource_len_ = 0; + attribute_len_ = 0; + /* TODO(fclem): Resize buffers if too big, but with an hysteresis threshold. */ + + object_active = DST.draw_ctx.obact; + + /* Init the 0 resource. */ + resource_handle(float4x4::identity()); +} + +void Manager::end_sync() +{ + GPU_debug_group_begin("Manager.end_sync"); + + matrix_buf.push_update(); + bounds_buf.push_update(); + infos_buf.push_update(); + attributes_buf.push_update(); + attributes_buf_legacy.push_update(); + + debug_bind(); + + /* Dispatch compute to finalize the resources on GPU. Save a bit of CPU time. */ + uint thread_groups = divide_ceil_u(resource_len_, DRW_FINALIZE_GROUP_SIZE); + GPUShader *shader = DRW_shader_draw_resource_finalize_get(); + GPU_shader_bind(shader); + GPU_shader_uniform_1i(shader, "resource_len", resource_len_); + GPU_storagebuf_bind(matrix_buf, GPU_shader_get_ssbo(shader, "matrix_buf")); + GPU_storagebuf_bind(bounds_buf, GPU_shader_get_ssbo(shader, "bounds_buf")); + GPU_storagebuf_bind(infos_buf, GPU_shader_get_ssbo(shader, "infos_buf")); + GPU_compute_dispatch(shader, thread_groups, 1, 1); + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + + GPU_debug_group_end(); +} + +void Manager::debug_bind() +{ +#ifdef DEBUG + if (DST.debug == nullptr) { + return; + } + GPU_storagebuf_bind(drw_debug_gpu_draw_buf_get(), DRW_DEBUG_DRAW_SLOT); + GPU_storagebuf_bind(drw_debug_gpu_print_buf_get(), DRW_DEBUG_PRINT_SLOT); +# ifndef DISABLE_DEBUG_SHADER_PRINT_BARRIER + /* Add a barrier to allow multiple shader writing to the same buffer. */ + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); +# endif +#endif +} + +void Manager::resource_bind() +{ + GPU_storagebuf_bind(matrix_buf, DRW_OBJ_MAT_SLOT); + GPU_storagebuf_bind(infos_buf, DRW_OBJ_INFOS_SLOT); + GPU_storagebuf_bind(attributes_buf, DRW_OBJ_ATTR_SLOT); + /* 2 is the hardcoded location of the uniform attr UBO. */ + /* TODO(@fclem): Remove this workaround. */ + GPU_uniformbuf_bind(attributes_buf_legacy, 2); +} + +void Manager::submit(PassSimple &pass, View &view) +{ + view.bind(); + + debug_bind(); + + command::RecordingState state; + state.inverted_view = view.is_inverted(); + + pass.draw_commands_buf_.bind(state, pass.headers_, pass.commands_); + + resource_bind(); + + pass.submit(state); + + state.cleanup(); +} + +void Manager::submit(PassMain &pass, View &view) +{ + view.bind(); + + debug_bind(); + + bool freeze_culling = (U.experimental.use_viewport_debug && DST.draw_ctx.v3d && + (DST.draw_ctx.v3d->debug_flag & V3D_DEBUG_FREEZE_CULLING) != 0); + + view.compute_visibility(bounds_buf, resource_len_, freeze_culling); + + command::RecordingState state; + state.inverted_view = view.is_inverted(); + + pass.draw_commands_buf_.bind(state, pass.headers_, pass.commands_, view.visibility_buf_); + + resource_bind(); + + pass.submit(state); + + state.cleanup(); +} + +void Manager::submit(PassSortable &pass, View &view) +{ + pass.sort(); + + this->submit(static_cast<PassMain &>(pass), view); +} + +void Manager::submit(PassSimple &pass) +{ + debug_bind(); + + command::RecordingState state; + + pass.draw_commands_buf_.bind(state, pass.headers_, pass.commands_); + + resource_bind(); + + pass.submit(state); + + state.cleanup(); +} + +Manager::SubmitDebugOutput Manager::submit_debug(PassSimple &pass, View &view) +{ + submit(pass, view); + + pass.draw_commands_buf_.resource_id_buf_.read(); + + Manager::SubmitDebugOutput output; + output.resource_id = {pass.draw_commands_buf_.resource_id_buf_.data(), + pass.draw_commands_buf_.resource_id_count_}; + /* There is no visibility data for PassSimple. */ + output.visibility = {(uint *)view.visibility_buf_.data(), 0}; + return output; +} + +Manager::SubmitDebugOutput Manager::submit_debug(PassMain &pass, View &view) +{ + submit(pass, view); + + GPU_finish(); + + pass.draw_commands_buf_.resource_id_buf_.read(); + view.visibility_buf_.read(); + + Manager::SubmitDebugOutput output; + output.resource_id = {pass.draw_commands_buf_.resource_id_buf_.data(), + pass.draw_commands_buf_.resource_id_count_}; + output.visibility = {(uint *)view.visibility_buf_.data(), divide_ceil_u(resource_len_, 32)}; + return output; +} + +Manager::DataDebugOutput Manager::data_debug() +{ + matrix_buf.read(); + bounds_buf.read(); + infos_buf.read(); + + Manager::DataDebugOutput output; + output.matrices = {matrix_buf.data(), resource_len_}; + output.bounds = {bounds_buf.data(), resource_len_}; + output.infos = {infos_buf.data(), resource_len_}; + return output; +} + +} // namespace blender::draw diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index a29f2fa7507..4f71e665390 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -377,7 +377,7 @@ struct DRWUniform { /* DRW_UNIFORM_INT_COPY */ int ivalue[4]; /* DRW_UNIFORM_BLOCK_OBATTRS */ - struct GPUUniformAttrList *uniform_attrs; + const struct GPUUniformAttrList *uniform_attrs; }; int location; /* Uniform location or binding point for textures and UBO's. */ uint8_t type; /* #DRWUniformType */ @@ -403,7 +403,7 @@ struct DRWShadingGroup { DRWResourceHandle pass_handle; /* Memblock key to parent pass. */ /* Set of uniform attributes used by this shader. */ - struct GPUUniformAttrList *uniform_attrs; + const struct GPUUniformAttrList *uniform_attrs; }; /* This struct is used after cache populate if using the Z sorting. * It will not conflict with the above struct. */ @@ -681,7 +681,7 @@ GPUBatch *drw_cache_procedural_triangles_get(void); GPUBatch *drw_cache_procedural_triangle_strips_get(void); void drw_uniform_attrs_pool_update(struct GHash *table, - struct GPUUniformAttrList *key, + const struct GPUUniformAttrList *key, DRWResourceHandle *handle, struct Object *ob, struct Object *dupli_parent, @@ -694,6 +694,9 @@ bool drw_engine_data_engines_data_validate(GPUViewport *viewport, void **engine_ void drw_engine_data_cache_release(GPUViewport *viewport); void drw_engine_data_free(GPUViewport *viewport); +void DRW_manager_begin_sync(void); +void DRW_manager_end_sync(void); + #ifdef __cplusplus } #endif diff --git a/source/blender/draw/intern/draw_manager.hh b/source/blender/draw/intern/draw_manager.hh new file mode 100644 index 00000000000..867b376702c --- /dev/null +++ b/source/blender/draw/intern/draw_manager.hh @@ -0,0 +1,230 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +#pragma once + +/** \file + * \ingroup draw + * + * `draw::Manager` is the interface between scene data and viewport engines. + * + * It holds per component data (`ObjectInfo`, `ObjectMatrices`, ...) indexed per `ResourceHandle`. + * + * \note It is currently work in progress and should replace the old global draw manager. + */ + +#include "BLI_listbase_wrapper.hh" +#include "BLI_sys_types.h" +#include "GPU_material.h" + +#include "draw_resource.hh" +#include "draw_view.hh" + +#include <string> + +namespace blender::draw { + +/* Forward declarations. */ + +namespace detail { +template<typename T> class Pass; +} // namespace detail + +namespace command { +class DrawCommandBuf; +class DrawMultiBuf; +} // namespace command + +using PassSimple = detail::Pass<command::DrawCommandBuf>; +using PassMain = detail::Pass<command::DrawMultiBuf>; +class PassSortable; + +class Manager { + using ObjectMatricesBuf = StorageArrayBuffer<ObjectMatrices, 128>; + using ObjectBoundsBuf = StorageArrayBuffer<ObjectBounds, 128>; + using ObjectInfosBuf = StorageArrayBuffer<ObjectInfos, 128>; + using ObjectAttributeBuf = StorageArrayBuffer<ObjectAttribute, 128>; + /** TODO(fclem): Remove once we get rid of old EEVEE codebase. DRW_RESOURCE_CHUNK_LEN = 512 */ + using ObjectAttributeLegacyBuf = UniformArrayBuffer<float4, 8 * 512>; + + public: + struct SubmitDebugOutput { + /** Indexed by resource id. */ + Span<uint32_t> visibility; + /** Indexed by drawn instance. */ + Span<uint32_t> resource_id; + }; + + struct DataDebugOutput { + /** Indexed by resource id. */ + Span<ObjectMatrices> matrices; + /** Indexed by resource id. */ + Span<ObjectBounds> bounds; + /** Indexed by resource id. */ + Span<ObjectInfos> infos; + }; + + /** + * Buffers containing all object data. Referenced by resource index. + * Exposed as public members for shader access after sync. + */ + ObjectMatricesBuf matrix_buf; + ObjectBoundsBuf bounds_buf; + ObjectInfosBuf infos_buf; + + /** + * Object Attributes are reference by indirection data inside ObjectInfos. + * This is because attribute list is arbitrary. + */ + ObjectAttributeBuf attributes_buf; + /** TODO(fclem): Remove once we get rid of old EEVEE codebase. Only here to satisfy bindings. */ + ObjectAttributeLegacyBuf attributes_buf_legacy; + + /** List of textures coming from Image data-blocks. They need to be refcounted in order to avoid + * beeing freed in another thread. */ + Vector<GPUTexture *> acquired_textures; + + private: + /** Number of resource handle recorded. */ + uint resource_len_ = 0; + /** Number of object attribute recorded. */ + uint attribute_len_ = 0; + + Object *object = nullptr; + Object *object_active = nullptr; + + public: + Manager(){}; + ~Manager(); + + /** + * Create a new resource handle for the given object. Can be called multiple time with the + * same object **successively** without duplicating the data. + */ + ResourceHandle resource_handle(const ObjectRef ref); + /** + * Get resource id for a loose matrix. The draw-calls for this resource handle won't be culled + * and there won't be any associated object info / bounds. Assumes correct handedness / winding. + */ + ResourceHandle resource_handle(const float4x4 &model_matrix); + /** + * Get resource id for a loose matrix with bounds. The draw-calls for this resource handle will + * be culled bute there won't be any associated object info / bounds. Assumes correct handedness + * / winding. + */ + ResourceHandle resource_handle(const float4x4 &model_matrix, + const float3 &bounds_center, + const float3 &bounds_half_extent); + + /** + * Populate additional per resource data on demand. + */ + void extract_object_attributes(ResourceHandle handle, + const ObjectRef &ref, + Span<GPUMaterial *> materials); + + /** + * Submit a pass for drawing. All resource reference will be dereferenced and commands will be + * sent to GPU. + */ + void submit(PassSimple &pass, View &view); + void submit(PassMain &pass, View &view); + void submit(PassSortable &pass, View &view); + /** + * Variant without any view. Must not contain any shader using `draw_view` create info. + */ + void submit(PassSimple &pass); + + /** + * Submit a pass for drawing but read back all data buffers for inspection. + */ + SubmitDebugOutput submit_debug(PassSimple &pass, View &view); + SubmitDebugOutput submit_debug(PassMain &pass, View &view); + + /** + * Check data buffers of the draw manager. Only to be used after end_sync(). + */ + DataDebugOutput data_debug(); + + /** + * Will acquire the texture using ref counting and release it after drawing. To be used for + * texture coming from blender Image. + */ + void acquire_texture(GPUTexture *texture) + { + GPU_texture_ref(texture); + acquired_textures.append(texture); + } + + /** TODO(fclem): The following should become private at some point. */ + void begin_sync(); + void end_sync(); + + void debug_bind(); + void resource_bind(); +}; + +inline ResourceHandle Manager::resource_handle(const ObjectRef ref) +{ + bool is_active_object = (ref.dupli_object ? ref.dupli_parent : ref.object) == object_active; + matrix_buf.get_or_resize(resource_len_).sync(*ref.object); + bounds_buf.get_or_resize(resource_len_).sync(*ref.object); + infos_buf.get_or_resize(resource_len_).sync(ref, is_active_object); + return ResourceHandle(resource_len_++, (ref.object->transflag & OB_NEG_SCALE) != 0); +} + +inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix) +{ + matrix_buf.get_or_resize(resource_len_).sync(model_matrix); + bounds_buf.get_or_resize(resource_len_).sync(); + infos_buf.get_or_resize(resource_len_).sync(); + return ResourceHandle(resource_len_++, false); +} + +inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix, + const float3 &bounds_center, + const float3 &bounds_half_extent) +{ + matrix_buf.get_or_resize(resource_len_).sync(model_matrix); + bounds_buf.get_or_resize(resource_len_).sync(bounds_center, bounds_half_extent); + infos_buf.get_or_resize(resource_len_).sync(); + return ResourceHandle(resource_len_++, false); +} + +inline void Manager::extract_object_attributes(ResourceHandle handle, + const ObjectRef &ref, + Span<GPUMaterial *> materials) +{ + ObjectInfos &infos = infos_buf.get_or_resize(handle.resource_index()); + infos.object_attrs_offset = attribute_len_; + + /* Simple cache solution to avoid duplicates. */ + Vector<uint32_t, 4> hash_cache; + + for (const GPUMaterial *mat : materials) { + const GPUUniformAttrList *attr_list = GPU_material_uniform_attributes(mat); + if (attr_list == nullptr) { + continue; + } + + LISTBASE_FOREACH (const GPUUniformAttr *, attr, &attr_list->list) { + /** WATCH: Linear Search. Avoid duplicate attributes across materials. */ + if ((mat != materials.first()) && (hash_cache.first_index_of_try(attr->hash_code) != -1)) { + /* Attribute has already been added to the attribute buffer by another material. */ + continue; + } + hash_cache.append(attr->hash_code); + if (attributes_buf.get_or_resize(attribute_len_).sync(ref, *attr)) { + infos.object_attrs_len++; + attribute_len_++; + } + } + } +} + +} // namespace blender::draw + +/* TODO(@fclem): This is for testing. The manager should be passed to the engine through the + * callbacks. */ +blender::draw::Manager *DRW_manager_get(); +blender::draw::ObjectRef DRW_object_ref_get(Object *object); diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 913d1b4c3f4..c75049508f9 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -1638,7 +1638,7 @@ void DRW_shgroup_add_material_resources(DRWShadingGroup *grp, struct GPUMaterial DRW_shgroup_uniform_block(grp, GPU_UBO_BLOCK_NAME, ubo); } - GPUUniformAttrList *uattrs = GPU_material_uniform_attributes(material); + const GPUUniformAttrList *uattrs = GPU_material_uniform_attributes(material); if (uattrs != NULL) { int loc = GPU_shader_get_uniform_block_binding(grp->shader, GPU_ATTRIBUTE_UBO_BLOCK_NAME); drw_shgroup_uniform_create_ex(grp, loc, DRW_UNIFORM_BLOCK_OBATTRS, uattrs, 0, 0, 1); diff --git a/source/blender/draw/intern/draw_pass.hh b/source/blender/draw/intern/draw_pass.hh new file mode 100644 index 00000000000..65faa9febbc --- /dev/null +++ b/source/blender/draw/intern/draw_pass.hh @@ -0,0 +1,1004 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +#pragma once + +/** \file + * \ingroup draw + * + * Passes record draw commands. Commands are executed only when a pass is submitted for execution. + * + * `PassMain`: + * Should be used on heavy load passes such as ones that may contain scene objects. Draw call + * submission is optimized for large number of draw calls. But has a significant overhead per + * #Pass. Use many #PassSub along with a main #Pass to reduce the overhead and allow groupings of + * commands. \note The draw call order inside a batch of multiple draw with the exact same state is + * not guaranteed and is not even deterministic. Use a PassSimple or PassSortable if ordering is + * needed. \note As of now, it is also quite limited in the type of draw command it can record + * (no custom vertex count, no custom first vertex). + * + * `PassSimple`: + * Does not have the overhead of #PassMain but does not have the culling and batching optimization. + * It should be used for passes that needs a few commands or that needs guaranteed draw call order. + * + * `Pass<T>::Sub`: + * A lightweight #Pass that lives inside a main #Pass. It can only be created from #Pass.sub() + * and is auto managed. This mean it can be created, filled and thrown away. A #PassSub reference + * is valid until the next #Pass.init() of the parent pass. Commands recorded inside a #PassSub are + * inserted inside the parent #Pass where the sub have been created durring submission. + * + * `PassSortable`: + * This is a sort of `PassMain` augmented with a per sub-pass sorting value. They can't directly + * contain draw command, everything needs to be inside sub-passes. Sub-passes are automatically + * sorted before submission. + * + * \note A pass can be recorded once and resubmitted any number of time. This can be a good + * optimization for passes that are always the same for each frame. The only thing to be aware of + * is the life time of external resources. If a pass contains draw-calls with non default + * ResourceHandle (not 0) or a reference to any non static resources (GPUBatch, PushConstant ref, + * ResourceBind ref) it will have to be re-recorded if any of these reference becomes invalid. + */ + +#include "BKE_image.h" +#include "BLI_vector.hh" +#include "DRW_gpu_wrapper.hh" +#include "GPU_debug.h" +#include "GPU_material.h" + +#include "draw_command.hh" +#include "draw_handle.hh" +#include "draw_manager.hh" +#include "draw_pass.hh" +#include "draw_shader_shared.h" +#include "draw_state.h" + +#include "intern/gpu_codegen.h" + +namespace blender::draw { + +using namespace blender::draw; +using namespace blender::draw::command; + +class Manager; + +/* -------------------------------------------------------------------- */ +/** \name Pass API + * \{ */ + +namespace detail { + +/** + * Special container that never moves allocated items and has fast indexing. + */ +template<typename T, + /** Numbers of element of type T to allocate together. */ + int64_t block_size = 16> +class SubPassVector { + private: + Vector<std::unique_ptr<Vector<T, block_size>>, 0> blocks_; + + public: + void clear() + { + blocks_.clear(); + } + + int64_t append_and_get_index(T &&elem) + { + /* Do not go over the inline size so that existing members never move. */ + if (blocks_.is_empty() || blocks_.last()->size() == block_size) { + blocks_.append(std::make_unique<Vector<T, block_size>>()); + } + return blocks_.last()->append_and_get_index(std::move(elem)) + + (blocks_.size() - 1) * block_size; + } + + T &operator[](int64_t index) + { + return (*blocks_[index / block_size])[index % block_size]; + } + + const T &operator[](int64_t index) const + { + return (*blocks_[index / block_size])[index % block_size]; + } +}; + +/** + * Public API of a draw pass. + */ +template< + /** Type of command buffer used to create the draw calls. */ + typename DrawCommandBufType> +class PassBase { + friend Manager; + + /** Will use texture own sampler state. */ + static constexpr eGPUSamplerState sampler_auto = GPU_SAMPLER_MAX; + + protected: + /** Highest level of the command stream. Split command stream in different command types. */ + Vector<command::Header, 0> headers_; + /** Commands referenced by headers (which contains their types). */ + Vector<command::Undetermined, 0> commands_; + /* Reference to draw commands buffer. Either own or from parent pass. */ + DrawCommandBufType &draw_commands_buf_; + /* Reference to sub-pass commands buffer. Either own or from parent pass. */ + SubPassVector<PassBase<DrawCommandBufType>> &sub_passes_; + /** Currently bound shader. Used for interface queries. */ + GPUShader *shader_; + + public: + const char *debug_name; + + PassBase(const char *name, + DrawCommandBufType &draw_command_buf, + SubPassVector<PassBase<DrawCommandBufType>> &sub_passes, + GPUShader *shader = nullptr) + : draw_commands_buf_(draw_command_buf), + sub_passes_(sub_passes), + shader_(shader), + debug_name(name){}; + + /** + * Reset the pass command pool. + * \note Implemented in derived class. Not a virtual function to avoid indirection. Here only for + * API readability listing. + */ + void init(); + + /** + * Create a sub-pass inside this pass. + */ + PassBase<DrawCommandBufType> &sub(const char *name); + + /** + * Changes the fixed function pipeline state. + * Starts as DRW_STATE_NO_DRAW at the start of a Pass submission. + * SubPass inherit previous pass state. + * + * IMPORTANT: This does not set the stencil mask/reference values. Add a call to state_stencil() + * to ensure correct behavior of stencil aware draws. + */ + void state_set(DRWState state); + + /** + * Clear the current frame-buffer. + */ + void clear_color(float4 color); + void clear_depth(float depth); + void clear_stencil(uint8_t stencil); + void clear_depth_stencil(float depth, uint8_t stencil); + void clear_color_depth_stencil(float4 color, float depth, uint8_t stencil); + + /** + * Reminders: + * - (compare_mask & reference) is what is tested against (compare_mask & stencil_value) + * stencil_value being the value stored in the stencil buffer. + * - (write-mask & reference) is what gets written if the test condition is fulfilled. + */ + void state_stencil(uint8_t write_mask, uint8_t reference, uint8_t compare_mask); + + /** + * Bind a shader. Any following bind() or push_constant() call will use its interface. + */ + void shader_set(GPUShader *shader); + + /** + * Bind a material shader along with its associated resources. Any following bind() or + * push_constant() call will use its interface. + * IMPORTANT: Assumes material is compiled and can be used (no compilation error). + */ + void material_set(Manager &manager, GPUMaterial *material); + + /** + * Record a draw call. + * \note Setting the count or first to -1 will use the values from the batch. + * \note An instance or vertex count of 0 will discard the draw call. It will not be recorded. + */ + void draw(GPUBatch *batch, + uint instance_len = -1, + uint vertex_len = -1, + uint vertex_first = -1, + ResourceHandle handle = {0}); + + /** + * Shorter version for the common case. + * \note Implemented in derived class. Not a virtual function to avoid indirection. + */ + void draw(GPUBatch *batch, ResourceHandle handle); + + /** + * Record a procedural draw call. Geometry is **NOT** source from a GPUBatch. + * \note An instance or vertex count of 0 will discard the draw call. It will not be recorded. + */ + void draw_procedural(GPUPrimType primitive, + uint instance_len, + uint vertex_len, + uint vertex_first = -1, + ResourceHandle handle = {0}); + + /** + * Indirect variants. + * \note If needed, the resource id need to also be set accordingly in the DrawCommand. + */ + void draw_indirect(GPUBatch *batch, + StorageBuffer<DrawCommand, true> &indirect_buffer, + ResourceHandle handle = {0}); + void draw_procedural_indirect(GPUPrimType primitive, + StorageBuffer<DrawCommand, true> &indirect_buffer, + ResourceHandle handle = {0}); + + /** + * Record a compute dispatch call. + */ + void dispatch(int3 group_len); + void dispatch(int3 *group_len); + void dispatch(StorageBuffer<DispatchCommand> &indirect_buffer); + + /** + * Record a barrier call to synchronize arbitrary load/store operation between draw calls. + */ + void barrier(eGPUBarrier type); + + /** + * Bind a shader resource. + * + * Reference versions are to be used when the resource might be resize / realloc or even change + * between the time it is referenced and the time it is dereferenced for drawing. + * + * IMPORTANT: Will keep a reference to the data and dereference it upon drawing. Make sure data + * still alive until pass submission. + * + * \note Variations using slot will not query a shader interface and can be used before + * binding a shader. + */ + void bind_image(const char *name, GPUTexture *image); + void bind_image(const char *name, GPUTexture **image); + void bind_image(int slot, GPUTexture *image); + void bind_image(int slot, GPUTexture **image); + void bind_texture(const char *name, GPUTexture *texture, eGPUSamplerState state = sampler_auto); + void bind_texture(const char *name, GPUTexture **texture, eGPUSamplerState state = sampler_auto); + void bind_texture(int slot, GPUTexture *texture, eGPUSamplerState state = sampler_auto); + void bind_texture(int slot, GPUTexture **texture, eGPUSamplerState state = sampler_auto); + void bind_ssbo(const char *name, GPUStorageBuf *buffer); + void bind_ssbo(const char *name, GPUStorageBuf **buffer); + void bind_ssbo(int slot, GPUStorageBuf *buffer); + void bind_ssbo(int slot, GPUStorageBuf **buffer); + void bind_ubo(const char *name, GPUUniformBuf *buffer); + void bind_ubo(const char *name, GPUUniformBuf **buffer); + void bind_ubo(int slot, GPUUniformBuf *buffer); + void bind_ubo(int slot, GPUUniformBuf **buffer); + + /** + * Update a shader constant. + * + * Reference versions are to be used when the resource might change between the time it is + * referenced and the time it is dereferenced for drawing. + * + * IMPORTANT: Will keep a reference to the data and dereference it upon drawing. Make sure data + * still alive until pass submission. + * + * \note bool reference version is expected to take bool1 reference which is aliased to int. + */ + void push_constant(const char *name, const float &data); + void push_constant(const char *name, const float2 &data); + void push_constant(const char *name, const float3 &data); + void push_constant(const char *name, const float4 &data); + void push_constant(const char *name, const int &data); + void push_constant(const char *name, const int2 &data); + void push_constant(const char *name, const int3 &data); + void push_constant(const char *name, const int4 &data); + void push_constant(const char *name, const bool &data); + void push_constant(const char *name, const float4x4 &data); + void push_constant(const char *name, const float *data, int array_len = 1); + void push_constant(const char *name, const float2 *data, int array_len = 1); + void push_constant(const char *name, const float3 *data, int array_len = 1); + void push_constant(const char *name, const float4 *data, int array_len = 1); + void push_constant(const char *name, const int *data, int array_len = 1); + void push_constant(const char *name, const int2 *data, int array_len = 1); + void push_constant(const char *name, const int3 *data, int array_len = 1); + void push_constant(const char *name, const int4 *data, int array_len = 1); + void push_constant(const char *name, const float4x4 *data); + + /** + * Turn the pass into a string for inspection. + */ + std::string serialize(std::string line_prefix = "") const; + + friend std::ostream &operator<<(std::ostream &stream, const PassBase &pass) + { + return stream << pass.serialize(); + } + + protected: + /** + * Internal Helpers + */ + + int push_constant_offset(const char *name); + + void clear(eGPUFrameBufferBits planes, float4 color, float depth, uint8_t stencil); + + GPUBatch *procedural_batch_get(GPUPrimType primitive); + + /** + * Return a new command recorded with the given type. + */ + command::Undetermined &create_command(command::Type type); + + void submit(command::RecordingState &state) const; +}; + +template<typename DrawCommandBufType> class Pass : public detail::PassBase<DrawCommandBufType> { + public: + using Sub = detail::PassBase<DrawCommandBufType>; + + private: + /** Sub-passes referenced by headers. */ + SubPassVector<detail::PassBase<DrawCommandBufType>> sub_passes_main_; + /** Draws are recorded as indirect draws for compatibility with the multi-draw pipeline. */ + DrawCommandBufType draw_commands_buf_main_; + + public: + Pass(const char *name) + : detail::PassBase<DrawCommandBufType>(name, draw_commands_buf_main_, sub_passes_main_){}; + + void init() + { + this->headers_.clear(); + this->commands_.clear(); + this->sub_passes_.clear(); + this->draw_commands_buf_.clear(); + } +}; // namespace blender::draw + +} // namespace detail + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Pass types + * \{ */ + +/** + * Normal pass type. No visibility or draw-call optimisation. + */ +// using PassSimple = detail::Pass<DrawCommandBuf>; + +/** + * Main pass type. + * Optimized for many draw calls and sub-pass. + * + * IMPORTANT: To be used only for passes containing lots of draw calls since it has a potentially + * high overhead due to batching and culling optimizations. + */ +// using PassMain = detail::Pass<DrawMultiBuf>; + +/** + * Special pass type for rendering transparent objects. + * The base level can only be composed of sub passes that will be ordered by a sorting value. + */ +class PassSortable : public PassMain { + friend Manager; + + private: + /** Sorting value associated with each sub pass. */ + Vector<float> sorting_values_; + + bool sorted_ = false; + + public: + PassSortable(const char *name_) : PassMain(name_){}; + + void init() + { + sorting_values_.clear(); + sorted_ = false; + PassMain::init(); + } + + PassMain::Sub &sub(const char *name, float sorting_value) + { + int64_t index = sub_passes_.append_and_get_index( + PassBase(name, draw_commands_buf_, sub_passes_, shader_)); + headers_.append({Type::SubPass, static_cast<uint>(index)}); + sorting_values_.append(sorting_value); + return sub_passes_[index]; + } + + std::string serialize(std::string line_prefix = "") const + { + if (sorted_ == false) { + const_cast<PassSortable *>(this)->sort(); + } + return PassMain::serialize(line_prefix); + } + + protected: + void sort() + { + if (sorted_ == false) { + std::sort(headers_.begin(), headers_.end(), [&](Header &a, Header &b) { + BLI_assert(a.type == Type::SubPass && b.type == Type::SubPass); + float a_val = sorting_values_[a.index]; + float b_val = sorting_values_[b.index]; + return a_val < b_val || (a_val == b_val && a.index < b.index); + }); + sorted_ = true; + } + } +}; + +/** \} */ + +namespace detail { + +/* -------------------------------------------------------------------- */ +/** \name PassBase Implementation + * \{ */ + +template<class T> inline command::Undetermined &PassBase<T>::create_command(command::Type type) +{ + int64_t index = commands_.append_and_get_index({}); + headers_.append({type, static_cast<uint>(index)}); + return commands_[index]; +} + +template<class T> +inline void PassBase<T>::clear(eGPUFrameBufferBits planes, + float4 color, + float depth, + uint8_t stencil) +{ + create_command(command::Type::Clear).clear = {(uint8_t)planes, stencil, depth, color}; +} + +template<class T> inline GPUBatch *PassBase<T>::procedural_batch_get(GPUPrimType primitive) +{ + switch (primitive) { + case GPU_PRIM_POINTS: + return drw_cache_procedural_points_get(); + case GPU_PRIM_LINES: + return drw_cache_procedural_lines_get(); + case GPU_PRIM_TRIS: + return drw_cache_procedural_triangles_get(); + case GPU_PRIM_TRI_STRIP: + return drw_cache_procedural_triangle_strips_get(); + default: + /* Add new one as needed. */ + BLI_assert_unreachable(); + return nullptr; + } +} + +template<class T> inline PassBase<T> &PassBase<T>::sub(const char *name) +{ + int64_t index = sub_passes_.append_and_get_index( + PassBase(name, draw_commands_buf_, sub_passes_, shader_)); + headers_.append({command::Type::SubPass, static_cast<uint>(index)}); + return sub_passes_[index]; +} + +template<class T> void PassBase<T>::submit(command::RecordingState &state) const +{ + GPU_debug_group_begin(debug_name); + + for (const command::Header &header : headers_) { + switch (header.type) { + default: + case Type::None: + break; + case Type::SubPass: + sub_passes_[header.index].submit(state); + break; + case command::Type::ShaderBind: + commands_[header.index].shader_bind.execute(state); + break; + case command::Type::ResourceBind: + commands_[header.index].resource_bind.execute(); + break; + case command::Type::PushConstant: + commands_[header.index].push_constant.execute(state); + break; + case command::Type::Draw: + commands_[header.index].draw.execute(state); + break; + case command::Type::DrawMulti: + commands_[header.index].draw_multi.execute(state); + break; + case command::Type::DrawIndirect: + commands_[header.index].draw_indirect.execute(state); + break; + case command::Type::Dispatch: + commands_[header.index].dispatch.execute(state); + break; + case command::Type::DispatchIndirect: + commands_[header.index].dispatch_indirect.execute(state); + break; + case command::Type::Barrier: + commands_[header.index].barrier.execute(); + break; + case command::Type::Clear: + commands_[header.index].clear.execute(); + break; + case command::Type::StateSet: + commands_[header.index].state_set.execute(state); + break; + case command::Type::StencilSet: + commands_[header.index].stencil_set.execute(); + break; + } + } + + GPU_debug_group_end(); +} + +template<class T> std::string PassBase<T>::serialize(std::string line_prefix) const +{ + std::stringstream ss; + ss << line_prefix << "." << debug_name << std::endl; + line_prefix += " "; + for (const command::Header &header : headers_) { + switch (header.type) { + default: + case Type::None: + break; + case Type::SubPass: + ss << sub_passes_[header.index].serialize(line_prefix); + break; + case Type::ShaderBind: + ss << line_prefix << commands_[header.index].shader_bind.serialize() << std::endl; + break; + case Type::ResourceBind: + ss << line_prefix << commands_[header.index].resource_bind.serialize() << std::endl; + break; + case Type::PushConstant: + ss << line_prefix << commands_[header.index].push_constant.serialize() << std::endl; + break; + case Type::Draw: + ss << line_prefix << commands_[header.index].draw.serialize() << std::endl; + break; + case Type::DrawMulti: + ss << commands_[header.index].draw_multi.serialize(line_prefix); + break; + case Type::DrawIndirect: + ss << line_prefix << commands_[header.index].draw_indirect.serialize() << std::endl; + break; + case Type::Dispatch: + ss << line_prefix << commands_[header.index].dispatch.serialize() << std::endl; + break; + case Type::DispatchIndirect: + ss << line_prefix << commands_[header.index].dispatch_indirect.serialize() << std::endl; + break; + case Type::Barrier: + ss << line_prefix << commands_[header.index].barrier.serialize() << std::endl; + break; + case Type::Clear: + ss << line_prefix << commands_[header.index].clear.serialize() << std::endl; + break; + case Type::StateSet: + ss << line_prefix << commands_[header.index].state_set.serialize() << std::endl; + break; + case Type::StencilSet: + ss << line_prefix << commands_[header.index].stencil_set.serialize() << std::endl; + break; + } + } + return ss.str(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw calls + * \{ */ + +template<class T> +inline void PassBase<T>::draw( + GPUBatch *batch, uint instance_len, uint vertex_len, uint vertex_first, ResourceHandle handle) +{ + if (instance_len == 0 || vertex_len == 0) { + return; + } + BLI_assert(shader_); + draw_commands_buf_.append_draw( + headers_, commands_, batch, instance_len, vertex_len, vertex_first, handle); +} + +template<class T> inline void PassBase<T>::draw(GPUBatch *batch, ResourceHandle handle) +{ + this->draw(batch, -1, -1, -1, handle); +} + +template<class T> +inline void PassBase<T>::draw_procedural(GPUPrimType primitive, + uint instance_len, + uint vertex_len, + uint vertex_first, + ResourceHandle handle) +{ + this->draw(procedural_batch_get(primitive), instance_len, vertex_len, vertex_first, handle); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Indirect draw calls + * \{ */ + +template<class T> +inline void PassBase<T>::draw_indirect(GPUBatch *batch, + StorageBuffer<DrawCommand, true> &indirect_buffer, + ResourceHandle handle) +{ + BLI_assert(shader_); + create_command(Type::DrawIndirect).draw_indirect = {batch, &indirect_buffer, handle}; +} + +template<class T> +inline void PassBase<T>::draw_procedural_indirect( + GPUPrimType primitive, + StorageBuffer<DrawCommand, true> &indirect_buffer, + ResourceHandle handle) +{ + this->draw_indirect(procedural_batch_get(primitive), indirect_buffer, handle); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Compute Dispatch Implementation + * \{ */ + +template<class T> inline void PassBase<T>::dispatch(int3 group_len) +{ + BLI_assert(shader_); + create_command(Type::Dispatch).dispatch = {group_len}; +} + +template<class T> inline void PassBase<T>::dispatch(int3 *group_len) +{ + BLI_assert(shader_); + create_command(Type::Dispatch).dispatch = {group_len}; +} + +template<class T> +inline void PassBase<T>::dispatch(StorageBuffer<DispatchCommand> &indirect_buffer) +{ + BLI_assert(shader_); + create_command(Type::DispatchIndirect).dispatch_indirect = {&indirect_buffer}; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clear Implementation + * \{ */ + +template<class T> inline void PassBase<T>::clear_color(float4 color) +{ + this->clear(GPU_COLOR_BIT, color, 0.0f, 0); +} + +template<class T> inline void PassBase<T>::clear_depth(float depth) +{ + this->clear(GPU_DEPTH_BIT, float4(0.0f), depth, 0); +} + +template<class T> inline void PassBase<T>::clear_stencil(uint8_t stencil) +{ + this->clear(GPU_STENCIL_BIT, float4(0.0f), 0.0f, stencil); +} + +template<class T> inline void PassBase<T>::clear_depth_stencil(float depth, uint8_t stencil) +{ + this->clear(GPU_DEPTH_BIT | GPU_STENCIL_BIT, float4(0.0f), depth, stencil); +} + +template<class T> +inline void PassBase<T>::clear_color_depth_stencil(float4 color, float depth, uint8_t stencil) +{ + this->clear(GPU_DEPTH_BIT | GPU_STENCIL_BIT | GPU_COLOR_BIT, color, depth, stencil); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Barrier Implementation + * \{ */ + +template<class T> inline void PassBase<T>::barrier(eGPUBarrier type) +{ + create_command(Type::Barrier).barrier = {type}; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name State Implementation + * \{ */ + +template<class T> inline void PassBase<T>::state_set(DRWState state) +{ + create_command(Type::StateSet).state_set = {state}; +} + +template<class T> +inline void PassBase<T>::state_stencil(uint8_t write_mask, uint8_t reference, uint8_t compare_mask) +{ + create_command(Type::StencilSet).stencil_set = {write_mask, reference, compare_mask}; +} + +template<class T> inline void PassBase<T>::shader_set(GPUShader *shader) +{ + shader_ = shader; + create_command(Type::ShaderBind).shader_bind = {shader}; +} + +template<class T> inline void PassBase<T>::material_set(Manager &manager, GPUMaterial *material) +{ + GPUPass *gpupass = GPU_material_get_pass(material); + shader_set(GPU_pass_shader_get(gpupass)); + + /* Bind all textures needed by the material. */ + ListBase textures = GPU_material_textures(material); + for (GPUMaterialTexture *tex : ListBaseWrapper<GPUMaterialTexture>(textures)) { + if (tex->ima) { + /* Image */ + ImageUser *iuser = tex->iuser_available ? &tex->iuser : nullptr; + if (tex->tiled_mapping_name[0]) { + GPUTexture *tiles = BKE_image_get_gpu_tiles(tex->ima, iuser, nullptr); + manager.acquire_texture(tiles); + bind_texture(tex->sampler_name, tiles, (eGPUSamplerState)tex->sampler_state); + + GPUTexture *tile_map = BKE_image_get_gpu_tilemap(tex->ima, iuser, nullptr); + manager.acquire_texture(tile_map); + bind_texture(tex->tiled_mapping_name, tile_map, (eGPUSamplerState)tex->sampler_state); + } + else { + GPUTexture *texture = BKE_image_get_gpu_texture(tex->ima, iuser, nullptr); + manager.acquire_texture(texture); + bind_texture(tex->sampler_name, texture, (eGPUSamplerState)tex->sampler_state); + } + } + else if (tex->colorband) { + /* Color Ramp */ + bind_texture(tex->sampler_name, *tex->colorband); + } + } + + GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material); + if (ubo != nullptr) { + bind_ubo(GPU_UBO_BLOCK_NAME, ubo); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Resource bind Implementation + * \{ */ + +template<class T> inline int PassBase<T>::push_constant_offset(const char *name) +{ + return GPU_shader_get_uniform(shader_, name); +} + +template<class T> inline void PassBase<T>::bind_ssbo(const char *name, GPUStorageBuf *buffer) +{ + this->bind_ssbo(GPU_shader_get_ssbo(shader_, name), buffer); +} + +template<class T> inline void PassBase<T>::bind_ubo(const char *name, GPUUniformBuf *buffer) +{ + this->bind_ubo(GPU_shader_get_uniform_block_binding(shader_, name), buffer); +} + +template<class T> +inline void PassBase<T>::bind_texture(const char *name, + GPUTexture *texture, + eGPUSamplerState state) +{ + this->bind_texture(GPU_shader_get_texture_binding(shader_, name), texture, state); +} + +template<class T> inline void PassBase<T>::bind_image(const char *name, GPUTexture *image) +{ + this->bind_texture(GPU_shader_get_texture_binding(shader_, name), image); +} + +template<class T> inline void PassBase<T>::bind_ssbo(int slot, GPUStorageBuf *buffer) +{ + create_command(Type::ResourceBind).resource_bind = {slot, buffer}; +} + +template<class T> inline void PassBase<T>::bind_ubo(int slot, GPUUniformBuf *buffer) +{ + create_command(Type::ResourceBind).resource_bind = {slot, buffer}; +} + +template<class T> +inline void PassBase<T>::bind_texture(int slot, GPUTexture *texture, eGPUSamplerState state) +{ + create_command(Type::ResourceBind).resource_bind = {slot, texture, state}; +} + +template<class T> inline void PassBase<T>::bind_image(int slot, GPUTexture *image) +{ + create_command(Type::ResourceBind).resource_bind = {slot, as_image(image)}; +} + +template<class T> inline void PassBase<T>::bind_ssbo(const char *name, GPUStorageBuf **buffer) +{ + this->bind_ssbo(GPU_shader_get_ssbo(shader_, name), buffer); +} + +template<class T> inline void PassBase<T>::bind_ubo(const char *name, GPUUniformBuf **buffer) +{ + this->bind_ubo(GPU_shader_get_uniform_block_binding(shader_, name), buffer); +} + +template<class T> +inline void PassBase<T>::bind_texture(const char *name, + GPUTexture **texture, + eGPUSamplerState state) +{ + this->bind_texture(GPU_shader_get_texture_binding(shader_, name), texture, state); +} + +template<class T> inline void PassBase<T>::bind_image(const char *name, GPUTexture **image) +{ + this->bind_image(GPU_shader_get_texture_binding(shader_, name), image); +} + +template<class T> inline void PassBase<T>::bind_ssbo(int slot, GPUStorageBuf **buffer) +{ + + create_command(Type::ResourceBind).resource_bind = {slot, buffer}; +} + +template<class T> inline void PassBase<T>::bind_ubo(int slot, GPUUniformBuf **buffer) +{ + create_command(Type::ResourceBind).resource_bind = {slot, buffer}; +} + +template<class T> +inline void PassBase<T>::bind_texture(int slot, GPUTexture **texture, eGPUSamplerState state) +{ + create_command(Type::ResourceBind).resource_bind = {slot, texture, state}; +} + +template<class T> inline void PassBase<T>::bind_image(int slot, GPUTexture **image) +{ + create_command(Type::ResourceBind).resource_bind = {slot, as_image(image)}; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Push Constant Implementation + * \{ */ + +template<class T> inline void PassBase<T>::push_constant(const char *name, const float &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const float2 &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const float3 &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const float4 &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const int &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const int2 &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const int3 &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const int4 &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const bool &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> +inline void PassBase<T>::push_constant(const char *name, const float *data, int array_len) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; +} + +template<class T> +inline void PassBase<T>::push_constant(const char *name, const float2 *data, int array_len) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; +} + +template<class T> +inline void PassBase<T>::push_constant(const char *name, const float3 *data, int array_len) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; +} + +template<class T> +inline void PassBase<T>::push_constant(const char *name, const float4 *data, int array_len) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; +} + +template<class T> +inline void PassBase<T>::push_constant(const char *name, const int *data, int array_len) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; +} + +template<class T> +inline void PassBase<T>::push_constant(const char *name, const int2 *data, int array_len) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; +} + +template<class T> +inline void PassBase<T>::push_constant(const char *name, const int3 *data, int array_len) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; +} + +template<class T> +inline void PassBase<T>::push_constant(const char *name, const int4 *data, int array_len) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const float4x4 *data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const float4x4 &data) +{ + /* WORKAROUND: Push 3 consecutive commands to hold the 64 bytes of the float4x4. + * This assumes that all commands are always stored in flat array of memory. */ + Undetermined commands[3]; + + PushConstant &cmd = commands[0].push_constant; + cmd.location = push_constant_offset(name); + cmd.array_len = 1; + cmd.comp_len = 16; + cmd.type = PushConstant::Type::FloatValue; + /* Copy overrides the next 2 commands. We append them as Type::None to not evaluate them. */ + *reinterpret_cast<float4x4 *>(&cmd.float4_value) = data; + + create_command(Type::PushConstant) = commands[0]; + create_command(Type::None) = commands[1]; + create_command(Type::None) = commands[2]; +} + +/** \} */ + +} // namespace detail + +} // namespace blender::draw diff --git a/source/blender/draw/intern/draw_resource.cc b/source/blender/draw/intern/draw_resource.cc new file mode 100644 index 00000000000..689df4edb31 --- /dev/null +++ b/source/blender/draw/intern/draw_resource.cc @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +/** \file + * \ingroup draw + */ + +#include "DNA_particle_types.h" +#include "RNA_access.h" +#include "RNA_path.h" +#include "RNA_types.h" + +#include "draw_handle.hh" +#include "draw_manager.hh" +#include "draw_shader_shared.h" + +/* -------------------------------------------------------------------- */ +/** \name ObjectAttributes + * \{ */ + +/** + * Extract object attribute from RNA property. + * Returns true if the attribute was correctly extracted. + * This function mirrors lookup_property in cycles/blender/blender_object.cpp + */ +bool ObjectAttribute::id_property_lookup(ID *id, const char *name) +{ + PointerRNA ptr, id_ptr; + PropertyRNA *prop; + + if (id == nullptr) { + return false; + } + + RNA_id_pointer_create(id, &id_ptr); + + if (!RNA_path_resolve(&id_ptr, name, &ptr, &prop)) { + return false; + } + + if (prop == nullptr) { + return false; + } + + PropertyType type = RNA_property_type(prop); + int array_len = RNA_property_array_length(&ptr, prop); + + if (array_len == 0) { + float value; + + if (type == PROP_FLOAT) { + value = RNA_property_float_get(&ptr, prop); + } + else if (type == PROP_INT) { + value = RNA_property_int_get(&ptr, prop); + } + else { + return false; + } + + *reinterpret_cast<float4 *>(&data_x) = float4(value, value, value, 1.0f); + return true; + } + + if (type == PROP_FLOAT && array_len <= 4) { + *reinterpret_cast<float4 *>(&data_x) = float4(0.0f, 0.0f, 0.0f, 1.0f); + RNA_property_float_get_array(&ptr, prop, &data_x); + return true; + } + return false; +} + +/** + * Go through all possible source of the given object uniform attribute. + * Returns true if the attribute was correctly filled. + * This function mirrors lookup_instance_property in cycles/blender/blender_object.cpp + */ +bool ObjectAttribute::sync(const blender::draw::ObjectRef &ref, const GPUUniformAttr &attr) +{ + hash_code = attr.hash_code; + + /* If requesting instance data, check the parent particle system and object. */ + if (attr.use_dupli) { + if ((ref.dupli_object != nullptr) && (ref.dupli_object->particle_system != nullptr)) { + ParticleSettings *settings = ref.dupli_object->particle_system->part; + if (this->id_property_lookup((ID *)settings, attr.name_id_prop) || + this->id_property_lookup((ID *)settings, attr.name)) { + return true; + } + } + if (this->id_property_lookup((ID *)ref.dupli_parent, attr.name_id_prop) || + this->id_property_lookup((ID *)ref.dupli_parent, attr.name)) { + return true; + } + } + + /* Check the object and mesh. */ + if (ref.object != nullptr) { + if (this->id_property_lookup((ID *)ref.object, attr.name_id_prop) || + this->id_property_lookup((ID *)ref.object, attr.name) || + this->id_property_lookup((ID *)ref.object->data, attr.name_id_prop) || + this->id_property_lookup((ID *)ref.object->data, attr.name)) { + return true; + } + } + return false; +} + +/** \} */ diff --git a/source/blender/draw/intern/draw_resource.hh b/source/blender/draw/intern/draw_resource.hh new file mode 100644 index 00000000000..22ee43592a9 --- /dev/null +++ b/source/blender/draw/intern/draw_resource.hh @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +#pragma once + +/** \file + * \ingroup draw + * + * Component / Object level resources like object attributes, matrices, visibility etc... + * Each of them are reference by resource index (#ResourceHandle). + */ + +#include "BKE_curve.h" +#include "BKE_duplilist.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +#include "BKE_volume.h" +#include "BLI_hash.h" +#include "DNA_curve_types.h" +#include "DNA_layer_types.h" +#include "DNA_meta_types.h" +#include "DNA_object_types.h" + +#include "draw_handle.hh" +#include "draw_manager.hh" +#include "draw_shader_shared.h" + +/* -------------------------------------------------------------------- */ +/** \name ObjectMatrices + * \{ */ + +inline void ObjectMatrices::sync(const Object &object) +{ + model = object.obmat; + model_inverse = object.imat; +} + +inline void ObjectMatrices::sync(const float4x4 &model_matrix) +{ + model = model_matrix; + model_inverse = model_matrix.inverted(); +} + +inline std::ostream &operator<<(std::ostream &stream, const ObjectMatrices &matrices) +{ + stream << "ObjectMatrices(" << std::endl; + stream << "model=" << matrices.model << ", " << std::endl; + stream << "model_inverse=" << matrices.model_inverse << ")" << std::endl; + return stream; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name ObjectInfos + * \{ */ + +ENUM_OPERATORS(eObjectInfoFlag, OBJECT_NEGATIVE_SCALE) + +inline void ObjectInfos::sync() +{ + object_attrs_len = 0; + object_attrs_offset = 0; + + flag = eObjectInfoFlag::OBJECT_NO_INFO; +} + +inline void ObjectInfos::sync(const blender::draw::ObjectRef ref, bool is_active_object) +{ + object_attrs_len = 0; + object_attrs_offset = 0; + + color = ref.object->color; + index = ref.object->index; + SET_FLAG_FROM_TEST(flag, is_active_object, eObjectInfoFlag::OBJECT_ACTIVE); + SET_FLAG_FROM_TEST( + flag, ref.object->base_flag & BASE_SELECTED, eObjectInfoFlag::OBJECT_SELECTED); + SET_FLAG_FROM_TEST( + flag, ref.object->base_flag & BASE_FROM_DUPLI, eObjectInfoFlag::OBJECT_FROM_DUPLI); + SET_FLAG_FROM_TEST( + flag, ref.object->base_flag & BASE_FROM_SET, eObjectInfoFlag::OBJECT_FROM_SET); + SET_FLAG_FROM_TEST( + flag, ref.object->transflag & OB_NEG_SCALE, eObjectInfoFlag::OBJECT_NEGATIVE_SCALE); + + if (ref.dupli_object == nullptr) { + /* TODO(fclem): this is rather costly to do at draw time. Maybe we can + * put it in ob->runtime and make depsgraph ensure it is up to date. */ + random = BLI_hash_int_2d(BLI_hash_string(ref.object->id.name + 2), 0) * (1.0f / 0xFFFFFFFF); + } + else { + random = ref.dupli_object->random_id * (1.0f / 0xFFFFFFFF); + } + /* Default values. Set if needed. */ + random = 0.0f; + + if (ref.object->data == nullptr) { + orco_add = float3(0.0f); + orco_mul = float3(1.0f); + return; + } + + switch (GS(reinterpret_cast<ID *>(ref.object->data)->name)) { + case ID_VO: { + BoundBox &bbox = *BKE_volume_boundbox_get(ref.object); + orco_add = (float3(bbox.vec[6]) + float3(bbox.vec[0])) * 0.5f; /* Center. */ + orco_mul = float3(bbox.vec[6]) - float3(bbox.vec[0]); /* Size. */ + break; + } + case ID_ME: { + BKE_mesh_texspace_get(static_cast<Mesh *>(ref.object->data), orco_add, orco_mul); + break; + } + case ID_CU_LEGACY: { + Curve &cu = *static_cast<Curve *>(ref.object->data); + BKE_curve_texspace_ensure(&cu); + orco_add = cu.loc; + orco_mul = cu.size; + break; + } + case ID_MB: { + MetaBall &mb = *static_cast<MetaBall *>(ref.object->data); + orco_add = mb.loc; + orco_mul = mb.size; + break; + } + default: + orco_add = float3(0.0f); + orco_mul = float3(1.0f); + break; + } +} + +inline std::ostream &operator<<(std::ostream &stream, const ObjectInfos &infos) +{ + stream << "ObjectInfos("; + if (infos.flag == eObjectInfoFlag::OBJECT_NO_INFO) { + stream << "skipped)" << std::endl; + return stream; + } + stream << "orco_add=" << infos.orco_add << ", "; + stream << "orco_mul=" << infos.orco_mul << ", "; + stream << "color=" << infos.color << ", "; + stream << "index=" << infos.index << ", "; + stream << "random=" << infos.random << ", "; + stream << "flag=" << infos.flag << ")" << std::endl; + return stream; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name ObjectBounds + * \{ */ + +inline void ObjectBounds::sync() +{ + bounding_sphere.w = -1.0f; /* Disable test. */ +} + +inline void ObjectBounds::sync(Object &ob) +{ + const BoundBox *bbox = BKE_object_boundbox_get(&ob); + if (bbox == nullptr) { + bounding_sphere.w = -1.0f; /* Disable test. */ + return; + } + *reinterpret_cast<float3 *>(&bounding_corners[0]) = bbox->vec[0]; + *reinterpret_cast<float3 *>(&bounding_corners[1]) = bbox->vec[4]; + *reinterpret_cast<float3 *>(&bounding_corners[2]) = bbox->vec[3]; + *reinterpret_cast<float3 *>(&bounding_corners[3]) = bbox->vec[1]; + bounding_sphere.w = 0.0f; /* Enable test. */ +} + +inline void ObjectBounds::sync(const float3 ¢er, const float3 &size) +{ + *reinterpret_cast<float3 *>(&bounding_corners[0]) = center - size; + *reinterpret_cast<float3 *>(&bounding_corners[1]) = center + float3(+size.x, -size.y, -size.z); + *reinterpret_cast<float3 *>(&bounding_corners[2]) = center + float3(-size.x, +size.y, -size.z); + *reinterpret_cast<float3 *>(&bounding_corners[3]) = center + float3(-size.x, -size.y, +size.z); + bounding_sphere.w = 0.0; /* Enable test. */ +} + +inline std::ostream &operator<<(std::ostream &stream, const ObjectBounds &bounds) +{ + stream << "ObjectBounds("; + if (bounds.bounding_sphere.w == -1.0f) { + stream << "skipped)" << std::endl; + return stream; + } + stream << std::endl; + stream << ".bounding_corners[0]" + << *reinterpret_cast<const float3 *>(&bounds.bounding_corners[0]) << std::endl; + stream << ".bounding_corners[1]" + << *reinterpret_cast<const float3 *>(&bounds.bounding_corners[1]) << std::endl; + stream << ".bounding_corners[2]" + << *reinterpret_cast<const float3 *>(&bounds.bounding_corners[2]) << std::endl; + stream << ".bounding_corners[3]" + << *reinterpret_cast<const float3 *>(&bounds.bounding_corners[3]) << std::endl; + stream << ".sphere=(pos=" << float3(bounds.bounding_sphere) + << ", rad=" << bounds.bounding_sphere.w << std::endl; + stream << ")" << std::endl; + return stream; +} + +/** \} */ diff --git a/source/blender/draw/intern/draw_shader.cc b/source/blender/draw/intern/draw_shader.cc index ecb30d54b64..960348b4a94 100644 --- a/source/blender/draw/intern/draw_shader.cc +++ b/source/blender/draw/intern/draw_shader.cc @@ -17,15 +17,15 @@ #include "draw_shader.h" extern "C" char datatoc_common_hair_lib_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]; struct GPUShader *debug_print_display_sh; struct GPUShader *debug_draw_display_sh; + struct GPUShader *draw_visibility_compute_sh; + struct GPUShader *draw_resource_finalize_sh; + struct GPUShader *draw_command_generate_sh; } e_data = {{nullptr}}; /* -------------------------------------------------------------------- */ @@ -127,6 +127,31 @@ GPUShader *DRW_shader_debug_draw_display_get() return e_data.debug_draw_display_sh; } +GPUShader *DRW_shader_draw_visibility_compute_get() +{ + if (e_data.draw_visibility_compute_sh == nullptr) { + e_data.draw_visibility_compute_sh = GPU_shader_create_from_info_name( + "draw_visibility_compute"); + } + return e_data.draw_visibility_compute_sh; +} + +GPUShader *DRW_shader_draw_resource_finalize_get() +{ + if (e_data.draw_resource_finalize_sh == nullptr) { + e_data.draw_resource_finalize_sh = GPU_shader_create_from_info_name("draw_resource_finalize"); + } + return e_data.draw_resource_finalize_sh; +} + +GPUShader *DRW_shader_draw_command_generate_get() +{ + if (e_data.draw_command_generate_sh == nullptr) { + e_data.draw_command_generate_sh = GPU_shader_create_from_info_name("draw_command_generate"); + } + return e_data.draw_command_generate_sh; +} + /** \} */ void DRW_shaders_free() @@ -136,4 +161,7 @@ void DRW_shaders_free() } DRW_SHADER_FREE_SAFE(e_data.debug_print_display_sh); DRW_SHADER_FREE_SAFE(e_data.debug_draw_display_sh); + DRW_SHADER_FREE_SAFE(e_data.draw_visibility_compute_sh); + DRW_SHADER_FREE_SAFE(e_data.draw_resource_finalize_sh); + DRW_SHADER_FREE_SAFE(e_data.draw_command_generate_sh); } diff --git a/source/blender/draw/intern/draw_shader.h b/source/blender/draw/intern/draw_shader.h index dabb4b3327f..3b8c0425fa9 100644 --- a/source/blender/draw/intern/draw_shader.h +++ b/source/blender/draw/intern/draw_shader.h @@ -32,6 +32,9 @@ struct GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, struct GPUShader *DRW_shader_debug_print_display_get(void); struct GPUShader *DRW_shader_debug_draw_display_get(void); +struct GPUShader *DRW_shader_draw_visibility_compute_get(void); +struct GPUShader *DRW_shader_draw_resource_finalize_get(void); +struct GPUShader *DRW_shader_draw_command_generate_get(void); void DRW_shaders_free(void); diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index 90a6475c42b..d43bfe6b159 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -5,18 +5,38 @@ # include "GPU_shader.h" # include "GPU_shader_shared_utils.h" +# include "draw_defines.h" typedef struct ViewInfos ViewInfos; typedef struct ObjectMatrices ObjectMatrices; typedef struct ObjectInfos ObjectInfos; +typedef struct ObjectBounds ObjectBounds; typedef struct VolumeInfos VolumeInfos; typedef struct CurvesInfos CurvesInfos; +typedef struct ObjectAttribute ObjectAttribute; typedef struct DrawCommand DrawCommand; -typedef struct DrawCommandIndexed DrawCommandIndexed; typedef struct DispatchCommand DispatchCommand; typedef struct DRWDebugPrintBuffer DRWDebugPrintBuffer; typedef struct DRWDebugVert DRWDebugVert; typedef struct DRWDebugDrawBuffer DRWDebugDrawBuffer; + +# ifdef __cplusplus +/* C++ only forward declarations. */ +struct Object; +struct ID; +struct GPUUniformAttr; + +namespace blender::draw { + +struct ObjectRef; + +} // namespace blender::draw + +# else /* __cplusplus */ +/* C only forward declarations. */ +typedef enum eObjectInfoFlag eObjectInfoFlag; + +# endif #endif #define DRW_SHADER_SHARED_H @@ -48,15 +68,18 @@ struct ViewInfos { float2 viewport_size_inverse; /** Frustum culling data. */ - /** NOTE: vec3 arrays are padded to vec4. */ + /** \note vec3 array padded to vec4. */ float4 frustum_corners[8]; float4 frustum_planes[6]; + float4 frustum_bound_sphere; /** For debugging purpose */ /* Mouse pixel. */ int2 mouse_pixel; - int2 _pad0; + /** True if facing needs to be inverted. */ + bool1 is_inverted; + int _pad0; }; BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16) @@ -74,23 +97,89 @@ BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16) # define CameraTexCoFactors drw_view.viewcamtexcofac #endif +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Debug draw shapes + * \{ */ + struct ObjectMatrices { - float4x4 drw_modelMatrix; - float4x4 drw_modelMatrixInverse; + float4x4 model; + float4x4 model_inverse; + +#if !defined(GPU_SHADER) && defined(__cplusplus) + void sync(const Object &object); + void sync(const float4x4 &model_matrix); +#endif +}; +BLI_STATIC_ASSERT_ALIGN(ObjectMatrices, 16) + +enum eObjectInfoFlag { + OBJECT_SELECTED = (1u << 0u), + OBJECT_FROM_DUPLI = (1u << 1u), + OBJECT_FROM_SET = (1u << 2u), + OBJECT_ACTIVE = (1u << 3u), + OBJECT_NEGATIVE_SCALE = (1u << 4u), + /* Avoid skipped info to change culling. */ + OBJECT_NO_INFO = ~OBJECT_NEGATIVE_SCALE }; -BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16) struct ObjectInfos { - float4 drw_OrcoTexCoFactors[2]; - float4 drw_ObjectColor; - float4 drw_Infos; +#if defined(GPU_SHADER) && !defined(DRAW_FINALIZE_SHADER) + /* TODO Rename to struct member for glsl too. */ + float4 orco_mul_bias[2]; + float4 color; + float4 infos; +#else + /** Uploaded as center + size. Converted to mul+bias to local coord. */ + float3 orco_add; + uint object_attrs_offset; + float3 orco_mul; + uint object_attrs_len; + + float4 color; + uint index; + uint _pad2; + float random; + eObjectInfoFlag flag; +#endif + +#if !defined(GPU_SHADER) && defined(__cplusplus) + void sync(); + void sync(const blender::draw::ObjectRef ref, bool is_active_object); +#endif }; -BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16) +BLI_STATIC_ASSERT_ALIGN(ObjectInfos, 16) + +struct ObjectBounds { + /** + * Uploaded as vertex (0, 4, 3, 1) of the bbox in local space, matching XYZ axis order. + * Then processed by GPU and stored as (0, 4-0, 3-0, 1-0) in world space for faster culling. + */ + float4 bounding_corners[4]; + /** Bounding sphere derived from the bounding corner. Computed on GPU. */ + float4 bounding_sphere; + /** Radius of the inscribed sphere derived from the bounding corner. Computed on GPU. */ +#define _inner_sphere_radius bounding_corners[3].w + +#if !defined(GPU_SHADER) && defined(__cplusplus) + void sync(); + void sync(Object &ob); + void sync(const float3 ¢er, const float3 &size); +#endif +}; +BLI_STATIC_ASSERT_ALIGN(ObjectBounds, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object attributes + * \{ */ struct VolumeInfos { - /* Object to grid-space. */ + /** Object to grid-space. */ float4x4 grids_xform[DRW_GRID_PER_VOLUME_MAX]; - /* NOTE: vec4 for alignment. Only float3 needed. */ + /** \note vec4 for alignment. Only float3 needed. */ float4 color_mul; float density_scale; float temperature_mul; @@ -100,38 +189,58 @@ 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. - * NOTE: GLSL pad arrays of scalar to 16 bytes (std140). */ + /** Per attribute scope, follows loading order. + * \note uint as bool in GLSL is 4 bytes. + * \note GLSL pad arrays of scalar to 16 bytes (std140). */ uint4 is_point_attribute[DRW_ATTRIBUTE_PER_CURVES_MAX]; }; 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) - -/* Indirect commands structures. */ +#pragma pack(push, 4) +struct ObjectAttribute { + /* Workaround the padding cost from alignment requirements. + * (see GL spec : 7.6.2.2 Standard Uniform Block Layout) */ + float data_x, data_y, data_z, data_w; + uint hash_code; -struct DrawCommand { - uint v_count; - uint i_count; - uint v_first; - uint i_first; +#if !defined(GPU_SHADER) && defined(__cplusplus) + bool sync(const blender::draw::ObjectRef &ref, const GPUUniformAttr &attr); + bool id_property_lookup(ID *id, const char *name); +#endif }; -BLI_STATIC_ASSERT_ALIGN(DrawCommand, 16) +#pragma pack(pop) +/** \note we only align to 4 bytes and fetch data manually so make sure + * C++ compiler gives us the same size. */ +BLI_STATIC_ASSERT_ALIGN(ObjectAttribute, 20) -struct DrawCommandIndexed { - uint v_count; - uint i_count; - uint v_first; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Indirect commands structures. + * \{ */ + +struct DrawCommand { + /* TODO(fclem): Rename */ + uint vertex_len; + uint instance_len; + uint vertex_first; +#if defined(GPU_SHADER) uint base_index; - uint i_first; - uint _pad0; - uint _pad1; - uint _pad2; + /** \note base_index is i_first for non-indexed draw-calls. */ +# define _instance_first_array base_index +#else + union { + uint base_index; + /* Use this instead of instance_first_indexed for non indexed draw calls. */ + uint instance_first_array; + }; +#endif + + uint instance_first_indexed; + + uint _pad0, _pad1, _pad2; }; -BLI_STATIC_ASSERT_ALIGN(DrawCommandIndexed, 16) +BLI_STATIC_ASSERT_ALIGN(DrawCommand, 16) struct DispatchCommand { uint num_groups_x; @@ -141,13 +250,15 @@ struct DispatchCommand { }; BLI_STATIC_ASSERT_ALIGN(DispatchCommand, 16) +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Debug print * \{ */ /* Take the header (DrawCommand) into account. */ #define DRW_DEBUG_PRINT_MAX (8 * 1024) - 4 -/* NOTE: Cannot be more than 255 (because of column encoding). */ +/** \note Cannot be more than 255 (because of column encoding). */ #define DRW_DEBUG_PRINT_WORD_WRAP_COLUMN 120u /* The debug print buffer is laid-out as the following struct. @@ -164,6 +275,9 @@ BLI_STATIC_ASSERT_ALIGN(DRWDebugPrintBuffer, 16) /* Reuse first instance as row index as we don't use instancing. Equivalent to * `DRWDebugPrintBuffer.command.i_first`. */ #define drw_debug_print_row_shared drw_debug_print_buf[3] +/** Offset to the first data. Equal to: sizeof(DrawCommand) / sizeof(uint). + * This is needed because we bind the whole buffer as a `uint` array. */ +#define drw_debug_print_offset 8 /** \} */ @@ -194,5 +308,8 @@ BLI_STATIC_ASSERT_ALIGN(DRWDebugPrintBuffer, 16) /* Equivalent to `DRWDebugDrawBuffer.command.v_count`. */ #define drw_debug_draw_v_count drw_debug_verts_buf[0].pos0 +/** Offset to the first data. Equal to: sizeof(DrawCommand) / sizeof(DRWDebugVert). + * This is needed because we bind the whole buffer as a `DRWDebugVert` array. */ +#define drw_debug_draw_offset 2 /** \} */ diff --git a/source/blender/draw/intern/draw_state.h b/source/blender/draw/intern/draw_state.h new file mode 100644 index 00000000000..bf1e63e0852 --- /dev/null +++ b/source/blender/draw/intern/draw_state.h @@ -0,0 +1,225 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file + * \ingroup draw + * + * Internal Pipeline State tracking. It is higher level than GPU state as everything fits a single + * enum. + */ + +/** + * DRWState is a bit-mask that stores the current render state and the desired render state. Based + * on the differences the minimum state changes can be invoked to setup the desired render state. + * + * The Write Stencil, Stencil test, Depth test and Blend state options are mutual exclusive + * 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), + /* Write Stencil. These options are mutual exclusive and packed into 2 bits */ + DRW_STATE_WRITE_STENCIL = (1 << 2), + DRW_STATE_WRITE_STENCIL_SHADOW_PASS = (2 << 2), + DRW_STATE_WRITE_STENCIL_SHADOW_FAIL = (3 << 2), + /** Depth test. These options are mutual exclusive and packed into 3 bits */ + DRW_STATE_DEPTH_ALWAYS = (1 << 4), + DRW_STATE_DEPTH_LESS = (2 << 4), + DRW_STATE_DEPTH_LESS_EQUAL = (3 << 4), + DRW_STATE_DEPTH_EQUAL = (4 << 4), + DRW_STATE_DEPTH_GREATER = (5 << 4), + DRW_STATE_DEPTH_GREATER_EQUAL = (6 << 4), + /** Culling test */ + DRW_STATE_CULL_BACK = (1 << 7), + DRW_STATE_CULL_FRONT = (1 << 8), + /** Stencil test. These options are mutually exclusive and packed into 2 bits. */ + DRW_STATE_STENCIL_ALWAYS = (1 << 9), + DRW_STATE_STENCIL_EQUAL = (2 << 9), + DRW_STATE_STENCIL_NEQUAL = (3 << 9), + + /** Blend state. These options are mutual exclusive and packed into 4 bits */ + DRW_STATE_BLEND_ADD = (1 << 11), + /** Same as additive but let alpha accumulate without pre-multiply. */ + DRW_STATE_BLEND_ADD_FULL = (2 << 11), + /** Standard alpha blending. */ + DRW_STATE_BLEND_ALPHA = (3 << 11), + /** Use that if color is already pre-multiply by alpha. */ + DRW_STATE_BLEND_ALPHA_PREMUL = (4 << 11), + DRW_STATE_BLEND_BACKGROUND = (5 << 11), + DRW_STATE_BLEND_OIT = (6 << 11), + DRW_STATE_BLEND_MUL = (7 << 11), + DRW_STATE_BLEND_SUB = (8 << 11), + /** Use dual source blending. WARNING: Only one color buffer allowed. */ + DRW_STATE_BLEND_CUSTOM = (9 << 11), + DRW_STATE_LOGIC_INVERT = (10 << 11), + DRW_STATE_BLEND_ALPHA_UNDER_PREMUL = (11 << 11), + + DRW_STATE_IN_FRONT_SELECT = (1 << 27), + DRW_STATE_SHADOW_OFFSET = (1 << 28), + DRW_STATE_CLIP_PLANES = (1 << 29), + DRW_STATE_FIRST_VERTEX_CONVENTION = (1 << 30), + /** DO NOT USE. Assumed always enabled. Only used internally. */ + DRW_STATE_PROGRAM_POINT_SIZE = (1u << 31), +} DRWState; + +ENUM_OPERATORS(DRWState, DRW_STATE_PROGRAM_POINT_SIZE); + +#define DRW_STATE_DEFAULT \ + (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL) +#define DRW_STATE_BLEND_ENABLED \ + (DRW_STATE_BLEND_ADD | DRW_STATE_BLEND_ADD_FULL | DRW_STATE_BLEND_ALPHA | \ + DRW_STATE_BLEND_ALPHA_PREMUL | DRW_STATE_BLEND_BACKGROUND | DRW_STATE_BLEND_OIT | \ + DRW_STATE_BLEND_MUL | DRW_STATE_BLEND_SUB | DRW_STATE_BLEND_CUSTOM | DRW_STATE_LOGIC_INVERT) +#define DRW_STATE_RASTERIZER_ENABLED \ + (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_STENCIL | \ + DRW_STATE_WRITE_STENCIL_SHADOW_PASS | DRW_STATE_WRITE_STENCIL_SHADOW_FAIL) +#define DRW_STATE_DEPTH_TEST_ENABLED \ + (DRW_STATE_DEPTH_ALWAYS | DRW_STATE_DEPTH_LESS | DRW_STATE_DEPTH_LESS_EQUAL | \ + DRW_STATE_DEPTH_EQUAL | DRW_STATE_DEPTH_GREATER | DRW_STATE_DEPTH_GREATER_EQUAL) +#define DRW_STATE_STENCIL_TEST_ENABLED \ + (DRW_STATE_STENCIL_ALWAYS | DRW_STATE_STENCIL_EQUAL | DRW_STATE_STENCIL_NEQUAL) +#define DRW_STATE_WRITE_STENCIL_ENABLED \ + (DRW_STATE_WRITE_STENCIL | DRW_STATE_WRITE_STENCIL_SHADOW_PASS | \ + DRW_STATE_WRITE_STENCIL_SHADOW_FAIL) + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus + +namespace blender::draw { + +/* -------------------------------------------------------------------- */ +/** \name DRWState to GPU state conversion + * \{ */ + +static inline eGPUWriteMask to_write_mask(DRWState state) +{ + eGPUWriteMask write_mask = GPU_WRITE_NONE; + if (state & DRW_STATE_WRITE_DEPTH) { + write_mask |= GPU_WRITE_DEPTH; + } + if (state & DRW_STATE_WRITE_COLOR) { + write_mask |= GPU_WRITE_COLOR; + } + if (state & DRW_STATE_WRITE_STENCIL_ENABLED) { + write_mask |= GPU_WRITE_STENCIL; + } + return write_mask; +} + +static inline eGPUFaceCullTest to_face_cull_test(DRWState state) +{ + switch (state & (DRW_STATE_CULL_BACK | DRW_STATE_CULL_FRONT)) { + case DRW_STATE_CULL_BACK: + return GPU_CULL_BACK; + case DRW_STATE_CULL_FRONT: + return GPU_CULL_FRONT; + default: + return GPU_CULL_NONE; + } +} + +static inline eGPUDepthTest to_depth_test(DRWState state) +{ + switch (state & DRW_STATE_DEPTH_TEST_ENABLED) { + case DRW_STATE_DEPTH_LESS: + return GPU_DEPTH_LESS; + case DRW_STATE_DEPTH_LESS_EQUAL: + return GPU_DEPTH_LESS_EQUAL; + case DRW_STATE_DEPTH_EQUAL: + return GPU_DEPTH_EQUAL; + case DRW_STATE_DEPTH_GREATER: + return GPU_DEPTH_GREATER; + case DRW_STATE_DEPTH_GREATER_EQUAL: + return GPU_DEPTH_GREATER_EQUAL; + case DRW_STATE_DEPTH_ALWAYS: + return GPU_DEPTH_ALWAYS; + default: + return GPU_DEPTH_NONE; + } +} + +static inline eGPUStencilOp to_stencil_op(DRWState state) +{ + switch (state & DRW_STATE_WRITE_STENCIL_ENABLED) { + case DRW_STATE_WRITE_STENCIL: + return GPU_STENCIL_OP_REPLACE; + case DRW_STATE_WRITE_STENCIL_SHADOW_PASS: + return GPU_STENCIL_OP_COUNT_DEPTH_PASS; + case DRW_STATE_WRITE_STENCIL_SHADOW_FAIL: + return GPU_STENCIL_OP_COUNT_DEPTH_FAIL; + default: + return GPU_STENCIL_OP_NONE; + } +} + +static inline eGPUStencilTest to_stencil_test(DRWState state) +{ + switch (state & DRW_STATE_STENCIL_TEST_ENABLED) { + case DRW_STATE_STENCIL_ALWAYS: + return GPU_STENCIL_ALWAYS; + case DRW_STATE_STENCIL_EQUAL: + return GPU_STENCIL_EQUAL; + case DRW_STATE_STENCIL_NEQUAL: + return GPU_STENCIL_NEQUAL; + default: + return GPU_STENCIL_NONE; + } +} + +static inline eGPUBlend to_blend(DRWState state) +{ + switch (state & DRW_STATE_BLEND_ENABLED) { + case DRW_STATE_BLEND_ADD: + return GPU_BLEND_ADDITIVE; + case DRW_STATE_BLEND_ADD_FULL: + return GPU_BLEND_ADDITIVE_PREMULT; + case DRW_STATE_BLEND_ALPHA: + return GPU_BLEND_ALPHA; + case DRW_STATE_BLEND_ALPHA_PREMUL: + return GPU_BLEND_ALPHA_PREMULT; + case DRW_STATE_BLEND_BACKGROUND: + return GPU_BLEND_BACKGROUND; + case DRW_STATE_BLEND_OIT: + return GPU_BLEND_OIT; + case DRW_STATE_BLEND_MUL: + return GPU_BLEND_MULTIPLY; + case DRW_STATE_BLEND_SUB: + return GPU_BLEND_SUBTRACT; + case DRW_STATE_BLEND_CUSTOM: + return GPU_BLEND_CUSTOM; + case DRW_STATE_LOGIC_INVERT: + return GPU_BLEND_INVERT; + case DRW_STATE_BLEND_ALPHA_UNDER_PREMUL: + return GPU_BLEND_ALPHA_UNDER_PREMUL; + default: + return GPU_BLEND_NONE; + } +} + +static inline eGPUProvokingVertex to_provoking_vertex(DRWState state) +{ + switch (state & DRW_STATE_FIRST_VERTEX_CONVENTION) { + case DRW_STATE_FIRST_VERTEX_CONVENTION: + return GPU_VERTEX_FIRST; + default: + return GPU_VERTEX_LAST; + } +} + +/** \} */ + +}; // namespace blender::draw + +#endif diff --git a/source/blender/draw/intern/draw_view.cc b/source/blender/draw/intern/draw_view.cc new file mode 100644 index 00000000000..326e8629e52 --- /dev/null +++ b/source/blender/draw/intern/draw_view.cc @@ -0,0 +1,332 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +/** \file + * \ingroup draw + */ + +#include "BLI_math_geom.h" +#include "GPU_compute.h" +#include "GPU_debug.h" + +#include "draw_debug.hh" +#include "draw_shader.h" +#include "draw_view.hh" + +namespace blender::draw { + +void View::sync(const float4x4 &view_mat, const float4x4 &win_mat) +{ + data_.viewmat = view_mat; + data_.viewinv = view_mat.inverted(); + data_.winmat = win_mat; + data_.wininv = win_mat.inverted(); + data_.persmat = data_.winmat * data_.viewmat; + data_.persinv = data_.persmat.inverted(); + /* Should not be used anymore. */ + data_.viewcamtexcofac = float4(1.0f, 1.0f, 0.0f, 0.0f); + + data_.is_inverted = (is_negative_m4(view_mat.ptr()) == is_negative_m4(win_mat.ptr())); + + update_view_vectors(); + + BoundBox &bound_box = *reinterpret_cast<BoundBox *>(&data_.frustum_corners); + BoundSphere &bound_sphere = *reinterpret_cast<BoundSphere *>(&data_.frustum_bound_sphere); + frustum_boundbox_calc(bound_box); + frustum_culling_planes_calc(); + frustum_culling_sphere_calc(bound_box, bound_sphere); + + dirty_ = true; +} + +void View::frustum_boundbox_calc(BoundBox &bbox) +{ + /* Extract the 8 corners from a Projection Matrix. */ +#if 0 /* Equivalent to this but it has accuracy problems. */ + BKE_boundbox_init_from_minmax(&bbox, float3(-1.0f),float3(1.0f)); + for (int i = 0; i < 8; i++) { + mul_project_m4_v3(data_.wininv.ptr(), bbox.vec[i]); + } +#endif + + float left, right, bottom, top, near, far; + bool is_persp = data_.winmat[3][3] == 0.0f; + + projmat_dimensions(data_.winmat.ptr(), &left, &right, &bottom, &top, &near, &far); + + bbox.vec[0][2] = bbox.vec[3][2] = bbox.vec[7][2] = bbox.vec[4][2] = -near; + bbox.vec[0][0] = bbox.vec[3][0] = left; + bbox.vec[4][0] = bbox.vec[7][0] = right; + bbox.vec[0][1] = bbox.vec[4][1] = bottom; + bbox.vec[7][1] = bbox.vec[3][1] = top; + + /* Get the coordinates of the far plane. */ + if (is_persp) { + float sca_far = far / near; + left *= sca_far; + right *= sca_far; + bottom *= sca_far; + top *= sca_far; + } + + bbox.vec[1][2] = bbox.vec[2][2] = bbox.vec[6][2] = bbox.vec[5][2] = -far; + bbox.vec[1][0] = bbox.vec[2][0] = left; + bbox.vec[6][0] = bbox.vec[5][0] = right; + bbox.vec[1][1] = bbox.vec[5][1] = bottom; + bbox.vec[2][1] = bbox.vec[6][1] = top; + + /* Transform into world space. */ + for (int i = 0; i < 8; i++) { + mul_m4_v3(data_.viewinv.ptr(), bbox.vec[i]); + } +} + +void View::frustum_culling_planes_calc() +{ + planes_from_projmat(data_.persmat.ptr(), + data_.frustum_planes[0], + data_.frustum_planes[5], + data_.frustum_planes[1], + data_.frustum_planes[3], + data_.frustum_planes[4], + data_.frustum_planes[2]); + + /* Normalize. */ + for (int p = 0; p < 6; p++) { + data_.frustum_planes[p].w /= normalize_v3(data_.frustum_planes[p]); + } +} + +void View::frustum_culling_sphere_calc(const BoundBox &bbox, BoundSphere &bsphere) +{ + /* Extract Bounding Sphere */ + if (data_.winmat[3][3] != 0.0f) { + /* Orthographic */ + /* The most extreme points on the near and far plane. (normalized device coords). */ + const float *nearpoint = bbox.vec[0]; + const float *farpoint = bbox.vec[6]; + + /* just use median point */ + mid_v3_v3v3(bsphere.center, farpoint, nearpoint); + bsphere.radius = len_v3v3(bsphere.center, farpoint); + } + else if (data_.winmat[2][0] == 0.0f && data_.winmat[2][1] == 0.0f) { + /* Perspective with symmetrical frustum. */ + + /* We obtain the center and radius of the circumscribed circle of the + * isosceles trapezoid composed by the diagonals of the near and far clipping plane */ + + /* center of each clipping plane */ + float mid_min[3], mid_max[3]; + mid_v3_v3v3(mid_min, bbox.vec[3], bbox.vec[4]); + mid_v3_v3v3(mid_max, bbox.vec[2], bbox.vec[5]); + + /* square length of the diagonals of each clipping plane */ + float a_sq = len_squared_v3v3(bbox.vec[3], bbox.vec[4]); + float b_sq = len_squared_v3v3(bbox.vec[2], bbox.vec[5]); + + /* distance squared between clipping planes */ + float h_sq = len_squared_v3v3(mid_min, mid_max); + + float fac = (4 * h_sq + b_sq - a_sq) / (8 * h_sq); + + /* The goal is to get the smallest sphere, + * not the sphere that passes through each corner */ + CLAMP(fac, 0.0f, 1.0f); + + interp_v3_v3v3(bsphere.center, mid_min, mid_max, fac); + + /* distance from the center to one of the points of the far plane (1, 2, 5, 6) */ + bsphere.radius = len_v3v3(bsphere.center, bbox.vec[1]); + } + else { + /* Perspective with asymmetrical frustum. */ + + /* We put the sphere center on the line that goes from origin + * to the center of the far clipping plane. */ + + /* Detect which of the corner of the far clipping plane is the farthest to the origin */ + float nfar[4]; /* most extreme far point in NDC space */ + float farxy[2]; /* far-point projection onto the near plane */ + float farpoint[3] = {0.0f}; /* most extreme far point in camera coordinate */ + float nearpoint[3]; /* most extreme near point in camera coordinate */ + float farcenter[3] = {0.0f}; /* center of far clipping plane in camera coordinate */ + float F = -1.0f, N; /* square distance of far and near point to origin */ + float f, n; /* distance of far and near point to z axis. f is always > 0 but n can be < 0 */ + float e, s; /* far and near clipping distance (<0) */ + float c; /* slope of center line = distance of far clipping center + * to z axis / far clipping distance. */ + float z; /* projection of sphere center on z axis (<0) */ + + /* Find farthest corner and center of far clip plane. */ + float corner[3] = {1.0f, 1.0f, 1.0f}; /* in clip space */ + for (int i = 0; i < 4; i++) { + float point[3]; + mul_v3_project_m4_v3(point, data_.wininv.ptr(), corner); + float len = len_squared_v3(point); + if (len > F) { + copy_v3_v3(nfar, corner); + copy_v3_v3(farpoint, point); + F = len; + } + add_v3_v3(farcenter, point); + /* rotate by 90 degree to walk through the 4 points of the far clip plane */ + float tmp = corner[0]; + corner[0] = -corner[1]; + corner[1] = tmp; + } + + /* the far center is the average of the far clipping points */ + mul_v3_fl(farcenter, 0.25f); + /* the extreme near point is the opposite point on the near clipping plane */ + copy_v3_fl3(nfar, -nfar[0], -nfar[1], -1.0f); + mul_v3_project_m4_v3(nearpoint, data_.wininv.ptr(), nfar); + /* this is a frustum projection */ + N = len_squared_v3(nearpoint); + e = farpoint[2]; + s = nearpoint[2]; + /* distance to view Z axis */ + f = len_v2(farpoint); + /* get corresponding point on the near plane */ + mul_v2_v2fl(farxy, farpoint, s / e); + /* this formula preserve the sign of n */ + sub_v2_v2(nearpoint, farxy); + n = f * s / e - len_v2(nearpoint); + c = len_v2(farcenter) / e; + /* the big formula, it simplifies to (F-N)/(2(e-s)) for the symmetric case */ + z = (F - N) / (2.0f * (e - s + c * (f - n))); + + bsphere.center[0] = farcenter[0] * z / e; + bsphere.center[1] = farcenter[1] * z / e; + bsphere.center[2] = z; + + /* For XR, the view matrix may contain a scale factor. Then, transforming only the center + * into world space after calculating the radius will result in incorrect behavior. */ + mul_m4_v3(data_.viewinv.ptr(), bsphere.center); /* Transform to world space. */ + mul_m4_v3(data_.viewinv.ptr(), farpoint); + bsphere.radius = len_v3v3(bsphere.center, farpoint); + } +} + +void View::set_clip_planes(Span<float4> planes) +{ + BLI_assert(planes.size() <= ARRAY_SIZE(data_.clip_planes)); + int i = 0; + for (const auto &plane : planes) { + data_.clip_planes[i++] = plane; + } +} + +void View::update_viewport_size() +{ + float4 viewport; + GPU_viewport_size_get_f(viewport); + float2 viewport_size = float2(viewport.z, viewport.w); + if (assign_if_different(data_.viewport_size, viewport_size)) { + dirty_ = true; + } +} + +void View::update_view_vectors() +{ + bool is_persp = data_.winmat[3][3] == 0.0f; + + /* Near clip distance. */ + data_.viewvecs[0][3] = (is_persp) ? -data_.winmat[3][2] / (data_.winmat[2][2] - 1.0f) : + -(data_.winmat[3][2] + 1.0f) / data_.winmat[2][2]; + + /* Far clip distance. */ + data_.viewvecs[1][3] = (is_persp) ? -data_.winmat[3][2] / (data_.winmat[2][2] + 1.0f) : + -(data_.winmat[3][2] - 1.0f) / data_.winmat[2][2]; + + /* View vectors for the corners of the view frustum. + * Can be used to recreate the world space position easily */ + float3 view_vecs[4] = { + {-1.0f, -1.0f, -1.0f}, + {1.0f, -1.0f, -1.0f}, + {-1.0f, 1.0f, -1.0f}, + {-1.0f, -1.0f, 1.0f}, + }; + + /* Convert the view vectors to view space */ + for (int i = 0; i < 4; i++) { + mul_project_m4_v3(data_.wininv.ptr(), view_vecs[i]); + /* Normalized trick see: + * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */ + if (is_persp) { + view_vecs[i].x /= view_vecs[i].z; + view_vecs[i].y /= view_vecs[i].z; + } + } + + /** + * If ortho : view_vecs[0] is the near-bottom-left corner of the frustum and + * view_vecs[1] is the vector going from the near-bottom-left corner to + * the far-top-right corner. + * If Persp : view_vecs[0].xy and view_vecs[1].xy are respectively the bottom-left corner + * when Z = 1, and top-left corner if Z = 1. + * view_vecs[0].z the near clip distance and view_vecs[1].z is the (signed) + * distance from the near plane to the far clip plane. + */ + copy_v3_v3(data_.viewvecs[0], view_vecs[0]); + + /* we need to store the differences */ + data_.viewvecs[1][0] = view_vecs[1][0] - view_vecs[0][0]; + data_.viewvecs[1][1] = view_vecs[2][1] - view_vecs[0][1]; + data_.viewvecs[1][2] = view_vecs[3][2] - view_vecs[0][2]; +} + +void View::bind() +{ + update_viewport_size(); + + if (dirty_) { + dirty_ = false; + data_.push_update(); + } + + GPU_uniformbuf_bind(data_, DRW_VIEW_UBO_SLOT); +} + +void View::compute_visibility(ObjectBoundsBuf &bounds, uint resource_len, bool debug_freeze) +{ + if (debug_freeze && frozen_ == false) { + data_freeze_ = static_cast<ViewInfos>(data_); + data_freeze_.push_update(); + } +#ifdef DEBUG + if (debug_freeze) { + drw_debug_matrix_as_bbox(data_freeze_.persinv, float4(0, 1, 0, 1)); + } +#endif + frozen_ = debug_freeze; + + GPU_debug_group_begin("View.compute_visibility"); + + /* TODO(fclem): Early out if visibility hasn't changed. */ + /* TODO(fclem): Resize to nearest pow2 to reduce fragmentation. */ + visibility_buf_.resize(divide_ceil_u(resource_len, 128)); + + uint32_t data = 0xFFFFFFFFu; + GPU_storagebuf_clear(visibility_buf_, GPU_R32UI, GPU_DATA_UINT, &data); + + if (do_visibility_) { + GPUShader *shader = DRW_shader_draw_visibility_compute_get(); + GPU_shader_bind(shader); + GPU_shader_uniform_1i(shader, "resource_len", resource_len); + GPU_storagebuf_bind(bounds, GPU_shader_get_ssbo(shader, "bounds_buf")); + GPU_storagebuf_bind(visibility_buf_, GPU_shader_get_ssbo(shader, "visibility_buf")); + GPU_uniformbuf_bind((frozen_) ? data_freeze_ : data_, DRW_VIEW_UBO_SLOT); + GPU_compute_dispatch(shader, divide_ceil_u(resource_len, DRW_VISIBILITY_GROUP_SIZE), 1, 1); + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + } + + if (frozen_) { + /* Bind back the non frozen data. */ + GPU_uniformbuf_bind(data_, DRW_VIEW_UBO_SLOT); + } + + GPU_debug_group_end(); +} + +} // namespace blender::draw diff --git a/source/blender/draw/intern/draw_view.hh b/source/blender/draw/intern/draw_view.hh new file mode 100644 index 00000000000..82e74774a5a --- /dev/null +++ b/source/blender/draw/intern/draw_view.hh @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +#pragma once + +/** \file + * \ingroup draw + */ + +#include "DRW_gpu_wrapper.hh" +#include "DRW_render.h" + +#include "draw_shader_shared.h" + +namespace blender::draw { + +class Manager; + +/* TODO deduplicate. */ +using ObjectBoundsBuf = StorageArrayBuffer<ObjectBounds, 128>; +/** \note Using uint4 for declaration but bound as uint. */ +using VisibilityBuf = StorageArrayBuffer<uint4, 1, true>; + +class View { + friend Manager; + + private: + UniformBuffer<ViewInfos> data_; + /** Freezed version of data_ used for debugging culling. */ + UniformBuffer<ViewInfos> data_freeze_; + /** Result of the visibility computation. 1 bit per resource ID. */ + VisibilityBuf visibility_buf_; + + const char *debug_name_; + + bool do_visibility_ = true; + bool dirty_ = true; + bool frozen_ = false; + + public: + View(const char *name) : visibility_buf_(name), debug_name_(name){}; + /* For compatibility with old system. Will be removed at some point. */ + View(const char *name, const DRWView *view) : visibility_buf_(name), debug_name_(name) + { + float4x4 view_mat, win_mat; + DRW_view_viewmat_get(view, view_mat.ptr(), false); + DRW_view_winmat_get(view, win_mat.ptr(), false); + this->sync(view_mat, win_mat); + } + + void set_clip_planes(Span<float4> planes); + + void sync(const float4x4 &view_mat, const float4x4 &win_mat); + + bool is_persp() const + { + return data_.winmat[3][3] == 0.0f; + } + + bool is_inverted() const + { + return data_.is_inverted; + } + + float far_clip() const + { + if (is_persp()) { + return -data_.winmat[3][2] / (data_.winmat[2][2] + 1.0f); + } + return -(data_.winmat[3][2] - 1.0f) / data_.winmat[2][2]; + } + + float near_clip() const + { + if (is_persp()) { + return -data_.winmat[3][2] / (data_.winmat[2][2] - 1.0f); + } + return -(data_.winmat[3][2] + 1.0f) / data_.winmat[2][2]; + } + + private: + /** Called from draw manager. */ + void bind(); + void compute_visibility(ObjectBoundsBuf &bounds, uint resource_len, bool debug_freeze); + + void update_view_vectors(); + void update_viewport_size(); + + void frustum_boundbox_calc(BoundBox &bbox); + void frustum_culling_planes_calc(); + void frustum_culling_sphere_calc(const BoundBox &bbox, BoundSphere &bsphere); +}; + +} // namespace blender::draw diff --git a/source/blender/draw/intern/draw_view_data.cc b/source/blender/draw/intern/draw_view_data.cc index 55f1ab83b3a..58d826e0218 100644 --- a/source/blender/draw/intern/draw_view_data.cc +++ b/source/blender/draw/intern/draw_view_data.cc @@ -7,6 +7,7 @@ #include "BLI_vector.hh" +#include "GPU_capabilities.h" #include "GPU_viewport.h" #include "DRW_render.h" @@ -16,6 +17,7 @@ #include "draw_manager_text.h" #include "draw_manager.h" +#include "draw_manager.hh" #include "draw_view_data.h" using namespace blender; @@ -33,6 +35,22 @@ struct DRWViewData { Vector<ViewportEngineData> engines; Vector<ViewportEngineData *> enabled_engines; + + /** New per view/viewport manager. Null if not supported by current hardware. */ + draw::Manager *manager = nullptr; + + DRWViewData() + { + /* Only for GL >= 4.3 implementation for now. */ + if (GPU_shader_storage_buffer_objects_support() && GPU_compute_shader_support()) { + manager = new draw::Manager(); + } + }; + + ~DRWViewData() + { + delete manager; + }; }; DRWViewData *DRW_view_data_create(ListBase *engine_types) @@ -237,3 +255,31 @@ ViewportEngineData *DRW_view_data_enabled_engine_iter_step(DRWEngineIterator *it ViewportEngineData *engine = iterator->engines[iterator->id++]; return engine; } + +draw::Manager *DRW_manager_get() +{ + BLI_assert(DST.view_data_active->manager); + return reinterpret_cast<draw::Manager *>(DST.view_data_active->manager); +} + +draw::ObjectRef DRW_object_ref_get(Object *object) +{ + BLI_assert(DST.view_data_active->manager); + return {object, DST.dupli_source, DST.dupli_parent}; +} + +void DRW_manager_begin_sync() +{ + if (DST.view_data_active->manager == nullptr) { + return; + } + reinterpret_cast<draw::Manager *>(DST.view_data_active->manager)->begin_sync(); +} + +void DRW_manager_end_sync() +{ + if (DST.view_data_active->manager == nullptr) { + return; + } + reinterpret_cast<draw::Manager *>(DST.view_data_active->manager)->end_sync(); +} diff --git a/source/blender/draw/intern/shaders/common_attribute_lib.glsl b/source/blender/draw/intern/shaders/common_attribute_lib.glsl index ce5e49c7f63..6b5b6fcc846 100644 --- a/source/blender/draw/intern/shaders/common_attribute_lib.glsl +++ b/source/blender/draw/intern/shaders/common_attribute_lib.glsl @@ -25,3 +25,4 @@ float attr_load_float(sampler3D tex); float attr_load_temperature_post(float attr); vec4 attr_load_color_post(vec4 attr); +vec4 attr_load_uniform(vec4 attr, const uint attr_hash); diff --git a/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl b/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl index 5f795d3abdb..3287897e73c 100644 --- a/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl +++ b/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl @@ -17,8 +17,7 @@ const vec4 drw_debug_default_color = vec4(1.0, 0.0, 0.0, 1.0); uint drw_debug_start_draw(uint v_needed) { uint vertid = atomicAdd(drw_debug_draw_v_count, v_needed); - /* NOTE: Skip the header manually. */ - vertid += 1; + vertid += drw_debug_draw_offset; return vertid; } diff --git a/source/blender/draw/intern/shaders/common_debug_print_lib.glsl b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl index 0c7f32bd00d..89d1729b52d 100644 --- a/source/blender/draw/intern/shaders/common_debug_print_lib.glsl +++ b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl @@ -71,8 +71,7 @@ void drw_print_char4(uint data) break; } uint cursor = atomicAdd(drw_debug_print_cursor, 1u); - /* NOTE: Skip the header manually. */ - cursor += 4; + cursor += drw_debug_print_offset; if (cursor < DRW_DEBUG_PRINT_MAX) { /* For future usage. (i.e: Color) */ uint flags = 0u; diff --git a/source/blender/draw/intern/shaders/common_intersect_lib.glsl b/source/blender/draw/intern/shaders/common_intersect_lib.glsl index 33378588553..83223f89277 100644 --- a/source/blender/draw/intern/shaders/common_intersect_lib.glsl +++ b/source/blender/draw/intern/shaders/common_intersect_lib.glsl @@ -70,6 +70,30 @@ IsectBox isect_data_setup(Box shape) return data; } +/* Construct box from 1 corner point + 3 side vectors. */ +IsectBox isect_data_setup(vec3 origin, vec3 side_x, vec3 side_y, vec3 side_z) +{ + IsectBox data; + data.corners[0] = origin; + data.corners[1] = origin + side_x; + data.corners[2] = origin + side_y + side_x; + data.corners[3] = origin + side_y; + data.corners[4] = data.corners[0] + side_z; + data.corners[5] = data.corners[1] + side_z; + data.corners[6] = data.corners[2] + side_z; + data.corners[7] = data.corners[3] + side_z; + + data.planes[0] = isect_plane_setup(data.corners[0], side_y, side_z); + data.planes[1] = isect_plane_setup(data.corners[0], side_x, side_y); + data.planes[2] = isect_plane_setup(data.corners[0], side_z, side_x); + /* Assumes that the box is actually a box! */ + data.planes[3] = vec4(-data.planes[0].xyz, -dot(-data.planes[0].xyz, data.corners[6])); + data.planes[4] = vec4(-data.planes[1].xyz, -dot(-data.planes[1].xyz, data.corners[6])); + data.planes[5] = vec4(-data.planes[2].xyz, -dot(-data.planes[2].xyz, data.corners[6])); + + return data; +} + struct IsectFrustum { vec3 corners[8]; vec4 planes[6]; @@ -194,6 +218,50 @@ bool intersect_view(Box box) return intersects; } +bool intersect_view(IsectBox i_box) +{ + bool intersects = true; + + /* Do Box vertices vs Frustum planes. */ + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(drw_view.frustum_planes[p], vec4(i_box.corners[v], 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + if (!intersects) { + return intersects; + } + + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(i_box.planes[p], vec4(drw_view.frustum_corners[v].xyz, 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + return intersects; +} + bool intersect_view(Sphere sphere) { bool intersects = true; diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl index 8ab2ef10e4c..6521476c3a7 100644 --- a/source/blender/draw/intern/shaders/common_view_lib.glsl +++ b/source/blender/draw/intern/shaders/common_view_lib.glsl @@ -155,7 +155,11 @@ uniform int drw_ResourceID; # define PASS_RESOURCE_ID # elif defined(GPU_VERTEX_SHADER) -# define resource_id gpu_InstanceIndex +# if defined(UNIFORM_RESOURCE_ID_NEW) +# define resource_id drw_ResourceID +# else +# define resource_id gpu_InstanceIndex +# endif # define PASS_RESOURCE_ID drw_ResourceID_iface.resource_index = resource_id; # elif defined(GPU_GEOMETRY_SHADER) @@ -203,8 +207,8 @@ flat in int resourceIDFrag; # ifndef DRW_SHADER_SHARED_H struct ObjectMatrices { - mat4 drw_modelMatrix; - mat4 drw_modelMatrixInverse; + mat4 model; + mat4 model_inverse; }; # endif /* DRW_SHADER_SHARED_H */ @@ -214,8 +218,8 @@ layout(std140) uniform modelBlock ObjectMatrices drw_matrices[DRW_RESOURCE_CHUNK_LEN]; }; -# define ModelMatrix (drw_matrices[resource_id].drw_modelMatrix) -# define ModelMatrixInverse (drw_matrices[resource_id].drw_modelMatrixInverse) +# define ModelMatrix (drw_matrices[resource_id].model) +# define ModelMatrixInverse (drw_matrices[resource_id].model_inverse) # endif /* USE_GPU_SHADER_CREATE_INFO */ #else /* GPU_INTEL */ diff --git a/source/blender/draw/intern/shaders/draw_command_generate_comp.glsl b/source/blender/draw/intern/shaders/draw_command_generate_comp.glsl new file mode 100644 index 00000000000..70842e5bb81 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_command_generate_comp.glsl @@ -0,0 +1,84 @@ + +/** + * Convert DrawPrototype into draw commands. + */ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +#define atomicAddAndGet(dst, val) (atomicAdd(dst, val) + val) + +/* This is only called by the last thread executed over the group's prototype draws. */ +void write_draw_call(DrawGroup group, uint group_id) +{ + DrawCommand cmd; + cmd.vertex_len = group.vertex_len; + cmd.vertex_first = group.vertex_first; + if (group.base_index != -1) { + cmd.base_index = group.base_index; + cmd.instance_first_indexed = group.start; + } + else { + cmd._instance_first_array = group.start; + } + /* Back-facing command. */ + cmd.instance_len = group_buf[group_id].back_facing_counter; + command_buf[group_id * 2 + 0] = cmd; + /* Front-facing command. */ + cmd.instance_len = group_buf[group_id].front_facing_counter; + command_buf[group_id * 2 + 1] = cmd; + + /* Reset the counters for a next command gen dispatch. Avoids resending the whole data just + * for this purpose. Only the last thread will execute this so it is threadsafe. */ + group_buf[group_id].front_facing_counter = 0u; + group_buf[group_id].back_facing_counter = 0u; + group_buf[group_id].total_counter = 0u; +} + +void main() +{ + uint proto_id = gl_GlobalInvocationID.x; + if (proto_id >= prototype_len) { + return; + } + + DrawPrototype proto = prototype_buf[proto_id]; + uint group_id = proto.group_id; + bool is_inverted = (proto.resource_handle & 0x80000000u) != 0; + uint resource_index = (proto.resource_handle & 0x7FFFFFFFu); + + /* Visibility test result. */ + bool is_visible = ((visibility_buf[resource_index / 32u] & (1u << (resource_index % 32u)))) != 0; + + DrawGroup group = group_buf[group_id]; + + if (!is_visible) { + /* Skip the draw but still count towards the completion. */ + if (atomicAddAndGet(group_buf[group_id].total_counter, proto.instance_len) == group.len) { + write_draw_call(group, group_id); + } + return; + } + + uint back_facing_len = group.len - group.front_facing_len; + uint front_facing_len = group.front_facing_len; + uint dst_index = group.start; + if (is_inverted) { + uint offset = atomicAdd(group_buf[group_id].back_facing_counter, proto.instance_len); + dst_index += offset; + if (atomicAddAndGet(group_buf[group_id].total_counter, proto.instance_len) == group.len) { + write_draw_call(group, group_id); + } + } + else { + uint offset = atomicAdd(group_buf[group_id].front_facing_counter, proto.instance_len); + dst_index += back_facing_len + offset; + if (atomicAddAndGet(group_buf[group_id].total_counter, proto.instance_len) == group.len) { + write_draw_call(group, group_id); + } + } + + for (uint i = dst_index; i < dst_index + proto.instance_len; i++) { + /* Fill resource_id buffer for each instance of this draw */ + resource_id_buf[i] = resource_index; + } +} diff --git a/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl b/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl index ab76df819d5..4061dda5d1c 100644 --- a/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl +++ b/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl @@ -6,7 +6,7 @@ void main() { /* Skip the first vertex containing header data. */ - DRWDebugVert vert = drw_debug_verts_buf[gl_VertexID + 1]; + DRWDebugVert vert = drw_debug_verts_buf[gl_VertexID + 2]; vec3 pos = uintBitsToFloat(uvec3(vert.pos0, vert.pos1, vert.pos2)); vec4 col = vec4((uvec4(vert.color) >> uvec4(0, 8, 16, 24)) & 0xFFu) / 255.0; diff --git a/source/blender/draw/intern/shaders/draw_debug_info.hh b/source/blender/draw/intern/shaders/draw_debug_info.hh index 893a5e537d9..ce450bb1210 100644 --- a/source/blender/draw/intern/shaders/draw_debug_info.hh +++ b/source/blender/draw/intern/shaders/draw_debug_info.hh @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "draw_defines.h" #include "gpu_shader_create_info.hh" /* -------------------------------------------------------------------- */ @@ -10,7 +11,7 @@ GPU_SHADER_CREATE_INFO(draw_debug_print) .typedef_source("draw_shader_shared.h") - .storage_buf(7, Qualifier::READ_WRITE, "uint", "drw_debug_print_buf[]"); + .storage_buf(DRW_DEBUG_PRINT_SLOT, Qualifier::READ_WRITE, "uint", "drw_debug_print_buf[]"); GPU_SHADER_INTERFACE_INFO(draw_debug_print_display_iface, "").flat(Type::UINT, "char_index"); @@ -34,7 +35,10 @@ GPU_SHADER_CREATE_INFO(draw_debug_print_display) GPU_SHADER_CREATE_INFO(draw_debug_draw) .typedef_source("draw_shader_shared.h") - .storage_buf(6, Qualifier::READ_WRITE, "DRWDebugVert", "drw_debug_verts_buf[]"); + .storage_buf(DRW_DEBUG_DRAW_SLOT, + Qualifier::READ_WRITE, + "DRWDebugVert", + "drw_debug_verts_buf[]"); GPU_SHADER_INTERFACE_INFO(draw_debug_draw_display_iface, "interp").flat(Type::VEC4, "color"); diff --git a/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl b/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl index f67e9d3f9e0..cb379056e2b 100644 --- a/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl +++ b/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl @@ -8,7 +8,7 @@ void main() { /* Skip first 4 chars containing header data. */ - uint char_data = drw_debug_print_buf[gl_VertexID + 4]; + uint char_data = drw_debug_print_buf[gl_VertexID + 8]; char_index = (char_data & 0xFFu) - 0x20u; /* Discard invalid chars. */ 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 8fd55ea351f..31fee018fbc 100644 --- a/source/blender/draw/intern/shaders/draw_object_infos_info.hh +++ b/source/blender/draw/intern/shaders/draw_object_infos_info.hh @@ -1,10 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "draw_defines.h" #include "gpu_shader_create_info.hh" GPU_SHADER_CREATE_INFO(draw_object_infos) .typedef_source("draw_shader_shared.h") .define("OBINFO_LIB") + .define("OrcoTexCoFactors", "(drw_infos[resource_id].orco_mul_bias)") + .define("ObjectInfo", "(drw_infos[resource_id].infos)") + .define("ObjectColor", "(drw_infos[resource_id].color)") .uniform_buf(1, "ObjectInfos", "drw_infos[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH); GPU_SHADER_CREATE_INFO(draw_volume_infos) @@ -14,3 +18,19 @@ GPU_SHADER_CREATE_INFO(draw_volume_infos) GPU_SHADER_CREATE_INFO(draw_curves_infos) .typedef_source("draw_shader_shared.h") .uniform_buf(2, "CurvesInfos", "drw_curves", Frequency::BATCH); + +GPU_SHADER_CREATE_INFO(draw_object_infos_new) + .typedef_source("draw_shader_shared.h") + .define("OBINFO_LIB") + .define("OrcoTexCoFactors", "(drw_infos[resource_id].orco_mul_bias)") + .define("ObjectInfo", "(drw_infos[resource_id].infos)") + .define("ObjectColor", "(drw_infos[resource_id].color)") + .storage_buf(DRW_OBJ_INFOS_SLOT, Qualifier::READ, "ObjectInfos", "drw_infos[]"); + +/** \note Requires draw_object_infos_new. */ +GPU_SHADER_CREATE_INFO(draw_object_attribute_new) + .define("OBATTR_LIB") + .define("ObjectAttributeStart", "(drw_infos[resource_id].orco_mul_bias[0].w)") + .define("ObjectAttributeLen", "(drw_infos[resource_id].orco_mul_bias[1].w)") + .storage_buf(DRW_OBJ_ATTR_SLOT, Qualifier::READ, "ObjectAttribute", "drw_attrs[]") + .additional_info("draw_object_infos_new"); diff --git a/source/blender/draw/intern/shaders/draw_resource_finalize_comp.glsl b/source/blender/draw/intern/shaders/draw_resource_finalize_comp.glsl new file mode 100644 index 00000000000..d834435e54e --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_resource_finalize_comp.glsl @@ -0,0 +1,64 @@ + +/** + * Finish computation of a few draw resource after sync. + */ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +void main() +{ + uint resource_id = gl_GlobalInvocationID.x; + if (resource_id >= resource_len) { + return; + } + + mat4 model_mat = matrix_buf[resource_id].model; + ObjectInfos infos = infos_buf[resource_id]; + ObjectBounds bounds = bounds_buf[resource_id]; + + if (bounds.bounding_sphere.w != -1.0) { + /* Convert corners to origin + sides in world space. */ + vec3 p0 = bounds.bounding_corners[0].xyz; + vec3 p01 = bounds.bounding_corners[1].xyz - p0; + vec3 p02 = bounds.bounding_corners[2].xyz - p0; + vec3 p03 = bounds.bounding_corners[3].xyz - p0; + /* Avoid flat box. */ + p01.x = max(p01.x, 1e-4); + p02.y = max(p02.y, 1e-4); + p03.z = max(p03.z, 1e-4); + vec3 diagonal = p01 + p02 + p03; + vec3 center = p0 + diagonal * 0.5; + float min_axis = min_v3(abs(diagonal)); + bounds_buf[resource_id].bounding_sphere.xyz = transform_point(model_mat, center); + /* We have to apply scaling to the diagonal. */ + bounds_buf[resource_id].bounding_sphere.w = length(transform_direction(model_mat, diagonal)) * + 0.5; + bounds_buf[resource_id]._inner_sphere_radius = min_axis; + bounds_buf[resource_id].bounding_corners[0].xyz = transform_point(model_mat, p0); + bounds_buf[resource_id].bounding_corners[1].xyz = transform_direction(model_mat, p01); + bounds_buf[resource_id].bounding_corners[2].xyz = transform_direction(model_mat, p02); + bounds_buf[resource_id].bounding_corners[3].xyz = transform_direction(model_mat, p03); + /* Always have correct handedness in the corners vectors. */ + if (flag_test(infos.flag, OBJECT_NEGATIVE_SCALE)) { + bounds_buf[resource_id].bounding_corners[0].xyz += + bounds_buf[resource_id].bounding_corners[1].xyz; + bounds_buf[resource_id].bounding_corners[1].xyz = + -bounds_buf[resource_id].bounding_corners[1].xyz; + } + + /* TODO: Bypass test for very large objects (see T67319). */ + if (bounds_buf[resource_id].bounding_sphere.w > 1e12) { + bounds_buf[resource_id].bounding_sphere.w = -1.0; + } + } + + vec3 loc = infos.orco_add; /* Box center. */ + vec3 size = infos.orco_mul; /* Box half-extent. */ + /* This is what the original computation looks like. + * Simplify to a nice MADD in shading code. */ + // orco = (pos - loc) / size; + // orco = pos * (1.0 / size) + (-loc / size); + vec3 size_inv = safe_rcp(size); + infos_buf[resource_id].orco_add = -loc * size_inv; + infos_buf[resource_id].orco_mul = size_inv; +}
\ No newline at end of file diff --git a/source/blender/draw/intern/shaders/draw_view_info.hh b/source/blender/draw/intern/shaders/draw_view_info.hh index 0400521c53d..c522c607791 100644 --- a/source/blender/draw/intern/shaders/draw_view_info.hh +++ b/source/blender/draw/intern/shaders/draw_view_info.hh @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "draw_defines.h" #include "gpu_shader_create_info.hh" /* -------------------------------------------------------------------- */ @@ -44,13 +45,13 @@ GPU_SHADER_CREATE_INFO(draw_resource_handle) * \{ */ GPU_SHADER_CREATE_INFO(draw_view) - .uniform_buf(0, "ViewInfos", "drw_view", Frequency::PASS) + .uniform_buf(DRW_VIEW_UBO_SLOT, "ViewInfos", "drw_view", Frequency::PASS) .typedef_source("draw_shader_shared.h"); GPU_SHADER_CREATE_INFO(draw_modelmat) .uniform_buf(8, "ObjectMatrices", "drw_matrices[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH) - .define("ModelMatrix", "(drw_matrices[resource_id].drw_modelMatrix)") - .define("ModelMatrixInverse", "(drw_matrices[resource_id].drw_modelMatrixInverse)") + .define("ModelMatrix", "(drw_matrices[resource_id].model)") + .define("ModelMatrixInverse", "(drw_matrices[resource_id].model_inverse)") .additional_info("draw_view"); GPU_SHADER_CREATE_INFO(draw_modelmat_legacy) @@ -136,3 +137,77 @@ GPU_SHADER_CREATE_INFO(draw_gpencil) .additional_info("draw_modelmat", "draw_resource_id_uniform", "draw_object_infos"); /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internal Draw Manager usage + * \{ */ + +GPU_SHADER_CREATE_INFO(draw_resource_finalize) + .do_static_compilation(true) + .typedef_source("draw_shader_shared.h") + .define("DRAW_FINALIZE_SHADER") + .local_group_size(DRW_FINALIZE_GROUP_SIZE) + .storage_buf(0, Qualifier::READ, "ObjectMatrices", "matrix_buf[]") + .storage_buf(1, Qualifier::READ_WRITE, "ObjectBounds", "bounds_buf[]") + .storage_buf(2, Qualifier::READ_WRITE, "ObjectInfos", "infos_buf[]") + .push_constant(Type::INT, "resource_len") + .compute_source("draw_resource_finalize_comp.glsl"); + +GPU_SHADER_CREATE_INFO(draw_visibility_compute) + .do_static_compilation(true) + .local_group_size(DRW_VISIBILITY_GROUP_SIZE) + .storage_buf(0, Qualifier::READ, "ObjectBounds", "bounds_buf[]") + .storage_buf(1, Qualifier::READ_WRITE, "uint", "visibility_buf[]") + .push_constant(Type::INT, "resource_len") + .compute_source("draw_visibility_comp.glsl") + .additional_info("draw_view"); + +GPU_SHADER_CREATE_INFO(draw_command_generate) + .do_static_compilation(true) + .typedef_source("draw_shader_shared.h") + .typedef_source("draw_command_shared.hh") + .local_group_size(DRW_COMMAND_GROUP_SIZE) + .storage_buf(0, Qualifier::READ_WRITE, "DrawGroup", "group_buf[]") + .storage_buf(1, Qualifier::READ, "uint", "visibility_buf[]") + .storage_buf(2, Qualifier::READ, "DrawPrototype", "prototype_buf[]") + .storage_buf(3, Qualifier::WRITE, "DrawCommand", "command_buf[]") + .storage_buf(DRW_RESOURCE_ID_SLOT, Qualifier::WRITE, "uint", "resource_id_buf[]") + .push_constant(Type::INT, "prototype_len") + .compute_source("draw_command_generate_comp.glsl"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Resource ID + * New implementation using gl_BaseInstance and storage buffers. + * \{ */ + +GPU_SHADER_CREATE_INFO(draw_resource_id_new) + .define("UNIFORM_RESOURCE_ID_NEW") + .storage_buf(DRW_RESOURCE_ID_SLOT, Qualifier::READ, "int", "resource_id_buf[]") + .define("drw_ResourceID", "resource_id_buf[gpu_BaseInstance + gl_InstanceID]"); + +/** + * Workaround the lack of gl_BaseInstance by binding the resource_id_buf as vertex buf. + */ +GPU_SHADER_CREATE_INFO(draw_resource_id_fallback) + .define("UNIFORM_RESOURCE_ID_NEW") + .vertex_in(15, Type::INT, "drw_ResourceID"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Object Resources + * \{ */ + +GPU_SHADER_CREATE_INFO(draw_modelmat_new) + .typedef_source("draw_shader_shared.h") + .storage_buf(DRW_OBJ_MAT_SLOT, Qualifier::READ, "ObjectMatrices", "drw_matrix_buf[]") + .define("drw_ModelMatrixInverse", "drw_matrix_buf[resource_id].model_inverse") + .define("drw_ModelMatrix", "drw_matrix_buf[resource_id].model") + /* TODO For compatibility with old shaders. To be removed. */ + .define("ModelMatrixInverse", "drw_ModelMatrixInverse") + .define("ModelMatrix", "drw_ModelMatrix") + .additional_info("draw_resource_id_new"); + +/** \} */ diff --git a/source/blender/draw/intern/shaders/draw_visibility_comp.glsl b/source/blender/draw/intern/shaders/draw_visibility_comp.glsl new file mode 100644 index 00000000000..7ec58c8f919 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_visibility_comp.glsl @@ -0,0 +1,46 @@ + +/** + * Compute visibility of each resource bounds for a given view. + */ +/* TODO(fclem): This could be augmented by a 2 pass occlusion culling system. */ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_intersect_lib.glsl) + +shared uint shared_result; + +void mask_visibility_bit() +{ + uint bit = 1u << gl_LocalInvocationID.x; + atomicAnd(visibility_buf[gl_WorkGroupID.x], ~bit); +} + +void main() +{ + if (gl_GlobalInvocationID.x >= resource_len) { + return; + } + + ObjectBounds bounds = bounds_buf[gl_GlobalInvocationID.x]; + + if (bounds.bounding_sphere.w != -1.0) { + IsectBox box = isect_data_setup(bounds.bounding_corners[0].xyz, + bounds.bounding_corners[1].xyz, + bounds.bounding_corners[2].xyz, + bounds.bounding_corners[3].xyz); + Sphere bounding_sphere = Sphere(bounds.bounding_sphere.xyz, bounds.bounding_sphere.w); + Sphere inscribed_sphere = Sphere(bounds.bounding_sphere.xyz, bounds._inner_sphere_radius); + + if (intersect_view(inscribed_sphere) == true) { + /* Visible. */ + } + else if (intersect_view(bounding_sphere) == false) { + /* Not visible. */ + mask_visibility_bit(); + } + else if (intersect_view(box) == false) { + /* Not visible. */ + mask_visibility_bit(); + } + } +}
\ No newline at end of file diff --git a/source/blender/draw/tests/draw_pass_test.cc b/source/blender/draw/tests/draw_pass_test.cc new file mode 100644 index 00000000000..f8a006d096b --- /dev/null +++ b/source/blender/draw/tests/draw_pass_test.cc @@ -0,0 +1,441 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "testing/testing.h" + +#include "draw_manager.hh" +#include "draw_pass.hh" +#include "draw_shader.h" +#include "draw_testing.hh" + +#include <bitset> + +namespace blender::draw { + +static void test_draw_pass_all_commands() +{ + Texture tex; + tex.ensure_2d(GPU_RGBA16, int2(1)); + + UniformBuffer<uint4> ubo; + ubo.push_update(); + + StorageBuffer<uint4> ssbo; + ssbo.push_update(); + + float alpha = 0.0f; + int3 dispatch_size(1); + + PassSimple pass = {"test.all_commands"}; + pass.init(); + pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_STENCIL); + pass.clear_color_depth_stencil(float4(0.25f, 0.5f, 100.0f, -2000.0f), 0.5f, 0xF0); + pass.state_stencil(0x80, 0x0F, 0x8F); + pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA)); + pass.bind_texture("image", tex); + pass.bind_texture("image", &tex); + pass.bind_image("missing_image", tex); /* Should not crash. */ + pass.bind_image("missing_image", &tex); /* Should not crash. */ + pass.bind_ubo("missing_ubo", ubo); /* Should not crash. */ + pass.bind_ubo("missing_ubo", &ubo); /* Should not crash. */ + pass.bind_ssbo("missing_ssbo", ssbo); /* Should not crash. */ + pass.bind_ssbo("missing_ssbo", &ssbo); /* Should not crash. */ + pass.push_constant("alpha", alpha); + pass.push_constant("alpha", &alpha); + pass.push_constant("ModelViewProjectionMatrix", float4x4::identity()); + pass.draw_procedural(GPU_PRIM_TRIS, 1, 3); + + /* Should not crash even if shader is not a compute. This is because we only serialize. */ + /* TODO(fclem): Use real compute shader. */ + pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA)); + pass.dispatch(dispatch_size); + pass.dispatch(&dispatch_size); + pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); + + /* Change references. */ + alpha = 1.0f; + dispatch_size = int3(2); + + std::string result = pass.serialize(); + std::stringstream expected; + expected << ".test.all_commands" << std::endl; + expected << " .state_set(6)" << std::endl; + expected << " .clear(color=(0.25, 0.5, 100, -2000), depth=0.5, stencil=0b11110000))" + << std::endl; + expected << " .stencil_set(write_mask=0b10000000, compare_mask=0b00001111, reference=0b10001111" + << std::endl; + expected << " .shader_bind(gpu_shader_3D_image_modulate_alpha)" << std::endl; + expected << " .bind_texture(0)" << std::endl; + expected << " .bind_texture_ref(0)" << std::endl; + expected << " .bind_image(-1)" << std::endl; + expected << " .bind_image_ref(-1)" << std::endl; + expected << " .bind_uniform_buf(-1)" << std::endl; + expected << " .bind_uniform_buf_ref(-1)" << std::endl; + expected << " .bind_storage_buf(-1)" << std::endl; + expected << " .bind_storage_buf_ref(-1)" << std::endl; + expected << " .push_constant(2, data=0)" << std::endl; + expected << " .push_constant(2, data=1)" << std::endl; + expected << " .push_constant(0, data=(" << std::endl; + expected << "( 1.000000, 0.000000, 0.000000, 0.000000)" << std::endl; + expected << "( 0.000000, 1.000000, 0.000000, 0.000000)" << std::endl; + expected << "( 0.000000, 0.000000, 1.000000, 0.000000)" << std::endl; + expected << "( 0.000000, 0.000000, 0.000000, 1.000000)" << std::endl; + expected << ")" << std::endl; + expected << ")" << std::endl; + expected << " .draw(inst_len=1, vert_len=3, vert_first=0, res_id=0)" << std::endl; + expected << " .shader_bind(gpu_shader_3D_image_modulate_alpha)" << std::endl; + expected << " .dispatch(1, 1, 1)" << std::endl; + expected << " .dispatch_ref(2, 2, 2)" << std::endl; + expected << " .barrier(4)" << std::endl; + + EXPECT_EQ(result, expected.str()); + + DRW_shape_cache_free(); +} +DRAW_TEST(draw_pass_all_commands) + +static void test_draw_pass_sub_ordering() +{ + PassSimple pass = {"test.sub_ordering"}; + pass.init(); + pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA)); + pass.push_constant("test_pass", 1); + + PassSimple::Sub &sub1 = pass.sub("Sub1"); + sub1.push_constant("test_sub1", 11); + + PassSimple::Sub &sub2 = pass.sub("Sub2"); + sub2.push_constant("test_sub2", 21); + + /* Will execute after both sub. */ + pass.push_constant("test_pass", 2); + + /* Will execute after sub1. */ + sub2.push_constant("test_sub2", 22); + + /* Will execute before sub2. */ + sub1.push_constant("test_sub1", 12); + + /* Will execute before end of pass. */ + sub2.push_constant("test_sub2", 23); + + std::string result = pass.serialize(); + std::stringstream expected; + expected << ".test.sub_ordering" << std::endl; + expected << " .shader_bind(gpu_shader_3D_image_modulate_alpha)" << std::endl; + expected << " .push_constant(-1, data=1)" << std::endl; + expected << " .Sub1" << std::endl; + expected << " .push_constant(-1, data=11)" << std::endl; + expected << " .push_constant(-1, data=12)" << std::endl; + expected << " .Sub2" << std::endl; + expected << " .push_constant(-1, data=21)" << std::endl; + expected << " .push_constant(-1, data=22)" << std::endl; + expected << " .push_constant(-1, data=23)" << std::endl; + expected << " .push_constant(-1, data=2)" << std::endl; + + EXPECT_EQ(result, expected.str()); +} +DRAW_TEST(draw_pass_sub_ordering) + +static void test_draw_pass_simple_draw() +{ + PassSimple pass = {"test.simple_draw"}; + pass.init(); + pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA)); + /* Each draw procedural type uses a different batch. Groups are drawn in correct order. */ + pass.draw_procedural(GPU_PRIM_TRIS, 1, 10, 1, {1}); + pass.draw_procedural(GPU_PRIM_POINTS, 4, 20, 2, {2}); + pass.draw_procedural(GPU_PRIM_TRIS, 2, 30, 3, {3}); + pass.draw_procedural(GPU_PRIM_POINTS, 5, 40, 4, ResourceHandle(4, true)); + pass.draw_procedural(GPU_PRIM_LINES, 1, 50, 5, {5}); + pass.draw_procedural(GPU_PRIM_POINTS, 6, 60, 6, {5}); + pass.draw_procedural(GPU_PRIM_TRIS, 3, 70, 7, {6}); + + std::string result = pass.serialize(); + std::stringstream expected; + expected << ".test.simple_draw" << std::endl; + expected << " .shader_bind(gpu_shader_3D_image_modulate_alpha)" << std::endl; + expected << " .draw(inst_len=1, vert_len=10, vert_first=1, res_id=1)" << std::endl; + expected << " .draw(inst_len=4, vert_len=20, vert_first=2, res_id=2)" << std::endl; + expected << " .draw(inst_len=2, vert_len=30, vert_first=3, res_id=3)" << std::endl; + expected << " .draw(inst_len=5, vert_len=40, vert_first=4, res_id=4)" << std::endl; + expected << " .draw(inst_len=1, vert_len=50, vert_first=5, res_id=5)" << std::endl; + expected << " .draw(inst_len=6, vert_len=60, vert_first=6, res_id=5)" << std::endl; + expected << " .draw(inst_len=3, vert_len=70, vert_first=7, res_id=6)" << std::endl; + + EXPECT_EQ(result, expected.str()); + + DRW_shape_cache_free(); +} +DRAW_TEST(draw_pass_simple_draw) + +static void test_draw_pass_multi_draw() +{ + PassMain pass = {"test.multi_draw"}; + pass.init(); + pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA)); + /* Each draw procedural type uses a different batch. Groups are drawn in reverse order. */ + pass.draw_procedural(GPU_PRIM_TRIS, 1, -1, -1, {1}); + pass.draw_procedural(GPU_PRIM_POINTS, 4, -1, -1, {2}); + pass.draw_procedural(GPU_PRIM_TRIS, 2, -1, -1, {3}); + pass.draw_procedural(GPU_PRIM_POINTS, 5, -1, -1, ResourceHandle(4, true)); + pass.draw_procedural(GPU_PRIM_LINES, 1, -1, -1, {5}); + pass.draw_procedural(GPU_PRIM_POINTS, 6, -1, -1, {5}); + pass.draw_procedural(GPU_PRIM_TRIS, 3, -1, -1, {6}); + + std::string result = pass.serialize(); + std::stringstream expected; + expected << ".test.multi_draw" << std::endl; + expected << " .shader_bind(gpu_shader_3D_image_modulate_alpha)" << std::endl; + expected << " .draw_multi(3)" << std::endl; + expected << " .group(id=2, len=1)" << std::endl; + expected << " .proto(instance_len=1, resource_id=5, front_face)" << std::endl; + expected << " .group(id=1, len=15)" << std::endl; + expected << " .proto(instance_len=5, resource_id=4, back_face)" << std::endl; + expected << " .proto(instance_len=6, resource_id=5, front_face)" << std::endl; + expected << " .proto(instance_len=4, resource_id=2, front_face)" << std::endl; + expected << " .group(id=0, len=6)" << std::endl; + expected << " .proto(instance_len=3, resource_id=6, front_face)" << std::endl; + expected << " .proto(instance_len=2, resource_id=3, front_face)" << std::endl; + expected << " .proto(instance_len=1, resource_id=1, front_face)" << std::endl; + + EXPECT_EQ(result, expected.str()); + + DRW_shape_cache_free(); +} +DRAW_TEST(draw_pass_multi_draw) + +static void test_draw_pass_sortable() +{ + PassSortable pass = {"test.sortable"}; + pass.init(); + + pass.sub("Sub3", 3.0f); + pass.sub("Sub2", 2.0f); + pass.sub("Sub5", 4.0f); + pass.sub("Sub4", 3.0f); + pass.sub("Sub1", 1.0f); + + std::string result = pass.serialize(); + std::stringstream expected; + expected << ".test.sortable" << std::endl; + expected << " .Sub1" << std::endl; + expected << " .Sub2" << std::endl; + expected << " .Sub3" << std::endl; + expected << " .Sub4" << std::endl; + expected << " .Sub5" << std::endl; + + EXPECT_EQ(result, expected.str()); + + DRW_shape_cache_free(); +} +DRAW_TEST(draw_pass_sortable) + +static void test_draw_resource_id_gen() +{ + float4x4 win_mat; + orthographic_m4(win_mat.ptr(), -1, 1, -1, 1, -1, 1); + + View view("test_view"); + view.sync(float4x4::identity(), win_mat); + + Manager drw; + + float4x4 obmat_1 = float4x4::identity(); + float4x4 obmat_2 = float4x4::identity(); + obmat_1.apply_scale(-0.5f); + obmat_2.apply_scale(0.5f); + + drw.begin_sync(); + ResourceHandle handle1 = drw.resource_handle(obmat_1); + ResourceHandle handle2 = drw.resource_handle(obmat_1); + ResourceHandle handle3 = drw.resource_handle(obmat_2); + drw.resource_handle(obmat_2, float3(2), float3(1)); + drw.end_sync(); + + StringRefNull expected = "2 1 1 1 1 3 3 1 1 1 1 1 3 2 2 2 2 2 2 1 1 1 "; + + { + /* Computed on CPU. */ + PassSimple pass = {"test.resource_id"}; + pass.init(); + pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA)); + pass.draw_procedural(GPU_PRIM_TRIS, 1, -1, -1, handle2); + pass.draw_procedural(GPU_PRIM_POINTS, 4, -1, -1, handle1); + pass.draw_procedural(GPU_PRIM_TRIS, 2, -1, -1, handle3); + pass.draw_procedural(GPU_PRIM_POINTS, 5, -1, -1, handle1); + pass.draw_procedural(GPU_PRIM_LINES, 1, -1, -1, handle3); + pass.draw_procedural(GPU_PRIM_POINTS, 6, -1, -1, handle2); + pass.draw_procedural(GPU_PRIM_TRIS, 3, -1, -1, handle1); + + Manager::SubmitDebugOutput debug = drw.submit_debug(pass, view); + + std::stringstream result; + for (auto val : debug.resource_id) { + result << val << " "; + } + + EXPECT_EQ(result.str(), expected); + } + { + /* Same thing with PassMain (computed on GPU) */ + PassSimple pass = {"test.resource_id"}; + pass.init(); + pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA)); + pass.draw_procedural(GPU_PRIM_TRIS, 1, -1, -1, handle2); + pass.draw_procedural(GPU_PRIM_POINTS, 4, -1, -1, handle1); + pass.draw_procedural(GPU_PRIM_TRIS, 2, -1, -1, handle3); + pass.draw_procedural(GPU_PRIM_POINTS, 5, -1, -1, handle1); + pass.draw_procedural(GPU_PRIM_LINES, 1, -1, -1, handle3); + pass.draw_procedural(GPU_PRIM_POINTS, 6, -1, -1, handle2); + pass.draw_procedural(GPU_PRIM_TRIS, 3, -1, -1, handle1); + + Manager::SubmitDebugOutput debug = drw.submit_debug(pass, view); + + std::stringstream result; + for (auto val : debug.resource_id) { + result << val << " "; + } + + EXPECT_EQ(result.str(), expected); + } + + DRW_shape_cache_free(); + DRW_shaders_free(); +} +DRAW_TEST(draw_resource_id_gen) + +static void test_draw_visibility() +{ + float4x4 win_mat; + orthographic_m4(win_mat.ptr(), -1, 1, -1, 1, -1, 1); + + View view("test_view"); + view.sync(float4x4::identity(), win_mat); + + Manager drw; + + float4x4 obmat_1 = float4x4::identity(); + float4x4 obmat_2 = float4x4::identity(); + obmat_1.apply_scale(-0.5f); + obmat_2.apply_scale(0.5f); + + drw.begin_sync(); /* Default {0} always visible. */ + drw.resource_handle(obmat_1); /* No bounds, always visible. */ + drw.resource_handle(obmat_1, float3(3), float3(1)); /* Out of view. */ + drw.resource_handle(obmat_2, float3(0), float3(1)); /* Inside view. */ + drw.end_sync(); + + PassMain pass = {"test.visibility"}; + pass.init(); + pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA)); + pass.draw_procedural(GPU_PRIM_TRIS, 1, -1); + + Manager::SubmitDebugOutput debug = drw.submit_debug(pass, view); + Vector<uint32_t> expected_visibility = {0}; + + std::stringstream result; + for (auto val : debug.visibility) { + result << std::bitset<32>(val); + } + + EXPECT_EQ(result.str(), "11111111111111111111111111111011"); + + DRW_shape_cache_free(); + DRW_shaders_free(); +} +DRAW_TEST(draw_visibility) + +static void test_draw_manager_sync() +{ + float4x4 obmat_1 = float4x4::identity(); + float4x4 obmat_2 = float4x4::identity(); + obmat_1.apply_scale(-0.5f); + obmat_2.apply_scale(0.5f); + + /* TODO find a way to create a minimum object to test resource handle creation on it. */ + Manager drw; + + drw.begin_sync(); + drw.resource_handle(obmat_1); + drw.resource_handle(obmat_2, float3(2), float3(1)); + drw.end_sync(); + + Manager::DataDebugOutput debug = drw.data_debug(); + + std::stringstream result; + for (const auto &val : debug.matrices) { + result << val; + } + for (const auto &val : debug.bounds) { + result << val; + } + for (const auto &val : debug.infos) { + result << val; + } + + std::stringstream expected; + expected << "ObjectMatrices(" << std::endl; + expected << "model=(" << std::endl; + expected << "( 1.000000, 0.000000, 0.000000, 0.000000)" << std::endl; + expected << "( 0.000000, 1.000000, 0.000000, 0.000000)" << std::endl; + expected << "( 0.000000, 0.000000, 1.000000, 0.000000)" << std::endl; + expected << "( 0.000000, 0.000000, 0.000000, 1.000000)" << std::endl; + expected << ")" << std::endl; + expected << ", " << std::endl; + expected << "model_inverse=(" << std::endl; + expected << "( 1.000000, -0.000000, 0.000000, -0.000000)" << std::endl; + expected << "( -0.000000, 1.000000, -0.000000, 0.000000)" << std::endl; + expected << "( 0.000000, -0.000000, 1.000000, -0.000000)" << std::endl; + expected << "( -0.000000, 0.000000, -0.000000, 1.000000)" << std::endl; + expected << ")" << std::endl; + expected << ")" << std::endl; + expected << "ObjectMatrices(" << std::endl; + expected << "model=(" << std::endl; + expected << "( -0.500000, -0.000000, -0.000000, 0.000000)" << std::endl; + expected << "( -0.000000, -0.500000, -0.000000, 0.000000)" << std::endl; + expected << "( -0.000000, -0.000000, -0.500000, 0.000000)" << std::endl; + expected << "( 0.000000, 0.000000, 0.000000, 1.000000)" << std::endl; + expected << ")" << std::endl; + expected << ", " << std::endl; + expected << "model_inverse=(" << std::endl; + expected << "( -2.000000, 0.000000, -0.000000, -0.000000)" << std::endl; + expected << "( 0.000000, -2.000000, 0.000000, 0.000000)" << std::endl; + expected << "( -0.000000, 0.000000, -2.000000, 0.000000)" << std::endl; + expected << "( -0.000000, -0.000000, 0.000000, 1.000000)" << std::endl; + expected << ")" << std::endl; + expected << ")" << std::endl; + expected << "ObjectMatrices(" << std::endl; + expected << "model=(" << std::endl; + expected << "( 0.500000, 0.000000, 0.000000, 0.000000)" << std::endl; + expected << "( 0.000000, 0.500000, 0.000000, 0.000000)" << std::endl; + expected << "( 0.000000, 0.000000, 0.500000, 0.000000)" << std::endl; + expected << "( 0.000000, 0.000000, 0.000000, 1.000000)" << std::endl; + expected << ")" << std::endl; + expected << ", " << std::endl; + expected << "model_inverse=(" << std::endl; + expected << "( 2.000000, -0.000000, 0.000000, -0.000000)" << std::endl; + expected << "( -0.000000, 2.000000, -0.000000, 0.000000)" << std::endl; + expected << "( 0.000000, -0.000000, 2.000000, -0.000000)" << std::endl; + expected << "( -0.000000, 0.000000, -0.000000, 1.000000)" << std::endl; + expected << ")" << std::endl; + expected << ")" << std::endl; + expected << "ObjectBounds(skipped)" << std::endl; + expected << "ObjectBounds(skipped)" << std::endl; + expected << "ObjectBounds(" << std::endl; + expected << ".bounding_corners[0](0.5, 0.5, 0.5)" << std::endl; + expected << ".bounding_corners[1](1, 0, 0)" << std::endl; + expected << ".bounding_corners[2](0, 1, 0)" << std::endl; + expected << ".bounding_corners[3](0, 0, 1)" << std::endl; + expected << ".sphere=(pos=(1, 1, 1), rad=0.866025" << std::endl; + expected << ")" << std::endl; + expected << "ObjectInfos(skipped)" << std::endl; + expected << "ObjectInfos(skipped)" << std::endl; + expected << "ObjectInfos(skipped)" << std::endl; + + EXPECT_EQ(result.str(), expected.str()); + + DRW_shaders_free(); +} +DRAW_TEST(draw_manager_sync) + +} // namespace blender::draw
\ No newline at end of file diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 280512a2dd3..b826f033cc0 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -4001,40 +4001,35 @@ static int gpencil_stroke_outline_exec(bContext *C, wmOperator *op) bool changed = false; - float viewmat[4][4], viewinv[4][4]; + float viewmat[4][4]; copy_m4_m4(viewmat, rv3d->viewmat); - copy_m4_m4(viewinv, rv3d->viewinv); switch (view_mode) { case GP_PERIMETER_FRONT: unit_m4(rv3d->viewmat); - rv3d->viewmat[1][1] = 0.0f; - rv3d->viewmat[1][2] = -1.0f; + viewmat[1][1] = 0.0f; + viewmat[1][2] = -1.0f; - rv3d->viewmat[2][1] = 1.0f; - rv3d->viewmat[2][2] = 0.0f; + viewmat[2][1] = 1.0f; + viewmat[2][2] = 0.0f; - rv3d->viewmat[3][2] = -10.0f; - invert_m4_m4(rv3d->viewinv, rv3d->viewmat); + viewmat[3][2] = -10.0f; break; case GP_PERIMETER_SIDE: - zero_m4(rv3d->viewmat); - rv3d->viewmat[0][2] = 1.0f; - rv3d->viewmat[1][0] = 1.0f; - rv3d->viewmat[2][1] = 1.0f; - rv3d->viewmat[3][3] = 1.0f; - invert_m4_m4(rv3d->viewinv, rv3d->viewmat); + zero_m4(viewmat); + viewmat[0][2] = 1.0f; + viewmat[1][0] = 1.0f; + viewmat[2][1] = 1.0f; + viewmat[3][3] = 1.0f; break; case GP_PERIMETER_TOP: - unit_m4(rv3d->viewmat); - unit_m4(rv3d->viewinv); + unit_m4(viewmat); break; case GP_PERIMETER_CAMERA: { Scene *scene = CTX_data_scene(C); Object *cam_ob = scene->camera; if (cam_ob != NULL) { - invert_m4_m4(rv3d->viewmat, cam_ob->obmat); - copy_m4_m4(rv3d->viewinv, cam_ob->obmat); + invert_m4_m4(viewmat, cam_ob->obmat); } break; } @@ -4107,7 +4102,7 @@ static int gpencil_stroke_outline_exec(bContext *C, wmOperator *op) /* Stroke. */ const float ovr_thickness = keep ? thickness : 0.0f; bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view( - rv3d, gpd, gpl, gps_duplicate, subdivisions, diff_mat, ovr_thickness); + viewmat, gpd, gpl, gps_duplicate, subdivisions, diff_mat, ovr_thickness); gps_perimeter->flag &= ~GP_STROKE_SELECT; /* Assign material. */ switch (material_mode) { @@ -4174,9 +4169,6 @@ static int gpencil_stroke_outline_exec(bContext *C, wmOperator *op) } } - /* Back to view matrix. */ - copy_m4_m4(rv3d->viewmat, viewmat); - copy_m4_m4(rv3d->viewinv, viewinv); if (changed) { /* notifiers */ diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index cbc88b57c65..be2fc566da5 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -945,7 +945,7 @@ static bGPDstroke *gpencil_stroke_to_outline(tGPsdata *p, bGPDstroke *gps) unit_m4(diff_mat); const float outline_thickness = (float)brush->size * gpencil_settings->outline_fac * 0.5f; bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view( - rv3d, p->gpd, gpl, gps_duplicate, 3, diff_mat, outline_thickness); + rv3d->viewmat, p->gpd, gpl, gps_duplicate, 3, diff_mat, outline_thickness); /* Assign material. */ if (gpencil_settings->material_alt == NULL) { gps_perimeter->mat_nr = gps->mat_nr; diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index 22e747e37ad..9a46728097c 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -291,6 +291,8 @@ typedef enum ThemeColorID { TH_WIDGET_EMBOSS, TH_WIDGET_TEXT_CURSOR, + TH_WIDGET_TEXT_SELECTION, + TH_WIDGET_TEXT_HIGHLIGHT, TH_EDITOR_OUTLINE, TH_TRANSPARENT_CHECKER_PRIMARY, diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 933724c9294..ca4918b2e8d 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -153,8 +153,8 @@ void ui_block_to_window(const ARegion *region, uiBlock *block, int *r_x, int *r_ ui_block_to_window_fl(region, block, &fx, &fy); - *r_x = (int)(fx + 0.5f); - *r_y = (int)(fy + 0.5f); + *r_x = (int)lround(fx); + *r_y = (int)lround(fy); } void ui_block_to_region_rctf(const ARegion *region, @@ -232,8 +232,8 @@ void ui_window_to_block(const ARegion *region, uiBlock *block, int *r_x, int *r_ ui_window_to_block_fl(region, block, &fx, &fy); - *r_x = (int)(fx + 0.5f); - *r_y = (int)(fy + 0.5f); + *r_x = (int)lround(fx); + *r_y = (int)lround(fy); } void ui_window_to_region(const ARegion *region, int *r_x, int *r_y) @@ -2363,9 +2363,9 @@ void ui_but_v3_set(uiBut *but, const float vec[3]) } else if (but->pointype == UI_BUT_POIN_CHAR) { char *cp = (char *)but->poin; - cp[0] = (char)(0.5f + vec[0] * 255.0f); - cp[1] = (char)(0.5f + vec[1] * 255.0f); - cp[2] = (char)(0.5f + vec[2] * 255.0f); + cp[0] = (char)lround(vec[0] * 255.0f); + cp[1] = (char)lround(vec[1] * 255.0f); + cp[2] = (char)lround(vec[2] * 255.0f); } else if (but->pointype == UI_BUT_POIN_FLOAT) { float *fp = (float *)but->poin; diff --git a/source/blender/editors/interface/interface_ops.cc b/source/blender/editors/interface/interface_ops.cc index 7649c80d700..a42e08c59d5 100644 --- a/source/blender/editors/interface/interface_ops.cc +++ b/source/blender/editors/interface/interface_ops.cc @@ -1015,7 +1015,7 @@ static void override_idtemplate_menu_draw(const bContext *UNUSED(C), Menu *menu) uiItemO(layout, IFACE_("Clear"), ICON_NONE, "UI_OT_override_idtemplate_clear"); } -static void override_idtemplate_menu(void) +static void override_idtemplate_menu() { MenuType *mt; @@ -1574,8 +1574,13 @@ static bool jump_to_target_button(bContext *C, bool poll) char *str_ptr = RNA_property_string_get_alloc( &ptr, prop, str_buf, sizeof(str_buf), nullptr); - int found = RNA_property_collection_lookup_string( - &coll_search->search_ptr, coll_search->search_prop, str_ptr, &target_ptr); + int found = 0; + /* Jump to target only works with search properties currently, not search callbacks yet. + * See ui_but_add_search. */ + if (coll_search->search_prop != NULL) { + found = RNA_property_collection_lookup_string( + &coll_search->search_ptr, coll_search->search_prop, str_ptr, &target_ptr); + } if (str_ptr != str_buf) { MEM_freeN(str_ptr); diff --git a/source/blender/editors/interface/interface_utils.cc b/source/blender/editors/interface/interface_utils.cc index b7ca2d9aa11..4b94834ce97 100644 --- a/source/blender/editors/interface/interface_utils.cc +++ b/source/blender/editors/interface/interface_utils.cc @@ -787,7 +787,7 @@ int UI_calc_float_precision(int prec, double value) */ value = fabs(value); if ((value < pow10_neg[prec]) && (value > (1.0 / max_pow))) { - int value_i = (int)((value * max_pow) + 0.5); + int value_i = (int)lround(value * max_pow); if (value_i != 0) { const int prec_span = 3; /* show: 0.01001, 5 would allow 0.0100001 for eg. */ int test_prec; diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index cfdcd08df4a..93b94d42d39 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -895,6 +895,12 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_WIDGET_TEXT_CURSOR: cp = btheme->tui.widget_text_cursor; break; + case TH_WIDGET_TEXT_SELECTION: + cp = btheme->tui.wcol_text.item; + break; + case TH_WIDGET_TEXT_HIGHLIGHT: + cp = btheme->tui.wcol_text.text_sel; + break; case TH_TRANSPARENT_CHECKER_PRIMARY: cp = btheme->tui.transparent_checker_primary; diff --git a/source/blender/editors/object/object_vgroup.cc b/source/blender/editors/object/object_vgroup.cc index 1a73d7e6569..310d73daa76 100644 --- a/source/blender/editors/object/object_vgroup.cc +++ b/source/blender/editors/object/object_vgroup.cc @@ -513,7 +513,7 @@ static void mesh_defvert_mirror_update_internal(Object *ob, else { /* Single vgroup. */ MDeformWeight *dw = BKE_defvert_ensure_index(dvert_dst, - BKE_object_defgroup_flip_index(ob, def_nr, 1)); + BKE_object_defgroup_flip_index(ob, def_nr, true)); if (dw) { dw->weight = BKE_defvert_find_weight(dvert_src, def_nr); } @@ -2938,10 +2938,10 @@ void OBJECT_OT_vertex_group_remove(wmOperatorType *ot) ot->flag = /*OPTYPE_REGISTER|*/ OPTYPE_UNDO; /* properties */ - PropertyRNA *prop = RNA_def_boolean(ot->srna, "all", 0, "All", "Remove all vertex groups"); + PropertyRNA *prop = RNA_def_boolean(ot->srna, "all", false, "All", "Remove all vertex groups"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean( - ot->srna, "all_unlocked", 0, "All Unlocked", "Remove all unlocked vertex groups"); + ot->srna, "all_unlocked", false, "All Unlocked", "Remove all unlocked vertex groups"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } @@ -3068,9 +3068,11 @@ void OBJECT_OT_vertex_group_remove_from(wmOperatorType *ot) ot->flag = /*OPTYPE_REGISTER|*/ OPTYPE_UNDO; /* properties */ - prop = RNA_def_boolean(ot->srna, "use_all_groups", 0, "All Groups", "Remove from all groups"); + prop = RNA_def_boolean( + ot->srna, "use_all_groups", false, "All Groups", "Remove from all groups"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "use_all_verts", 0, "All Vertices", "Clear the active group"); + prop = RNA_def_boolean( + ot->srna, "use_all_verts", false, "All Vertices", "Clear the active group"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } @@ -3870,7 +3872,7 @@ void OBJECT_OT_vertex_group_mirror(wmOperatorType *ot) RNA_def_boolean( ot->srna, "use_topology", - 0, + false, "Topology Mirror", "Use topology based mirroring (for when both sides of mesh have matching, unique topology)"); } diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 262b33dc0a3..a4db5edf0f6 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -1585,102 +1585,75 @@ void draw_nodespace_back_pix(const bContext &C, GPU_matrix_pop(); } -bool node_link_bezier_handles(const View2D *v2d, - const SpaceNode *snode, - const bNodeLink &link, - float vec[4][2]) +static float2 socket_link_connection_location(const bNodeSocket &socket, const bNodeLink &link) { - float cursor[2] = {0.0f, 0.0f}; - - /* this function can be called with snode null (via cut_links_intersect) */ - /* XXX map snode->runtime->cursor back to view space */ - if (snode) { - cursor[0] = snode->runtime->cursor[0] * UI_DPI_FAC; - cursor[1] = snode->runtime->cursor[1] * UI_DPI_FAC; - } - - /* in v0 and v3 we put begin/end points */ - if (link.fromsock) { - vec[0][0] = link.fromsock->locx; - vec[0][1] = link.fromsock->locy; - if (link.fromsock->flag & SOCK_MULTI_INPUT) { - const float2 position = node_link_calculate_multi_input_position( - {link.fromsock->locx, link.fromsock->locy}, - link.fromsock->total_inputs - 1, - link.fromsock->total_inputs); - copy_v2_v2(vec[0], position); - } - } - else { - if (snode == nullptr) { - return false; - } - copy_v2_v2(vec[0], cursor); - } - if (link.tosock) { - vec[3][0] = link.tosock->locx; - vec[3][1] = link.tosock->locy; - if (!(link.tonode->flag & NODE_HIDDEN) && link.tosock->flag & SOCK_MULTI_INPUT) { - const float2 position = node_link_calculate_multi_input_position( - {link.tosock->locx, link.tosock->locy}, - link.multi_input_socket_index, - link.tosock->total_inputs); - copy_v2_v2(vec[3], position); - } - } - else { - if (snode == nullptr) { - return false; - } - copy_v2_v2(vec[3], cursor); + const float2 socket_location(socket.locx, socket.locy); + if (socket.flag & SOCK_MULTI_INPUT && socket.in_out == SOCK_IN) { + return node_link_calculate_multi_input_position( + socket_location, link.multi_input_socket_index, socket.total_inputs); } + return socket_location; +} - /* may be called outside of drawing (so pass spacetype) */ - int curving = UI_GetThemeValueType(TH_NODE_CURVING, SPACE_NODE); - +static void calculate_inner_link_bezier_points(std::array<float2, 4> &points) +{ + const int curving = UI_GetThemeValueType(TH_NODE_CURVING, SPACE_NODE); if (curving == 0) { /* Straight line: align all points. */ - interp_v2_v2v2(vec[1], vec[0], vec[3], 1.0f / 3.0f); - interp_v2_v2v2(vec[2], vec[0], vec[3], 2.0f / 3.0f); - return true; + points[1] = math::interpolate(points[0], points[3], 1.0f / 3.0f); + points[2] = math::interpolate(points[0], points[3], 2.0f / 3.0f); } + else { + const float dist = curving * 0.1f * math::distance(points[0].x, points[3].x); - const float dist = curving * 0.10f * fabsf(vec[0][0] - vec[3][0]); + points[1].x = points[0].x + dist; + points[1].y = points[0].y; - vec[1][0] = vec[0][0] + dist; - vec[1][1] = vec[0][1]; + points[2].x = points[3].x - dist; + points[2].y = points[3].y; + } +} - vec[2][0] = vec[3][0] - dist; - vec[2][1] = vec[3][1]; +static std::array<float2, 4> node_link_bezier_points(const bNodeLink &link) +{ + std::array<float2, 4> points; + points[0] = socket_link_connection_location(*link.fromsock, link); + points[3] = socket_link_connection_location(*link.tosock, link); + calculate_inner_link_bezier_points(points); + return points; +} - if (v2d && min_ffff(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) > v2d->cur.xmax) { - return false; /* clipped */ +static bool node_link_draw_is_visible(const View2D &v2d, const std::array<float2, 4> &points) +{ + if (min_ffff(points[0].x, points[1].x, points[2].x, points[3].x) > v2d.cur.xmax) { + return false; } - if (v2d && max_ffff(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) < v2d->cur.xmin) { - return false; /* clipped */ + if (max_ffff(points[0].x, points[1].x, points[2].x, points[3].x) < v2d.cur.xmin) { + return false; } - return true; } -bool node_link_bezier_points(const View2D *v2d, - const SpaceNode *snode, - const bNodeLink &link, - float coord_array[][2], - const int resol) +void node_link_bezier_points_evaluated(const bNodeLink &link, + std::array<float2, NODE_LINK_RESOL + 1> &coords) { - float vec[4][2]; + const std::array<float2, 4> points = node_link_bezier_points(link); - if (node_link_bezier_handles(v2d, snode, link, vec)) { - /* always do all three, to prevent data hanging around */ - BKE_curve_forward_diff_bezier( - vec[0][0], vec[1][0], vec[2][0], vec[3][0], coord_array[0] + 0, resol, sizeof(float[2])); - BKE_curve_forward_diff_bezier( - vec[0][1], vec[1][1], vec[2][1], vec[3][1], coord_array[0] + 1, resol, sizeof(float[2])); - - return true; - } - return false; + /* The extra +1 in size is required by these functions and would be removed ideally. */ + BKE_curve_forward_diff_bezier(points[0].x, + points[1].x, + points[2].x, + points[3].x, + &coords[0].x, + NODE_LINK_RESOL, + sizeof(float2)); + BKE_curve_forward_diff_bezier(points[0].y, + points[1].y, + points[2].y, + points[3].y, + &coords[0].y, + NODE_LINK_RESOL, + sizeof(float2)); } #define NODELINK_GROUP_SIZE 256 @@ -1959,10 +1932,7 @@ struct NodeLinkDrawConfig { }; static void nodelink_batch_add_link(const SpaceNode &snode, - const float2 &p0, - const float2 &p1, - const float2 &p2, - const float2 &p3, + const std::array<float2, 4> &points, const NodeLinkDrawConfig &draw_config) { /* Only allow these colors. If more is needed, you need to modify the shader accordingly. */ @@ -1973,10 +1943,10 @@ static void nodelink_batch_add_link(const SpaceNode &snode, BLI_assert(ELEM(draw_config.th_col3, TH_WIRE, TH_REDALERT, -1)); g_batch_link.count++; - copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p0_step), p0); - copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p1_step), p1); - copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p2_step), p2); - copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p3_step), p3); + copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p0_step), points[0]); + copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p1_step), points[1]); + copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p2_step), points[2]); + copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p3_step), points[3]); char *colid = (char *)GPU_vertbuf_raw_step(&g_batch_link.colid_step); colid[0] = nodelink_get_color_id(draw_config.th_col1); colid[1] = nodelink_get_color_id(draw_config.th_col2); @@ -2012,19 +1982,17 @@ static void node_draw_link_end_marker(const float2 center, static void node_draw_link_end_markers(const bNodeLink &link, const NodeLinkDrawConfig &draw_config, - const float handles[4][2], + const std::array<float2, 4> &points, const bool outline) { const float radius = (outline ? 0.65f : 0.45f) * NODE_SOCKSIZE; if (link.fromsock) { - const float2 link_start(handles[0]); node_draw_link_end_marker( - link_start, radius, outline ? draw_config.outline_color : draw_config.start_color); + points[0], radius, outline ? draw_config.outline_color : draw_config.start_color); } if (link.tosock) { - const float2 link_end(handles[3]); node_draw_link_end_marker( - link_end, radius, outline ? draw_config.outline_color : draw_config.end_color); + points[3], radius, outline ? draw_config.outline_color : draw_config.end_color); } } @@ -2130,7 +2098,7 @@ static NodeLinkDrawConfig nodelink_get_draw_config(const bContext &C, static void node_draw_link_bezier_ex(const SpaceNode &snode, const NodeLinkDrawConfig &draw_config, - const float handles[4][2]) + const std::array<float2, 4> &points) { if (g_batch_link.batch == nullptr) { nodelink_batch_init(); @@ -2138,12 +2106,12 @@ static void node_draw_link_bezier_ex(const SpaceNode &snode, if (g_batch_link.enabled && !draw_config.highlighted) { /* Add link to batch. */ - nodelink_batch_add_link(snode, handles[0], handles[1], handles[2], handles[3], draw_config); + nodelink_batch_add_link(snode, points, draw_config); } else { NodeLinkData node_link_data; - for (int i = 0; i < 4; i++) { - copy_v2_v2(node_link_data.bezierPts[i], handles[i]); + for (const int i : IndexRange(points.size())) { + copy_v2_v2(node_link_data.bezierPts[i], points[i]); } copy_v4_v4(node_link_data.colors[0], draw_config.outline_color); @@ -2180,14 +2148,14 @@ void node_draw_link_bezier(const bContext &C, const int th_col3, const bool selected) { - float handles[4][2]; - if (!node_link_bezier_handles(&v2d, &snode, link, handles)) { + const std::array<float2, 4> points = node_link_bezier_points(link); + if (!node_link_draw_is_visible(v2d, points)) { return; } const NodeLinkDrawConfig draw_config = nodelink_get_draw_config( C, v2d, snode, link, th_col1, th_col2, th_col3, selected); - node_draw_link_bezier_ex(snode, draw_config, handles); + node_draw_link_bezier_ex(snode, draw_config, points); } void node_draw_link(const bContext &C, @@ -2236,6 +2204,17 @@ void node_draw_link(const bContext &C, node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3, selected); } +static std::array<float2, 4> node_link_bezier_points_dragged(const SpaceNode &snode, + const bNodeLink &link) +{ + const float2 cursor = snode.runtime->cursor * UI_DPI_FAC; + std::array<float2, 4> points; + points[0] = link.fromsock ? socket_link_connection_location(*link.fromsock, link) : cursor; + points[3] = link.tosock ? socket_link_connection_location(*link.tosock, link) : cursor; + calculate_inner_link_bezier_points(points); + return points; +} + void node_draw_link_dragged(const bContext &C, const View2D &v2d, const SpaceNode &snode, @@ -2245,19 +2224,16 @@ void node_draw_link_dragged(const bContext &C, return; } - float handles[4][2]; - if (!node_link_bezier_handles(&v2d, &snode, link, handles)) { - return; - } + const std::array<float2, 4> points = node_link_bezier_points_dragged(snode, link); const NodeLinkDrawConfig draw_config = nodelink_get_draw_config( C, v2d, snode, link, TH_ACTIVE, TH_ACTIVE, TH_WIRE, true); /* End marker outline. */ - node_draw_link_end_markers(link, draw_config, handles, true); + node_draw_link_end_markers(link, draw_config, points, true); /* Link. */ - node_draw_link_bezier_ex(snode, draw_config, handles); + node_draw_link_bezier_ex(snode, draw_config, points); /* End marker fill. */ - node_draw_link_end_markers(link, draw_config, handles, false); + node_draw_link_end_markers(link, draw_config, points, false); } } // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc index 9014e36c4e2..553d4013324 100644 --- a/source/blender/editors/space_node/link_drag_search.cc +++ b/source/blender/editors/space_node/link_drag_search.cc @@ -293,7 +293,7 @@ static uiBlock *create_search_popup_block(bContext *C, ARegion *region, void *ar 0, nullptr); - const int offset[2] = {0, -UI_UNIT_Y}; + const int2 offset = {0, -UI_UNIT_Y}; UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, offset); return block; } diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 02684d92eaf..7e46877d0ba 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -5,6 +5,8 @@ * \ingroup spnode */ +#include <numeric> + #include "MEM_guardedalloc.h" #include "DNA_collection_types.h" @@ -20,6 +22,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "BKE_node_tree_update.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -101,198 +104,113 @@ bNode *add_static_node(const bContext &C, int type, const float2 &location) /** \name Add Reroute Operator * \{ */ -static bool add_reroute_intersect_check(const bNodeLink &link, - float mcoords[][2], - int tot, - float result[2]) +std::optional<float2> link_path_intersection(const bNodeLink &link, const Span<float2> path) { - float coord_array[NODE_LINK_RESOL + 1][2]; - - if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) { - for (int i = 0; i < tot - 1; i++) { - for (int b = 0; b < NODE_LINK_RESOL; b++) { - if (isect_seg_seg_v2_point( - mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1], result) > 0) { - return true; - } + std::array<float2, NODE_LINK_RESOL + 1> coords; + node_link_bezier_points_evaluated(link, coords); + + for (const int i : path.index_range().drop_back(1)) { + for (const int j : IndexRange(NODE_LINK_RESOL)) { + float2 result; + if (isect_seg_seg_v2_point(path[i], path[i + 1], coords[j], coords[j + 1], result) > 0) { + return result; } } } - return false; -} -struct bNodeSocketLink { - struct bNodeSocketLink *next, *prev; + return std::nullopt; +} - struct bNodeSocket *sock; - struct bNodeLink *link; - float point[2]; +struct RerouteCutsForSocket { + /* The output socket's owner node. */ + bNode *from_node; + /* Intersected links connected to the socket and their path intersection locations. */ + Map<bNodeLink *, float2> links; }; -static bNodeSocketLink *add_reroute_insert_socket_link(ListBase *lb, - bNodeSocket *sock, - bNodeLink *link, - const float point[2]) +static int add_reroute_exec(bContext *C, wmOperator *op) { - bNodeSocketLink *socklink, *prev; - - socklink = MEM_cnew<bNodeSocketLink>("socket link"); - socklink->sock = sock; - socklink->link = link; - copy_v2_v2(socklink->point, point); + const ARegion ®ion = *CTX_wm_region(C); + SpaceNode &snode = *CTX_wm_space_node(C); + bNodeTree &ntree = *snode.edittree; - for (prev = (bNodeSocketLink *)lb->last; prev; prev = prev->prev) { - if (prev->sock == sock) { + Vector<float2> path; + RNA_BEGIN (op->ptr, itemptr, "path") { + float2 loc_region; + RNA_float_get_array(&itemptr, "loc", loc_region); + float2 loc_view; + UI_view2d_region_to_view(®ion.v2d, loc_region.x, loc_region.y, &loc_view.x, &loc_view.y); + path.append(loc_view); + if (path.size() >= 256) { break; } } - BLI_insertlinkafter(lb, prev, socklink); - return socklink; -} - -static bNodeSocketLink *add_reroute_do_socket_section(bContext *C, - bNodeSocketLink *socklink, - int in_out) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - bNode *reroute_node = nullptr; - bNodeSocket *cursock = socklink->sock; - float insert_point[2]; - int num_links; - - zero_v2(insert_point); - num_links = 0; - - while (socklink && socklink->sock == cursock) { - if (!(socklink->link->flag & NODE_LINK_TEST)) { - socklink->link->flag |= NODE_LINK_TEST; - - /* create the reroute node for this cursock */ - if (!reroute_node) { - reroute_node = nodeAddStaticNode(C, ntree, NODE_REROUTE); - - /* add a single link to/from the reroute node to replace multiple links */ - if (in_out == SOCK_OUT) { - nodeAddLink(ntree, - socklink->link->fromnode, - socklink->link->fromsock, - reroute_node, - (bNodeSocket *)reroute_node->inputs.first); - } - else { - nodeAddLink(ntree, - reroute_node, - (bNodeSocket *)reroute_node->outputs.first, - socklink->link->tonode, - socklink->link->tosock); - } - } - - /* insert the reroute node into the link */ - if (in_out == SOCK_OUT) { - socklink->link->fromnode = reroute_node; - socklink->link->fromsock = (bNodeSocket *)reroute_node->outputs.first; - } - else { - socklink->link->tonode = reroute_node; - socklink->link->tosock = (bNodeSocket *)reroute_node->inputs.first; - } - - add_v2_v2(insert_point, socklink->point); - num_links++; - } - socklink = socklink->next; - } - - if (num_links > 0) { - /* average cut point from shared links */ - mul_v2_fl(insert_point, 1.0f / num_links); - - reroute_node->locx = insert_point[0] / UI_DPI_FAC; - reroute_node->locy = insert_point[1] / UI_DPI_FAC; + RNA_END; - LISTBASE_FOREACH_BACKWARD (bNode *, frame_node, &ntree->nodes) { - if (frame_node->type == NODE_FRAME && BLI_rctf_isect_pt_v(&frame_node->totr, insert_point)) { - nodeAttachNode(reroute_node, frame_node); - break; - } - } + if (path.is_empty()) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } - return socklink; -} + ntree.ensure_topology_cache(); + const Vector<bNode *> frame_nodes = ntree.nodes_by_type("NodeFrame"); -static int add_reroute_exec(bContext *C, wmOperator *op) -{ - SpaceNode &snode = *CTX_wm_space_node(C); - ARegion ®ion = *CTX_wm_region(C); - bNodeTree &ntree = *snode.edittree; - float mcoords[256][2]; - int i = 0; + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + node_deselect_all(snode); - /* Get the cut path */ - RNA_BEGIN (op->ptr, itemptr, "path") { - float loc[2]; + /* All link "cuts" that start at a particular output socket. Deduplicating new reroutes per + * output socket is useful because it allows reusing reroutes for connected intersections. + * Further deduplication using the second map means we only have one cut per link.*/ + Map<bNodeSocket *, RerouteCutsForSocket> cuts_per_socket; - RNA_float_get_array(&itemptr, "loc", loc); - UI_view2d_region_to_view( - ®ion.v2d, (short)loc[0], (short)loc[1], &mcoords[i][0], &mcoords[i][1]); - i++; - if (i >= 256) { - break; + LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { + if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { + continue; + } + const std::optional<float2> intersection = link_path_intersection(*link, path); + if (!intersection) { + continue; } + RerouteCutsForSocket &from_cuts = cuts_per_socket.lookup_or_add_default(link->fromsock); + from_cuts.from_node = link->fromnode; + from_cuts.links.add(link, *intersection); } - RNA_END; - - if (i > 1) { - ListBase output_links, input_links; - bNodeSocketLink *socklink; - float insert_point[2]; - - /* always first */ - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - node_deselect_all(snode); + for (const auto item : cuts_per_socket.items()) { + const Map<bNodeLink *, float2> &cuts = item.value.links; - /* Find cut links and sort them by sockets */ - BLI_listbase_clear(&output_links); - BLI_listbase_clear(&input_links); + bNode *reroute = nodeAddStaticNode(C, &ntree, NODE_REROUTE); - LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { - if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { - continue; - } - if (add_reroute_intersect_check(*link, mcoords, i, insert_point)) { - add_reroute_insert_socket_link(&output_links, link->fromsock, link, insert_point); - add_reroute_insert_socket_link(&input_links, link->tosock, link, insert_point); + nodeAddLink(&ntree, + item.value.from_node, + item.key, + reroute, + static_cast<bNodeSocket *>(reroute->inputs.first)); - /* Clear flag */ - link->flag &= ~NODE_LINK_TEST; - } + /* Reconnect links from the original output socket to the new reroute. */ + for (bNodeLink *link : cuts.keys()) { + link->fromnode = reroute; + link->fromsock = static_cast<bNodeSocket *>(reroute->outputs.first); + BKE_ntree_update_tag_link_changed(&ntree); } - /* Create reroute nodes for intersected links. - * Only one reroute if links share the same input/output socket. - */ - socklink = (bNodeSocketLink *)output_links.first; - while (socklink) { - socklink = add_reroute_do_socket_section(C, socklink, SOCK_OUT); - } - socklink = (bNodeSocketLink *)input_links.first; - while (socklink) { - socklink = add_reroute_do_socket_section(C, socklink, SOCK_IN); + /* Place the new reroute at the average location of all connected cuts. */ + const float2 loc = std::accumulate(cuts.values().begin(), cuts.values().end(), float2(0)) / + cuts.size() / UI_DPI_FAC; + reroute->locx = loc.x; + reroute->locy = loc.y; + + /* Attach the reroute node to frame nodes behind it. */ + for (const int i : frame_nodes.index_range()) { + bNode *frame_node = frame_nodes.last(i); + if (BLI_rctf_isect_pt_v(&frame_node->totr, loc)) { + nodeAttachNode(reroute, frame_node); + break; + } } - - BLI_freelistN(&output_links); - BLI_freelistN(&input_links); - - /* always last */ - ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree); - return OPERATOR_FINISHED; } - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree); + return OPERATOR_FINISHED; } void NODE_OT_add_reroute(wmOperatorType *ot) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index a5b4d2bcf4e..225bae89c66 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -340,13 +340,13 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, bool add_output_space = false; int buty; - LISTBASE_FOREACH (bNodeSocket *, nsock, &node.outputs) { - if (nodeSocketIsHidden(nsock)) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node.outputs) { + if (nodeSocketIsHidden(socket)) { continue; } PointerRNA sockptr; - RNA_pointer_create(&ntree.id, &RNA_NodeSocket, nsock, &sockptr); + RNA_pointer_create(&ntree.id, &RNA_NodeSocket, socket, &sockptr); uiLayout *layout = UI_block_layout(&block, UI_LAYOUT_VERTICAL, @@ -369,10 +369,10 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, /* Align output buttons to the right. */ uiLayout *row = uiLayoutRow(layout, true); uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT); - const char *socket_label = nodeSocketLabel(nsock); - nsock->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label)); + const char *socket_label = nodeSocketLabel(socket); + socket->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label)); - node_socket_add_tooltip(&ntree, &node, nsock, row); + node_socket_add_tooltip(ntree, node, *socket, *row); UI_block_align_end(&block); UI_block_layout_resolve(&block, nullptr, &buty); @@ -381,11 +381,11 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, buty = min_ii(buty, dy - NODE_DY); /* Round the socket location to stop it from jiggling. */ - nsock->locx = round(loc.x + NODE_WIDTH(node)); - nsock->locy = round(dy - NODE_DYS); + socket->locx = round(loc.x + NODE_WIDTH(node)); + socket->locy = round(dy - NODE_DYS); dy = buty; - if (nsock->next) { + if (socket->next) { dy -= NODE_SOCKDY; } @@ -463,20 +463,20 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, } /* Input sockets. */ - LISTBASE_FOREACH (bNodeSocket *, nsock, &node.inputs) { - if (nodeSocketIsHidden(nsock)) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) { + if (nodeSocketIsHidden(socket)) { continue; } PointerRNA sockptr; - RNA_pointer_create(&ntree.id, &RNA_NodeSocket, nsock, &sockptr); + RNA_pointer_create(&ntree.id, &RNA_NodeSocket, socket, &sockptr); /* Add the half the height of a multi-input socket to cursor Y * to account for the increased height of the taller sockets. */ float multi_input_socket_offset = 0.0f; - if (nsock->flag & SOCK_MULTI_INPUT) { - if (nsock->total_inputs > 2) { - multi_input_socket_offset = (nsock->total_inputs - 2) * NODE_MULTI_INPUT_LINK_GAP; + if (socket->flag & SOCK_MULTI_INPUT) { + if (socket->total_inputs > 2) { + multi_input_socket_offset = (socket->total_inputs - 2) * NODE_MULTI_INPUT_LINK_GAP; } } dy -= multi_input_socket_offset * 0.5f; @@ -501,10 +501,10 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, uiLayout *row = uiLayoutRow(layout, true); - const char *socket_label = nodeSocketLabel(nsock); - nsock->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label)); + const char *socket_label = nodeSocketLabel(socket); + socket->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label)); - node_socket_add_tooltip(&ntree, &node, nsock, row); + node_socket_add_tooltip(ntree, node, *socket, *row); UI_block_align_end(&block); UI_block_layout_resolve(&block, nullptr, &buty); @@ -512,12 +512,12 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, /* Ensure minimum socket height in case layout is empty. */ buty = min_ii(buty, dy - NODE_DY); - nsock->locx = loc.x; + socket->locx = loc.x; /* Round the socket vertical position to stop it from jiggling. */ - nsock->locy = round(dy - NODE_DYS); + socket->locy = round(dy - NODE_DYS); dy = buty - multi_input_socket_offset * 0.5; - if (nsock->next) { + if (socket->next) { dy -= NODE_SOCKDY; } } @@ -555,13 +555,13 @@ static void node_update_hidden(bNode &node, uiBlock &block) loc.y = round(loc.y); /* Calculate minimal radius. */ - LISTBASE_FOREACH (bNodeSocket *, nsock, &node.inputs) { - if (!nodeSocketIsHidden(nsock)) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) { + if (!nodeSocketIsHidden(socket)) { totin++; } } - LISTBASE_FOREACH (bNodeSocket *, nsock, &node.outputs) { - if (!nodeSocketIsHidden(nsock)) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node.outputs) { + if (!nodeSocketIsHidden(socket)) { totout++; } } @@ -581,11 +581,11 @@ static void node_update_hidden(bNode &node, uiBlock &block) float rad = (float)M_PI / (1.0f + (float)totout); float drad = rad; - LISTBASE_FOREACH (bNodeSocket *, nsock, &node.outputs) { - if (!nodeSocketIsHidden(nsock)) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node.outputs) { + if (!nodeSocketIsHidden(socket)) { /* Round the socket location to stop it from jiggling. */ - nsock->locx = round(node.totr.xmax - hiddenrad + sinf(rad) * hiddenrad); - nsock->locy = round(node.totr.ymin + hiddenrad + cosf(rad) * hiddenrad); + socket->locx = round(node.totr.xmax - hiddenrad + sinf(rad) * hiddenrad); + socket->locy = round(node.totr.ymin + hiddenrad + cosf(rad) * hiddenrad); rad += drad; } } @@ -593,11 +593,11 @@ static void node_update_hidden(bNode &node, uiBlock &block) /* Input sockets. */ rad = drad = -(float)M_PI / (1.0f + (float)totin); - LISTBASE_FOREACH (bNodeSocket *, nsock, &node.inputs) { - if (!nodeSocketIsHidden(nsock)) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) { + if (!nodeSocketIsHidden(socket)) { /* Round the socket location to stop it from jiggling. */ - nsock->locx = round(node.totr.xmin + hiddenrad + sinf(rad) * hiddenrad); - nsock->locy = round(node.totr.ymin + hiddenrad + cosf(rad) * hiddenrad); + socket->locx = round(node.totr.xmin + hiddenrad + sinf(rad) * hiddenrad); + socket->locy = round(node.totr.ymin + hiddenrad + cosf(rad) * hiddenrad); rad += drad; } } @@ -720,8 +720,7 @@ static void node_socket_draw_multi_input(const float color[4], const float color_outline[4], const float width, const float height, - const int locx, - const int locy) + const float2 location) { /* The other sockets are drawn with the keyframe shader. There, the outline has a base thickness * that can be varied but always scales with the size the socket is drawn at. Using `U.dpi_fac` @@ -731,10 +730,10 @@ static void node_socket_draw_multi_input(const float color[4], /* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width. */ const rctf rect = { - locx - width + outline_width * 0.5f, - locx + width - outline_width * 0.5f, - locy - height + outline_width * 0.5f, - locy + height - outline_width * 0.5f, + location.x - width + outline_width * 0.5f, + location.x + width - outline_width * 0.5f, + location.y - height + outline_width * 0.5f, + location.y + height - outline_width * 0.5f, }; UI_draw_roundbox_corner_set(UI_CNR_ALL); @@ -776,9 +775,9 @@ void node_socket_color_get(const bContext &C, } struct SocketTooltipData { - bNodeTree *ntree; - bNode *node; - bNodeSocket *socket; + const bNodeTree *ntree; + const bNode *node; + const bNodeSocket *socket; }; static void create_inspection_string_for_generic_value(const GPointer value, std::stringstream &ss) @@ -999,11 +998,11 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo } } -static std::optional<std::string> create_socket_inspection_string(bContext *C, - bNode &node, - bNodeSocket &socket) +static std::optional<std::string> create_socket_inspection_string(const bContext &C, + const bNode &node, + const bNodeSocket &socket) { - SpaceNode *snode = CTX_wm_space_node(C); + const SpaceNode *snode = CTX_wm_space_node(&C); if (snode == nullptr) { return {}; }; @@ -1038,41 +1037,41 @@ static std::optional<std::string> create_socket_inspection_string(bContext *C, return ss.str(); } -static bool node_socket_has_tooltip(bNodeTree *ntree, bNodeSocket *socket) +static bool node_socket_has_tooltip(const bNodeTree &ntree, const bNodeSocket &socket) { - if (ntree->type == NTREE_GEOMETRY) { + if (ntree.type == NTREE_GEOMETRY) { return true; } - if (socket->runtime->declaration != nullptr) { - const blender::nodes::SocketDeclaration &socket_decl = *socket->runtime->declaration; + if (socket.runtime->declaration != nullptr) { + const blender::nodes::SocketDeclaration &socket_decl = *socket.runtime->declaration; return !socket_decl.description().is_empty(); } return false; } -static char *node_socket_get_tooltip(bContext *C, - bNodeTree *ntree, - bNode *node, - bNodeSocket *socket) +static char *node_socket_get_tooltip(const bContext &C, + const bNodeTree &ntree, + const bNode &node, + const bNodeSocket &socket) { std::stringstream output; - if (socket->runtime->declaration != nullptr) { - const blender::nodes::SocketDeclaration &socket_decl = *socket->runtime->declaration; + if (socket.runtime->declaration != nullptr) { + const blender::nodes::SocketDeclaration &socket_decl = *socket.runtime->declaration; blender::StringRef description = socket_decl.description(); if (!description.is_empty()) { output << TIP_(description.data()); } } - if (ntree->type == NTREE_GEOMETRY) { + if (ntree.type == NTREE_GEOMETRY) { if (!output.str().empty()) { output << ".\n\n"; } std::optional<std::string> socket_inspection_str = create_socket_inspection_string( - C, *node, *socket); + C, node, socket); if (socket_inspection_str.has_value()) { output << *socket_inspection_str; } @@ -1082,28 +1081,31 @@ static char *node_socket_get_tooltip(bContext *C, } if (output.str().empty()) { - output << nodeSocketLabel(socket); + output << nodeSocketLabel(&socket); } return BLI_strdup(output.str().c_str()); } -void node_socket_add_tooltip(bNodeTree *ntree, bNode *node, bNodeSocket *sock, uiLayout *layout) +void node_socket_add_tooltip(const bNodeTree &ntree, + const bNode &node, + const bNodeSocket &sock, + uiLayout &layout) { if (!node_socket_has_tooltip(ntree, sock)) { return; } - SocketTooltipData *data = MEM_cnew<SocketTooltipData>(__func__); - data->ntree = ntree; - data->node = node; - data->socket = sock; + SocketTooltipData *data = MEM_new<SocketTooltipData>(__func__); + data->ntree = &ntree; + data->node = &node; + data->socket = &sock; uiLayoutSetTooltipFunc( - layout, + &layout, [](bContext *C, void *argN, const char *UNUSED(tip)) { - SocketTooltipData *data = static_cast<SocketTooltipData *>(argN); - return node_socket_get_tooltip(C, data->ntree, data->node, data->socket); + const SocketTooltipData *data = static_cast<SocketTooltipData *>(argN); + return node_socket_get_tooltip(*C, *data->ntree, *data->node, *data->socket); }, data, MEM_dupallocN, @@ -1123,9 +1125,10 @@ static void node_socket_draw_nested(const bContext &C, const float size, const bool selected) { + const float2 location(sock.locx, sock.locy); + float color[4]; float outline_color[4]; - node_socket_color_get(C, ntree, node_ptr, sock, color); node_socket_outline_color_get(selected, sock.type, outline_color); @@ -1133,15 +1136,15 @@ static void node_socket_draw_nested(const bContext &C, color, outline_color, size, - sock.locx, - sock.locy, + location.x, + location.y, pos_id, col_id, shape_id, size_id, outline_col_id); - if (!node_socket_has_tooltip(&ntree, &sock)) { + if (!node_socket_has_tooltip(ntree, sock)) { return; } @@ -1153,8 +1156,8 @@ static void node_socket_draw_nested(const bContext &C, UI_BTYPE_BUT, 0, ICON_NONE, - sock.locx - size / 2, - sock.locy - size / 2, + location.x - size / 2.0f, + location.y - size / 2.0f, size, size, nullptr, @@ -1164,16 +1167,16 @@ static void node_socket_draw_nested(const bContext &C, 0, nullptr); - SocketTooltipData *data = (SocketTooltipData *)MEM_mallocN(sizeof(SocketTooltipData), __func__); + SocketTooltipData *data = MEM_new<SocketTooltipData>(__func__); data->ntree = &ntree; - data->node = (bNode *)node_ptr.data; + data->node = static_cast<const bNode *>(node_ptr.data); data->socket = &sock; UI_but_func_tooltip_set( but, [](bContext *C, void *argN, const char *UNUSED(tip)) { SocketTooltipData *data = (SocketTooltipData *)argN; - return node_socket_get_tooltip(C, data->ntree, data->node, data->socket); + return node_socket_get_tooltip(*C, *data->ntree, *data->node, *data->socket); }, data, MEM_freeN); @@ -1308,7 +1311,7 @@ static void node_draw_preview(bNodePreview *preview, rctf *prv) } /* Common handle function for operator buttons that need to select the node first. */ -static void node_toggle_button_cb(struct bContext *C, void *node_argv, void *op_argv) +static void node_toggle_button_cb(bContext *C, void *node_argv, void *op_argv) { bNode *node = (bNode *)node_argv; const char *opname = (const char *)op_argv; @@ -1527,7 +1530,8 @@ static void node_draw_sockets(const View2D &v2d, node_socket_color_get(C, ntree, node_ptr, *socket, color); node_socket_outline_color_get(socket->flag & SELECT, socket->type, outline_color); - node_socket_draw_multi_input(color, outline_color, width, height, socket->locx, socket->locy); + const float2 location(socket->locx, socket->locy); + node_socket_draw_multi_input(color, outline_color, width, height, location); } } @@ -2645,13 +2649,13 @@ static void reroute_node_prepare_for_draw(bNode &node) const float2 loc = node_to_view(node, float2(0)); /* reroute node has exactly one input and one output, both in the same place */ - bNodeSocket *nsock = (bNodeSocket *)node.outputs.first; - nsock->locx = loc.x; - nsock->locy = loc.y; + bNodeSocket *socket = (bNodeSocket *)node.outputs.first; + socket->locx = loc.x; + socket->locy = loc.y; - nsock = (bNodeSocket *)node.inputs.first; - nsock->locx = loc.x; - nsock->locy = loc.y; + socket = (bNodeSocket *)node.inputs.first; + socket->locx = loc.x; + socket->locy = loc.y; const float size = 8.0f; node.width = size * 2; @@ -2768,7 +2772,7 @@ static void frame_node_draw_label(const bNodeTree &ntree, BLF_wordwrap(fontid, line_width); LISTBASE_FOREACH (const TextLine *, line, &text->lines) { - struct ResultBLF info; + ResultBLF info; if (line->line[0]) { BLF_position(fontid, x, y, 0); BLF_draw_ex(fontid, line->line, line->len, &info); @@ -3023,8 +3027,9 @@ static void draw_nodetree(const bContext &C, bNodeInstanceKey parent_key) { SpaceNode *snode = CTX_wm_space_node(&C); + ntree.ensure_topology_cache(); - Vector<bNode *> nodes = ntree.nodes; + Span<bNode *> nodes = ntree.all_nodes(); Array<uiBlock *> blocks = node_uiblocks_init(C, nodes); @@ -3104,8 +3109,8 @@ void node_draw_space(const bContext &C, ARegion ®ion) } /* Current View2D center, will be set temporarily for parent node trees. */ - float center[2]; - UI_view2d_center_get(&v2d, ¢er[0], ¢er[1]); + float2 center; + UI_view2d_center_get(&v2d, ¢er.x, ¢er.y); /* Store new view center in path and current edit tree. */ copy_v2_v2(path->view_center, center); diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 7f8a479739f..5f92e514385 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -320,7 +320,7 @@ static void compo_completejob(void *cjv) /** \name Composite Job C API * \{ */ -void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner) +void ED_node_composite_job(const bContext *C, bNodeTree *nodetree, Scene *scene_owner) { using namespace blender::ed::space_node; @@ -464,22 +464,22 @@ void ED_node_set_tree_type(SpaceNode *snode, bNodeTreeType *typeinfo) } } -bool ED_node_is_compositor(struct SpaceNode *snode) +bool ED_node_is_compositor(SpaceNode *snode) { return STREQ(snode->tree_idname, ntreeType_Composite->idname); } -bool ED_node_is_shader(struct SpaceNode *snode) +bool ED_node_is_shader(SpaceNode *snode) { return STREQ(snode->tree_idname, ntreeType_Shader->idname); } -bool ED_node_is_texture(struct SpaceNode *snode) +bool ED_node_is_texture(SpaceNode *snode) { return STREQ(snode->tree_idname, ntreeType_Texture->idname); } -bool ED_node_is_geometry(struct SpaceNode *snode) +bool ED_node_is_geometry(SpaceNode *snode) { return STREQ(snode->tree_idname, ntreeType_Geometry->idname); } @@ -550,7 +550,7 @@ void ED_node_shader_default(const bContext *C, ID *id) } } -void ED_node_composit_default(const bContext *C, struct Scene *sce) +void ED_node_composit_default(const bContext *C, Scene *sce) { /* but lets check it anyway */ if (sce->nodetree) { @@ -921,7 +921,7 @@ static void edit_node_properties_get( /** \name Node Generic * \{ */ -static bool socket_is_occluded(const bNodeSocket &sock, +static bool socket_is_occluded(const float2 &location, const bNode &node_the_socket_belongs_to, const SpaceNode &snode) { @@ -933,7 +933,7 @@ static bool socket_is_occluded(const bNodeSocket &sock, rctf socket_hitbox; const float socket_hitbox_radius = NODE_SOCKSIZE - 0.1f * U.widget_unit; - BLI_rctf_init_pt_radius(&socket_hitbox, float2(sock.locx, sock.locy), socket_hitbox_radius); + BLI_rctf_init_pt_radius(&socket_hitbox, location, socket_hitbox_radius); if (BLI_rctf_inside_rctf(&node->totr, &socket_hitbox)) { return true; } @@ -956,14 +956,14 @@ struct NodeSizeWidget { }; static void node_resize_init( - bContext *C, wmOperator *op, const float cursor[2], const bNode *node, NodeResizeDirection dir) + bContext *C, wmOperator *op, const float2 &cursor, const bNode *node, NodeResizeDirection dir) { NodeSizeWidget *nsw = MEM_cnew<NodeSizeWidget>(__func__); op->customdata = nsw; - nsw->mxstart = cursor[0]; - nsw->mystart = cursor[1]; + nsw->mxstart = cursor.x; + nsw->mystart = cursor.y; /* store old */ nsw->oldlocx = node->locx; @@ -1010,12 +1010,12 @@ static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: { - int mval[2]; + int2 mval; WM_event_drag_start_mval(event, region, mval); float mx, my; - UI_view2d_region_to_view(®ion->v2d, mval[0], mval[1], &mx, &my); - float dx = (mx - nsw->mxstart) / UI_DPI_FAC; - float dy = (my - nsw->mystart) / UI_DPI_FAC; + UI_view2d_region_to_view(®ion->v2d, mval.x, mval.y, &mx, &my); + const float dx = (mx - nsw->mxstart) / UI_DPI_FAC; + const float dy = (my - nsw->mystart) / UI_DPI_FAC; if (node) { float *pwidth = &node->width; @@ -1117,11 +1117,11 @@ static int node_resize_invoke(bContext *C, wmOperator *op, const wmEvent *event) } /* convert mouse coordinates to v2d space */ - float cursor[2]; - int mval[2]; + float2 cursor; + int2 mval; WM_event_drag_start_mval(event, region, mval); - UI_view2d_region_to_view(®ion->v2d, mval[0], mval[1], &cursor[0], &cursor[1]); - const NodeResizeDirection dir = node_get_resize_direction(node, cursor[0], cursor[1]); + UI_view2d_region_to_view(®ion->v2d, mval.x, mval.y, &cursor.x, &cursor.y); + const NodeResizeDirection dir = node_get_resize_direction(node, cursor.x, cursor.y); if (dir == NODE_RESIZE_NONE) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } @@ -1199,21 +1199,22 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set) } /* checks snode->mouse position, and returns found node/socket */ -static bool cursor_isect_multi_input_socket(const float cursor[2], const bNodeSocket &socket) +static bool cursor_isect_multi_input_socket(const float2 &cursor, const bNodeSocket &socket) { const float node_socket_height = node_socket_calculate_height(socket); - rctf multi_socket_rect; + const float2 location(socket.locx, socket.locy); /* `.xmax = socket->locx + NODE_SOCKSIZE * 5.5f` * would be the same behavior as for regular sockets. * But keep it smaller because for multi-input socket you * sometimes want to drag the link to the other side, if you may * accidentally pick the wrong link otherwise. */ + rctf multi_socket_rect; BLI_rctf_init(&multi_socket_rect, - socket.locx - NODE_SOCKSIZE * 4.0f, - socket.locx + NODE_SOCKSIZE * 2.0f, - socket.locy - node_socket_height, - socket.locy + node_socket_height); - if (BLI_rctf_isect_pt(&multi_socket_rect, cursor[0], cursor[1])) { + location.x - NODE_SOCKSIZE * 4.0f, + location.x + NODE_SOCKSIZE * 2.0f, + location.y - node_socket_height, + location.y + node_socket_height); + if (BLI_rctf_isect_pt(&multi_socket_rect, cursor.x, cursor.y)) { return true; } return false; @@ -1251,17 +1252,18 @@ bool node_find_indicated_socket(SpaceNode &snode, if (in_out & SOCK_IN) { LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { if (!nodeSocketIsHidden(sock)) { + const float2 location(sock->locx, sock->locy); if (sock->flag & SOCK_MULTI_INPUT && !(node->flag & NODE_HIDDEN)) { if (cursor_isect_multi_input_socket(cursor, *sock)) { - if (!socket_is_occluded(*sock, *node, snode)) { + if (!socket_is_occluded(location, *node, snode)) { *nodep = node; *sockp = sock; return true; } } } - else if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) { - if (!socket_is_occluded(*sock, *node, snode)) { + else if (BLI_rctf_isect_pt(&rect, location.x, location.y)) { + if (!socket_is_occluded(location, *node, snode)) { *nodep = node; *sockp = sock; return true; @@ -1273,8 +1275,9 @@ bool node_find_indicated_socket(SpaceNode &snode, if (in_out & SOCK_OUT) { LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { if (!nodeSocketIsHidden(sock)) { - if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) { - if (!socket_is_occluded(*sock, *node, snode)) { + const float2 location(sock->locx, sock->locy); + if (BLI_rctf_isect_pt(&rect, location.x, location.y)) { + if (!socket_is_occluded(location, *node, snode)) { *nodep = node; *sockp = sock; return true; @@ -1300,11 +1303,12 @@ float node_link_dim_factor(const View2D &v2d, const bNodeLink &link) return 1.0f; } + const float2 from(link.fromsock->locx, link.fromsock->locy); + const float2 to(link.tosock->locx, link.tosock->locy); + const float min_endpoint_distance = std::min( - std::max(BLI_rctf_length_x(&v2d.cur, link.fromsock->locx), - BLI_rctf_length_y(&v2d.cur, link.fromsock->locy)), - std::max(BLI_rctf_length_x(&v2d.cur, link.tosock->locx), - BLI_rctf_length_y(&v2d.cur, link.tosock->locy))); + std::max(BLI_rctf_length_x(&v2d.cur, from.x), BLI_rctf_length_y(&v2d.cur, from.y)), + std::max(BLI_rctf_length_x(&v2d.cur, to.x), BLI_rctf_length_y(&v2d.cur, to.y))); if (min_endpoint_distance == 0.0f) { return 1.0f; @@ -1363,7 +1367,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) bNode *lastnode = (bNode *)ntree->nodes.last; LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->flag & SELECT) { - bNode *new_node = blender::bke::node_copy_with_mapping( + bNode *new_node = bke::node_copy_with_mapping( ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map); node_map.add_new(node, new_node); changed = true; @@ -2230,12 +2234,12 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) if (node->flag & SELECT) { /* No ID refcounting, this node is virtual, * detached from any actual Blender data currently. */ - bNode *new_node = blender::bke::node_copy_with_mapping(nullptr, - *node, - LIB_ID_CREATE_NO_USER_REFCOUNT | - LIB_ID_CREATE_NO_MAIN, - false, - socket_map); + bNode *new_node = bke::node_copy_with_mapping(nullptr, + *node, + LIB_ID_CREATE_NO_USER_REFCOUNT | + LIB_ID_CREATE_NO_MAIN, + false, + socket_map); node_map.add_new(node, new_node); } } @@ -2355,11 +2359,11 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) node_deselect_all(*snode); /* calculate "barycenter" for placing on mouse cursor */ - float center[2] = {0.0f, 0.0f}; + float2 center = {0.0f, 0.0f}; int num_nodes = 0; LISTBASE_FOREACH_INDEX (bNode *, node, clipboard_nodes_lb, num_nodes) { - center[0] += BLI_rctf_cent_x(&node->totr); - center[1] += BLI_rctf_cent_y(&node->totr); + center.x += BLI_rctf_cent_x(&node->totr); + center.y += BLI_rctf_cent_y(&node->totr); } mul_v2_fl(center, 1.0 / num_nodes); @@ -2368,7 +2372,7 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) /* copy nodes from clipboard */ LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) { - bNode *new_node = blender::bke::node_copy_with_mapping( + bNode *new_node = bke::node_copy_with_mapping( ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map); node_map.add_new(node, new_node); } diff --git a/source/blender/editors/space_node/node_gizmo.cc b/source/blender/editors/space_node/node_gizmo.cc index 4f27f9baabc..f9126556b71 100644 --- a/source/blender/editors/space_node/node_gizmo.cc +++ b/source/blender/editors/space_node/node_gizmo.cc @@ -49,14 +49,14 @@ static void node_gizmo_calc_matrix_space(const SpaceNode *snode, static void node_gizmo_calc_matrix_space_with_image_dims(const SpaceNode *snode, const ARegion *region, - const float image_dims[2], + const float2 &image_dims, float matrix_space[4][4]) { unit_m4(matrix_space); - mul_v3_fl(matrix_space[0], snode->zoom * image_dims[0]); - mul_v3_fl(matrix_space[1], snode->zoom * image_dims[1]); - matrix_space[3][0] = ((region->winx / 2) + snode->xof) - ((image_dims[0] / 2.0f) * snode->zoom); - matrix_space[3][1] = ((region->winy / 2) + snode->yof) - ((image_dims[1] / 2.0f) * snode->zoom); + mul_v3_fl(matrix_space[0], snode->zoom * image_dims.x); + mul_v3_fl(matrix_space[1], snode->zoom * image_dims.y); + matrix_space[3][0] = ((region->winx / 2) + snode->xof) - ((image_dims.x / 2.0f) * snode->zoom); + matrix_space[3][1] = ((region->winy / 2) + snode->yof) - ((image_dims.y / 2.0f) * snode->zoom); } /** \} */ @@ -135,7 +135,7 @@ static void WIDGETGROUP_node_transform_refresh(const bContext *C, wmGizmoGroup * ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); if (ibuf) { - const float dims[2] = { + const float2 dims = { (ibuf->x > 0) ? ibuf->x : 64.0f, (ibuf->y > 0) ? ibuf->y : 64.0f, }; @@ -190,7 +190,7 @@ struct NodeCropWidgetGroup { wmGizmo *border; struct { - float dims[2]; + float2 dims; } state; struct { @@ -206,10 +206,7 @@ static void gizmo_node_crop_update(struct NodeCropWidgetGroup *crop_group) crop_group->update_data.context, &crop_group->update_data.ptr, crop_group->update_data.prop); } -static void two_xy_to_rect(const NodeTwoXYs *nxy, - rctf *rect, - const float dims[2], - bool is_relative) +static void two_xy_to_rect(const NodeTwoXYs *nxy, rctf *rect, const float2 &dims, bool is_relative) { if (is_relative) { rect->xmin = nxy->fac_x1; @@ -218,16 +215,16 @@ static void two_xy_to_rect(const NodeTwoXYs *nxy, rect->ymax = nxy->fac_y2; } else { - rect->xmin = nxy->x1 / dims[0]; - rect->xmax = nxy->x2 / dims[0]; - rect->ymin = nxy->y1 / dims[1]; - rect->ymax = nxy->y2 / dims[1]; + rect->xmin = nxy->x1 / dims.x; + rect->xmax = nxy->x2 / dims.x; + rect->ymin = nxy->y1 / dims.y; + rect->ymax = nxy->y2 / dims.y; } } static void two_xy_from_rect(NodeTwoXYs *nxy, const rctf *rect, - const float dims[2], + const float2 &dims, bool is_relative) { if (is_relative) { @@ -237,10 +234,10 @@ static void two_xy_from_rect(NodeTwoXYs *nxy, nxy->fac_y2 = rect->ymax; } else { - nxy->x1 = rect->xmin * dims[0]; - nxy->x2 = rect->xmax * dims[0]; - nxy->y1 = rect->ymin * dims[1]; - nxy->y2 = rect->ymax * dims[1]; + nxy->x1 = rect->xmin * dims.x; + nxy->x2 = rect->xmax * dims.x; + nxy->y1 = rect->ymin * dims.y; + nxy->y2 = rect->ymax * dims.y; } } @@ -321,9 +318,7 @@ static bool WIDGETGROUP_node_crop_poll(const bContext *C, wmGizmoGroupType *UNUS static void WIDGETGROUP_node_crop_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) { - struct NodeCropWidgetGroup *crop_group = (NodeCropWidgetGroup *)MEM_mallocN( - sizeof(struct NodeCropWidgetGroup), __func__); - + NodeCropWidgetGroup *crop_group = MEM_new<NodeCropWidgetGroup>(__func__); crop_group->border = WM_gizmo_new("GIZMO_GT_cage_2d", gzgroup, nullptr); RNA_enum_set(crop_group->border->ptr, @@ -407,7 +402,7 @@ struct NodeSunBeamsWidgetGroup { wmGizmo *gizmo; struct { - float dims[2]; + float2 dims; } state; }; @@ -512,7 +507,7 @@ struct NodeCornerPinWidgetGroup { wmGizmo *gizmos[4]; struct { - float dims[2]; + float2 dims; } state; }; diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index bb520c0537e..21def1bd9d7 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -25,6 +25,7 @@ #include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_node_runtime.hh" #include "BKE_node_tree_update.h" #include "BKE_report.h" @@ -462,8 +463,7 @@ static bool node_group_separate_selected( bNode *newnode; if (make_copy) { /* make a copy */ - newnode = blender::bke::node_copy_with_mapping( - &ngroup, *node, LIB_ID_COPY_DEFAULT, true, socket_map); + newnode = bke::node_copy_with_mapping(&ngroup, *node, LIB_ID_COPY_DEFAULT, true, socket_map); node_map.add_new(node, newnode); } else { @@ -653,7 +653,7 @@ static bool node_group_make_use_node(bNode &node, bNode *gnode) static bool node_group_make_test_selected(bNodeTree &ntree, bNode *gnode, const char *ntree_idname, - struct ReportList &reports) + ReportList &reports) { int ok = true; @@ -717,13 +717,13 @@ static int node_get_selected_minmax( INIT_MINMAX2(min, max); LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { if (node_group_make_use_node(*node, gnode)) { - float loc[2]; - nodeToView(node, node->offsetx, node->offsety, &loc[0], &loc[1]); - minmax_v2v2_v2(min, max, loc); + float2 loc; + nodeToView(node, node->offsetx, node->offsety, &loc.x, &loc.y); + math::min_max(loc, min, max); if (use_size) { - loc[0] += node->width; - loc[1] -= node->height; - minmax_v2v2_v2(min, max, loc); + loc.x += node->width; + loc.y -= node->height; + math::min_max(loc, min, max); } totselect++; } @@ -837,8 +837,8 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, /* relink external sockets */ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree.links) { - int fromselect = node_group_make_use_node(*link->fromnode, gnode); - int toselect = node_group_make_use_node(*link->tonode, gnode); + const bool fromselect = node_group_make_use_node(*link->fromnode, gnode); + const bool toselect = node_group_make_use_node(*link->tonode, gnode); if ((fromselect && link->tonode == gnode) || (toselect && link->fromnode == gnode)) { /* remove all links to/from the gnode. @@ -918,8 +918,8 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, /* move internal links */ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree.links) { - int fromselect = node_group_make_use_node(*link->fromnode, gnode); - int toselect = node_group_make_use_node(*link->tonode, gnode); + const bool fromselect = node_group_make_use_node(*link->fromnode, gnode); + const bool toselect = node_group_make_use_node(*link->tonode, gnode); if (fromselect && toselect) { BLI_remlink(&ntree.links, link); @@ -1042,9 +1042,6 @@ static int node_group_make_exec(bContext *C, wmOperator *op) nodeSetActive(&ntree, gnode); if (ngroup) { ED_node_tree_push(&snode, ngroup, gnode); - LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { - sort_multi_input_socket_links(snode, *node, nullptr, nullptr); - } } } diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index b7fa6ffd807..6754673cffc 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -144,7 +144,10 @@ void node_socket_color_get(const bContext &C, void node_draw_space(const bContext &C, ARegion ®ion); -void node_socket_add_tooltip(bNodeTree *ntree, bNode *node, bNodeSocket *sock, uiLayout *layout); +void node_socket_add_tooltip(const bNodeTree &ntree, + const bNode &node, + const bNodeSocket &sock, + uiLayout &layout); /** * Sort nodes by selection: unselected nodes first, then selected, @@ -166,7 +169,7 @@ void node_keymap(wmKeyConfig *keyconf); /* node_select.cc */ rctf node_frame_rect_inside(const bNode &node); -bool node_or_socket_isect_event(bContext *C, const wmEvent *event); +bool node_or_socket_isect_event(const bContext &C, const wmEvent &event); void node_deselect_all(SpaceNode &snode); void node_socket_select(bNode *node, bNodeSocket &sock); @@ -229,19 +232,12 @@ void node_draw_link_bezier(const bContext &C, int th_col2, int th_col3, bool selected); -/** If v2d not nullptr, it clips and returns 0 if not visible. */ -bool node_link_bezier_points(const View2D *v2d, - const SpaceNode *snode, - const bNodeLink &link, - float coord_array[][2], - int resol); -/** - * Return quadratic beziers points for a given nodelink and clip if v2d is not nullptr. - */ -bool node_link_bezier_handles(const View2D *v2d, - const SpaceNode *snode, - const bNodeLink &ink, - float vec[4][2]); + +void node_link_bezier_points_evaluated(const bNodeLink &link, + std::array<float2, NODE_LINK_RESOL + 1> &coords); + +std::optional<float2> link_path_intersection(const bNodeLink &link, Span<float2> path); + void draw_nodespace_back_pix(const bContext &C, ARegion ®ion, SpaceNode &snode, @@ -271,11 +267,6 @@ void NODE_OT_group_edit(wmOperatorType *ot); /* node_relationships.cc */ -void sort_multi_input_socket_links(SpaceNode &snode, - bNode &node, - bNodeLink *drag_link, - const float2 *cursor); - void NODE_OT_link(wmOperatorType *ot); void NODE_OT_link_make(wmOperatorType *ot); void NODE_OT_links_cut(wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc index ce000aba1da..c936f07a594 100644 --- a/source/blender/editors/space_node/node_ops.cc +++ b/source/blender/editors/space_node/node_ops.cc @@ -111,7 +111,7 @@ void node_operatortypes() WM_operatortype_append(NODE_OT_cryptomatte_layer_remove); } -void node_keymap(struct wmKeyConfig *keyconf) +void node_keymap( wmKeyConfig *keyconf) { /* Entire Editor only ----------------- */ WM_keymap_ensure(keyconf, "Node Generic", SPACE_NODE, 0); diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 3a8bb56bc5e..067c01dcc58 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -11,6 +11,7 @@ #include "DNA_node_types.h" #include "BLI_easing.h" +#include "BLI_stack.hh" #include "BKE_anim_data.h" #include "BKE_context.h" @@ -52,11 +53,6 @@ #include "node_intern.hh" /* own include */ -struct bNodeListItem { - struct bNodeListItem *next, *prev; - struct bNode *node; -}; - struct NodeInsertOfsData { bNodeTree *ntree; bNode *insert; /* inserted node */ @@ -77,6 +73,8 @@ static void clear_picking_highlight(ListBase *links) namespace blender::ed::space_node { +void update_multi_input_indices_for_removed_links(bNode &node); + /* -------------------------------------------------------------------- */ /** \name Add Node * \{ */ @@ -107,11 +105,9 @@ static void pick_link( nldrag.links.append(link); nodeRemLink(snode.edittree, &link_to_pick); - + snode.edittree->ensure_topology_cache(); BLI_assert(nldrag.last_node_hovered_while_dragging_a_link != nullptr); - - sort_multi_input_socket_links( - snode, *nldrag.last_node_hovered_while_dragging_a_link, nullptr, nullptr); + update_multi_input_indices_for_removed_links(*nldrag.last_node_hovered_while_dragging_a_link); /* Send changed event to original link->tonode. */ if (node) { @@ -125,10 +121,8 @@ static void pick_input_link_by_link_intersect(const bContext &C, const float2 &cursor) { SpaceNode *snode = CTX_wm_space_node(&C); - const ARegion *region = CTX_wm_region(&C); - const View2D *v2d = ®ion->v2d; - float drag_start[2]; + float2 drag_start; RNA_float_get_array(op.ptr, "drag_start", drag_start); bNode *node; bNodeSocket *socket; @@ -137,26 +131,16 @@ static void pick_input_link_by_link_intersect(const bContext &C, /* Distance to test overlapping of cursor on link. */ const float cursor_link_touch_distance = 12.5f * UI_DPI_FAC; - const int resolution = NODE_LINK_RESOL; - bNodeLink *link_to_pick = nullptr; clear_picking_highlight(&snode->edittree->links); LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { if (link->tosock == socket) { /* Test if the cursor is near a link. */ - float vec[4][2]; - node_link_bezier_handles(v2d, snode, *link, vec); - - float data[NODE_LINK_RESOL * 2 + 2]; - BKE_curve_forward_diff_bezier( - vec[0][0], vec[1][0], vec[2][0], vec[3][0], data, resolution, sizeof(float[2])); - BKE_curve_forward_diff_bezier( - vec[0][1], vec[1][1], vec[2][1], vec[3][1], data + 1, resolution, sizeof(float[2])); - - for (int i = 0; i < resolution * 2; i += 2) { - float *l1 = &data[i]; - float *l2 = &data[i + 2]; - float distance = dist_squared_to_line_segment_v2(cursor, l1, l2); + std::array<float2, NODE_LINK_RESOL + 1> coords; + node_link_bezier_points_evaluated(*link, coords); + + for (const int i : IndexRange(coords.size() - 1)) { + const float distance = dist_squared_to_line_segment_v2(cursor, coords[i], coords[i + 1]); if (distance < cursor_link_touch_distance) { link_to_pick = link; nldrag.last_picked_multi_input_socket_link = link_to_pick; @@ -308,35 +292,24 @@ struct LinkAndPosition { float2 multi_socket_position; }; -void sort_multi_input_socket_links(SpaceNode &snode, - bNode &node, - bNodeLink *drag_link, - const float2 *cursor) +static void sort_multi_input_socket_links_with_drag(bNode &node, + bNodeLink &drag_link, + const float2 &cursor) { - LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) { - if (!(socket->flag & SOCK_MULTI_INPUT)) { + for (bNodeSocket *socket : node.input_sockets()) { + if (!socket->is_multi_input()) { continue; } - Vector<LinkAndPosition, 8> links; + const float2 &socket_location = {socket->locx, socket->locy}; - LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) { - if (link->tosock == socket) { - links.append( - {link, - node_link_calculate_multi_input_position({link->tosock->locx, link->tosock->locy}, - link->multi_input_socket_index, - link->tosock->total_inputs)}); - } - } + Vector<LinkAndPosition, 8> links; + for (bNodeLink *link : socket->directly_linked_links()) { + const float2 location = node_link_calculate_multi_input_position( + socket_location, link->multi_input_socket_index, link->tosock->total_inputs); + links.append({link, location}); + }; - if (drag_link) { - LinkAndPosition link_and_position{}; - link_and_position.link = drag_link; - if (cursor) { - link_and_position.multi_socket_position = *cursor; - } - links.append(link_and_position); - } + links.append({&drag_link, cursor}); std::sort(links.begin(), links.end(), [](const LinkAndPosition a, const LinkAndPosition b) { return a.multi_socket_position.y < b.multi_socket_position.y; @@ -348,6 +321,23 @@ void sort_multi_input_socket_links(SpaceNode &snode, } } +void update_multi_input_indices_for_removed_links(bNode &node) +{ + for (bNodeSocket *socket : node.input_sockets()) { + if (!socket->is_multi_input()) { + continue; + } + Vector<bNodeLink *, 8> links = socket->directly_linked_links(); + std::sort(links.begin(), links.end(), [](const bNodeLink *a, const bNodeLink *b) { + return a->multi_input_socket_index < b->multi_input_socket_index; + }); + + for (const int i : links.index_range()) { + links[i]->multi_input_socket_index = i; + } + } +} + static void snode_autoconnect(SpaceNode &snode, const bool allow_multiple, const bool replace) { bNodeTree *ntree = snode.edittree; @@ -637,7 +627,8 @@ static int link_socket_to_viewer(const bContext &C, if (viewer_bnode == nullptr) { /* Create a new viewer node if none exists. */ const int viewer_type = get_default_viewer_type(&C); - const float2 location{bsocket_to_view.locx + 100, bsocket_to_view.locy}; + const float2 location{bsocket_to_view.locx / UI_DPI_FAC + 100, + bsocket_to_view.locy / UI_DPI_FAC}; viewer_bnode = add_static_node(C, viewer_type, location); if (viewer_bnode == nullptr) { return OPERATOR_CANCELLED; @@ -942,6 +933,7 @@ static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cur if (nldrag->in_out == SOCK_OUT) { bNode *tnode; bNodeSocket *tsock = nullptr; + snode.edittree->ensure_topology_cache(); if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_IN)) { for (bNodeLink *link : nldrag->links) { /* skip if socket is on the same node as the fromsock */ @@ -968,19 +960,19 @@ static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cur continue; } if (link->tosock && link->tosock->flag & SOCK_MULTI_INPUT) { - sort_multi_input_socket_links(snode, *tnode, link, &cursor); + sort_multi_input_socket_links_with_drag(*tnode, *link, cursor); } } } else { for (bNodeLink *link : nldrag->links) { - if (nldrag->last_node_hovered_while_dragging_a_link) { - sort_multi_input_socket_links( - snode, *nldrag->last_node_hovered_while_dragging_a_link, nullptr, &cursor); - } link->tonode = nullptr; link->tosock = nullptr; } + if (nldrag->last_node_hovered_while_dragging_a_link) { + update_multi_input_indices_for_removed_links( + *nldrag->last_node_hovered_while_dragging_a_link); + } } } else { @@ -1177,7 +1169,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) bool detach = RNA_boolean_get(op->ptr, "detach"); - int mval[2]; + int2 mval; WM_event_drag_start_mval(event, ®ion, mval); float2 cursor; @@ -1317,28 +1309,6 @@ void NODE_OT_link_make(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Node Link Intersect - * \{ */ - -static bool node_links_intersect(bNodeLink &link, const float mcoords[][2], int tot) -{ - float coord_array[NODE_LINK_RESOL + 1][2]; - - if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) { - for (int i = 0; i < tot - 1; i++) { - for (int b = 0; b < NODE_LINK_RESOL; b++) { - if (isect_seg_seg_v2(mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1]) > 0) { - return true; - } - } - } - } - return false; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Cut Link Operator * \{ */ @@ -1346,56 +1316,63 @@ static int cut_links_exec(bContext *C, wmOperator *op) { Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); - ARegion ®ion = *CTX_wm_region(C); + const ARegion ®ion = *CTX_wm_region(C); - int i = 0; - float mcoords[256][2]; + Vector<float2> path; RNA_BEGIN (op->ptr, itemptr, "path") { - float loc[2]; - - RNA_float_get_array(&itemptr, "loc", loc); - UI_view2d_region_to_view( - ®ion.v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]); - i++; - if (i >= 256) { + float2 loc_region; + RNA_float_get_array(&itemptr, "loc", loc_region); + float2 loc_view; + UI_view2d_region_to_view(®ion.v2d, loc_region.x, loc_region.y, &loc_view.x, &loc_view.y); + path.append(loc_view); + if (path.size() >= 256) { break; } } RNA_END; - if (i > 1) { - bool found = false; + if (path.is_empty()) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } - ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); + bool found = false; - LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode.edittree->links) { - if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { - continue; - } + ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); - if (node_links_intersect(*link, mcoords, i)) { + bNodeTree &node_tree = *snode.edittree; - if (found == false) { - /* TODO(sergey): Why did we kill jobs twice? */ - ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); - found = true; - } + Set<bNode *> affected_nodes; - bNode *to_node = link->tonode; - nodeRemLink(snode.edittree, link); - sort_multi_input_socket_links(snode, *to_node, nullptr, nullptr); - } + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &node_tree.links) { + if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { + continue; } - ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); - if (found) { - return OPERATOR_FINISHED; + if (link_path_intersection(*link, path)) { + + if (!found) { + /* TODO(sergey): Why did we kill jobs twice? */ + ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); + found = true; + } + + bNode *to_node = link->tonode; + nodeRemLink(snode.edittree, link); + affected_nodes.add(to_node); } + } - return OPERATOR_CANCELLED; + node_tree.ensure_topology_cache(); + for (bNode *node : affected_nodes) { + update_multi_input_indices_for_removed_links(*node); } - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); + if (found) { + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; } void NODE_OT_links_cut(wmOperatorType *ot) @@ -1429,69 +1406,99 @@ void NODE_OT_links_cut(wmOperatorType *ot) /** \name Mute Links Operator * \{ */ +static bool all_links_muted(const bNodeSocket &socket) +{ + for (const bNodeLink *link : socket.directly_linked_links()) { + if (!(link->flag & NODE_LINK_MUTED)) { + return false; + } + } + return true; +} + static int mute_links_exec(bContext *C, wmOperator *op) { Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); - ARegion ®ion = *CTX_wm_region(C); + const ARegion ®ion = *CTX_wm_region(C); + bNodeTree &ntree = *snode.edittree; - int i = 0; - float mcoords[256][2]; + Vector<float2> path; RNA_BEGIN (op->ptr, itemptr, "path") { - float loc[2]; - - RNA_float_get_array(&itemptr, "loc", loc); - UI_view2d_region_to_view( - ®ion.v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]); - i++; - if (i >= 256) { + float2 loc_region; + RNA_float_get_array(&itemptr, "loc", loc_region); + float2 loc_view; + UI_view2d_region_to_view(®ion.v2d, loc_region.x, loc_region.y, &loc_view.x, &loc_view.y); + path.append(loc_view); + if (path.size() >= 256) { break; } } RNA_END; - if (i > 1) { - ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); + if (path.is_empty()) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } - /* Count intersected links and clear test flag. */ - int tot = 0; - LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) { - if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { - continue; - } - link->flag &= ~NODE_LINK_TEST; - if (node_links_intersect(*link, mcoords, i)) { - tot++; - } + ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); + + ntree.ensure_topology_cache(); + + Set<bNodeLink *> affected_links; + LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { + if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { + continue; } - if (tot == 0) { - return OPERATOR_CANCELLED; + if (!link_path_intersection(*link, path)) { + continue; } + affected_links.add(link); + } - /* Mute links. */ - LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) { - if (node_link_is_hidden_or_dimmed(region.v2d, *link) || (link->flag & NODE_LINK_TEST)) { - continue; - } + if (affected_links.is_empty()) { + return OPERATOR_CANCELLED; + } - if (node_links_intersect(*link, mcoords, i)) { - nodeMuteLinkToggle(snode.edittree, link); + bke::node_tree_runtime::AllowUsingOutdatedInfo allow_outdated_info{ntree}; + + for (bNodeLink *link : affected_links) { + nodeLinkSetMute(&ntree, link, !(link->flag & NODE_LINK_MUTED)); + const bool muted = link->flag & NODE_LINK_MUTED; + + /* Propagate mute status downsteam past reroute nodes. */ + if (link->tonode->is_reroute()) { + Stack<bNodeLink *> links; + links.push_multiple(link->tonode->output_sockets().first()->directly_linked_links()); + while (!links.is_empty()) { + bNodeLink *link = links.pop(); + nodeLinkSetMute(&ntree, link, muted); + if (!link->tonode->is_reroute()) { + continue; + } + links.push_multiple(link->tonode->output_sockets().first()->directly_linked_links()); } } - - /* Clear remaining test flags. */ - LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) { - if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { - continue; + /* Propagate mute status upstream past reroutes, but only if all outputs are muted. */ + if (link->fromnode->is_reroute()) { + if (!muted || all_links_muted(*link->fromsock)) { + Stack<bNodeLink *> links; + links.push_multiple(link->fromnode->input_sockets().first()->directly_linked_links()); + while (!links.is_empty()) { + bNodeLink *link = links.pop(); + nodeLinkSetMute(&ntree, link, muted); + if (!link->fromnode->is_reroute()) { + continue; + } + if (!muted || all_links_muted(*link->fromsock)) { + links.push_multiple(link->fromnode->input_sockets().first()->directly_linked_links()); + } + } } - link->flag &= ~NODE_LINK_TEST; } - - ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); - return OPERATOR_FINISHED; } - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree); + return OPERATOR_FINISHED; } void NODE_OT_links_mute(wmOperatorType *ot) @@ -1707,11 +1714,11 @@ void NODE_OT_join(wmOperatorType *ot) static bNode *node_find_frame_to_attach(ARegion ®ion, const bNodeTree &ntree, - const int mouse_xy[2]) + const int2 mouse_xy) { /* convert mouse coordinates to v2d space */ - float cursor[2]; - UI_view2d_region_to_view(®ion.v2d, UNPACK2(mouse_xy), &cursor[0], &cursor[1]); + float2 cursor; + UI_view2d_region_to_view(®ion.v2d, mouse_xy.x, mouse_xy.y, &cursor.x, &cursor.y); LISTBASE_FOREACH_BACKWARD (bNode *, frame, &ntree.nodes) { /* skip selected, those are the nodes we want to attach */ @@ -1935,6 +1942,7 @@ static bool ed_node_link_conditions(ScrArea *area, void ED_node_link_intersect_test(ScrArea *area, int test) { + using namespace blender; using namespace blender::ed::space_node; bNode *select; @@ -1958,36 +1966,34 @@ void ED_node_link_intersect_test(ScrArea *area, int test) bNodeLink *selink = nullptr; float dist_best = FLT_MAX; LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - float coord_array[NODE_LINK_RESOL + 1][2]; if (node_link_is_hidden_or_dimmed(region->v2d, *link)) { continue; } - if (node_link_bezier_points(nullptr, nullptr, *link, coord_array, NODE_LINK_RESOL)) { - float dist = FLT_MAX; + std::array<float2, NODE_LINK_RESOL + 1> coords; + node_link_bezier_points_evaluated(*link, coords); + float dist = FLT_MAX; - /* loop over link coords to find shortest dist to - * upper left node edge of a intersected line segment */ - for (int i = 0; i < NODE_LINK_RESOL; i++) { - /* Check if the node rectangle intersects the line from this point to next one. */ - if (BLI_rctf_isect_segment(&select->totr, coord_array[i], coord_array[i + 1])) { - /* store the shortest distance to the upper left edge - * of all intersections found so far */ - const float node_xy[] = {select->totr.xmin, select->totr.ymax}; + /* loop over link coords to find shortest dist to + * upper left node edge of a intersected line segment */ + for (int i = 0; i < NODE_LINK_RESOL; i++) { + /* Check if the node rectangle intersects the line from this point to next one. */ + if (BLI_rctf_isect_segment(&select->totr, coords[i], coords[i + 1])) { + /* store the shortest distance to the upper left edge + * of all intersections found so far */ + const float node_xy[] = {select->totr.xmin, select->totr.ymax}; - /* to be precise coord_array should be clipped by select->totr, - * but not done since there's no real noticeable difference */ - dist = min_ff( - dist_squared_to_line_segment_v2(node_xy, coord_array[i], coord_array[i + 1]), dist); - } + /* to be precise coords should be clipped by select->totr, + * but not done since there's no real noticeable difference */ + dist = min_ff(dist_squared_to_line_segment_v2(node_xy, coords[i], coords[i + 1]), dist); } + } - /* we want the link with the shortest distance to node center */ - if (dist < dist_best) { - dist_best = dist; - selink = link; - } + /* we want the link with the shortest distance to node center */ + if (dist < dist_best) { + dist_best = dist; + selink = link; } } diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index 9d73156edab..8cc3c80fdfd 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -48,7 +48,7 @@ namespace blender::ed::space_node { -static bool is_event_over_node_or_socket(bContext *C, const wmEvent *event); +static bool is_event_over_node_or_socket(const bContext &C, const wmEvent &event); /** * Function to detect if there is a visible view3d that uses workbench in texture mode. @@ -100,17 +100,17 @@ rctf node_frame_rect_inside(const bNode &node) return frame_inside; } -bool node_or_socket_isect_event(bContext *C, const wmEvent *event) +bool node_or_socket_isect_event(const bContext &C, const wmEvent &event) { return is_event_over_node_or_socket(C, event); } -static bool node_frame_select_isect_mouse(bNode *node, const float2 &mouse) +static bool node_frame_select_isect_mouse(const bNode &node, const float2 &mouse) { /* Frame nodes are selectable by their borders (including their whole rect - as for other nodes - * would prevent e.g. box selection of nodes inside that frame). */ - const rctf frame_inside = node_frame_rect_inside(*node); - if (BLI_rctf_isect_pt(&node->totr, mouse.x, mouse.y) && + const rctf frame_inside = node_frame_rect_inside(node); + if (BLI_rctf_isect_pt(&node.totr, mouse.x, mouse.y) && !BLI_rctf_isect_pt(&frame_inside, mouse.x, mouse.y)) { return true; } @@ -118,19 +118,18 @@ static bool node_frame_select_isect_mouse(bNode *node, const float2 &mouse) return false; } -static bNode *node_under_mouse_select(bNodeTree &ntree, int mx, int my) +static bNode *node_under_mouse_select(bNodeTree &ntree, const float2 mouse) { LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree.nodes) { switch (node->type) { case NODE_FRAME: { - const float2 mouse{(float)mx, (float)my}; - if (node_frame_select_isect_mouse(node, mouse)) { + if (node_frame_select_isect_mouse(*node, mouse)) { return node; } break; } default: { - if (BLI_rctf_isect_pt(&node->totr, mx, my)) { + if (BLI_rctf_isect_pt(&node->totr, int(mouse.x), int(mouse.y))) { return node; } break; @@ -140,35 +139,32 @@ static bNode *node_under_mouse_select(bNodeTree &ntree, int mx, int my) return nullptr; } -static bNode *node_under_mouse_tweak(bNodeTree &ntree, const float2 &mouse) +static bool node_under_mouse_tweak(const bNodeTree &ntree, const float2 &mouse) { - using namespace blender::math; - - LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree.nodes) { + LISTBASE_FOREACH_BACKWARD (const bNode *, node, &ntree.nodes) { switch (node->type) { case NODE_REROUTE: { - bNodeSocket *socket = (bNodeSocket *)node->inputs.first; - const float2 location{socket->locx, socket->locy}; - if (distance(mouse, location) < 24.0f) { - return node; + const float2 location = node_to_view(*node, {node->locx, node->locy}); + if (math::distance(mouse, location) < 24.0f) { + return true; } break; } case NODE_FRAME: { - if (node_frame_select_isect_mouse(node, mouse)) { - return node; + if (node_frame_select_isect_mouse(*node, mouse)) { + return true; } break; } default: { if (BLI_rctf_isect_pt(&node->totr, mouse.x, mouse.y)) { - return node; + return true; } break; } } } - return nullptr; + return false; } static bool is_position_over_node_or_socket(SpaceNode &snode, const float2 &mouse) @@ -187,17 +183,17 @@ static bool is_position_over_node_or_socket(SpaceNode &snode, const float2 &mous return false; } -static bool is_event_over_node_or_socket(bContext *C, const wmEvent *event) +static bool is_event_over_node_or_socket(const bContext &C, const wmEvent &event) { - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - float2 mouse; + SpaceNode &snode = *CTX_wm_space_node(&C); + ARegion ®ion = *CTX_wm_region(&C); - int mval[2]; - WM_event_drag_start_mval(event, region, mval); + int2 mval; + WM_event_drag_start_mval(&event, ®ion, mval); - UI_view2d_region_to_view(®ion->v2d, mval[0], mval[1], &mouse.x, &mouse.y); - return is_position_over_node_or_socket(*snode, mouse); + float2 mouse; + UI_view2d_region_to_view(®ion.v2d, mval.x, mval.y, &mouse.x, &mouse.y); + return is_position_over_node_or_socket(snode, mouse); } void node_socket_select(bNode *node, bNodeSocket &sock) @@ -514,8 +510,8 @@ void node_select_single(bContext &C, bNode &node) static bool node_mouse_select(bContext *C, wmOperator *op, - const int mval[2], - struct SelectPick_Params *params) + const int2 mval, + SelectPick_Params *params) { Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); @@ -526,7 +522,6 @@ static bool node_mouse_select(bContext *C, bNode *node, *tnode; bNodeSocket *sock = nullptr; bNodeSocket *tsock; - float cursor[2]; /* always do socket_select when extending selection. */ const bool socket_select = (params->sel_op == SEL_OP_XOR) || @@ -536,7 +531,8 @@ static bool node_mouse_select(bContext *C, bool node_was_selected = false; /* get mouse coordinates in view2d space */ - UI_view2d_region_to_view(®ion.v2d, mval[0], mval[1], &cursor[0], &cursor[1]); + float2 cursor; + UI_view2d_region_to_view(®ion.v2d, mval.x, mval.y, &cursor.x, &cursor.y); /* first do socket selection, these generally overlap with nodes. */ if (socket_select) { @@ -593,7 +589,7 @@ static bool node_mouse_select(bContext *C, if (!sock) { /* find the closest visible node */ - node = node_under_mouse_select(*snode.edittree, (int)cursor[0], (int)cursor[1]); + node = node_under_mouse_select(*snode.edittree, cursor); found = (node != nullptr); node_was_selected = node && (node->flag & SELECT); @@ -667,10 +663,10 @@ static bool node_mouse_select(bContext *C, static int node_select_exec(bContext *C, wmOperator *op) { /* get settings from RNA properties for operator */ - int mval[2]; + int2 mval; RNA_int_get_array(op->ptr, "location", mval); - struct SelectPick_Params params = {}; + SelectPick_Params params = {}; ED_select_pick_params_from_operator(op->ptr, ¶ms); /* perform the select */ @@ -787,7 +783,7 @@ static int node_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *ev { const bool tweak = RNA_boolean_get(op->ptr, "tweak"); - if (tweak && is_event_over_node_or_socket(C, event)) { + if (tweak && is_event_over_node_or_socket(*C, *event)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } @@ -836,7 +832,7 @@ static int node_circleselect_exec(bContext *C, wmOperator *op) bNode *node; int x, y, radius; - float offset[2]; + float2 offset; float zoom = (float)(BLI_rcti_size_x(®ion->winrct)) / (float)(BLI_rctf_size_x(®ion->v2d.cur)); @@ -854,7 +850,7 @@ static int node_circleselect_exec(bContext *C, wmOperator *op) y = RNA_int_get(op->ptr, "y"); radius = RNA_int_get(op->ptr, "radius"); - UI_view2d_region_to_view(®ion->v2d, x, y, &offset[0], &offset[1]); + UI_view2d_region_to_view(®ion->v2d, x, y, &offset.x, &offset.y); for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { switch (node->type) { @@ -916,7 +912,7 @@ static int node_lasso_select_invoke(bContext *C, wmOperator *op, const wmEvent * { const bool tweak = RNA_boolean_get(op->ptr, "tweak"); - if (tweak && is_event_over_node_or_socket(C, event)) { + if (tweak && is_event_over_node_or_socket(*C, *event)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } @@ -968,14 +964,14 @@ static bool do_lasso_select_node(bContext *C, break; } default: { - int screen_co[2]; - const float cent[2] = {BLI_rctf_cent_x(&node->totr), BLI_rctf_cent_y(&node->totr)}; + int2 screen_co; + const float2 center = {BLI_rctf_cent_x(&node->totr), BLI_rctf_cent_y(&node->totr)}; /* marker in screen coords */ if (UI_view2d_view_to_region_clip( - ®ion->v2d, cent[0], cent[1], &screen_co[0], &screen_co[1]) && - BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) && - BLI_lasso_is_point_inside(mcoords, mcoords_len, screen_co[0], screen_co[1], INT_MAX)) { + ®ion->v2d, center.x, center.y, &screen_co.x, &screen_co.y) && + BLI_rcti_isect_pt(&rect, screen_co.x, screen_co.y) && + BLI_lasso_is_point_inside(mcoords, mcoords_len, screen_co.x, screen_co.y, INT_MAX)) { nodeSetSelected(node, select); changed = true; } @@ -1298,7 +1294,7 @@ static void node_find_create_label(const bNode *node, char *str, int maxlen) } /* Generic search invoke. */ -static void node_find_update_fn(const struct bContext *C, +static void node_find_update_fn(const bContext *C, void *UNUSED(arg), const char *str, uiSearchItems *items, @@ -1330,7 +1326,7 @@ static void node_find_update_fn(const struct bContext *C, BLI_string_search_free(search); } -static void node_find_exec_fn(struct bContext *C, void *UNUSED(arg1), void *arg2) +static void node_find_exec_fn(bContext *C, void *UNUSED(arg1), void *arg2) { SpaceNode *snode = CTX_wm_space_node(C); bNode *active = (bNode *)arg2; diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index 58a313c328e..5fc194e02a4 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -758,43 +758,42 @@ namespace blender::ed::space_node { /**************************** Node Tree Layout *******************************/ static void ui_node_draw_input( - uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input, int depth); + uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, bNodeSocket &input, int depth); static void ui_node_draw_node( - uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, int depth) + uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, int depth) { - bNodeSocket *input; PointerRNA nodeptr; - RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr); + RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr); - if (node->typeinfo->draw_buttons) { - if (node->type != NODE_GROUP) { - uiLayoutSetPropSep(layout, true); - node->typeinfo->draw_buttons(layout, C, &nodeptr); + if (node.typeinfo->draw_buttons) { + if (node.type != NODE_GROUP) { + uiLayoutSetPropSep(&layout, true); + node.typeinfo->draw_buttons(&layout, &C, &nodeptr); } } - for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) { - ui_node_draw_input(layout, C, ntree, node, input, depth + 1); + LISTBASE_FOREACH (bNodeSocket *, input, &node.inputs) { + ui_node_draw_input(layout, C, ntree, node, *input, depth + 1); } } static void ui_node_draw_input( - uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input, int depth) + uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, bNodeSocket &input, int depth) { PointerRNA inputptr, nodeptr; - uiBlock *block = uiLayoutGetBlock(layout); + uiBlock *block = uiLayoutGetBlock(&layout); uiLayout *row = nullptr; bool dependency_loop; - if (input->flag & SOCK_UNAVAIL) { + if (input.flag & SOCK_UNAVAIL) { return; } /* to avoid eternal loops on cyclic dependencies */ - node->flag |= NODE_TEST; - bNode *lnode = (input->link) ? input->link->fromnode : nullptr; + node.flag |= NODE_TEST; + bNode *lnode = (input.link) ? input.link->fromnode : nullptr; dependency_loop = (lnode && (lnode->flag & NODE_TEST)); if (dependency_loop) { @@ -802,10 +801,10 @@ static void ui_node_draw_input( } /* socket RNA pointer */ - RNA_pointer_create(&ntree->id, &RNA_NodeSocket, input, &inputptr); - RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr); + RNA_pointer_create(&ntree.id, &RNA_NodeSocket, &input, &inputptr); + RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr); - row = uiLayoutRow(layout, true); + row = uiLayoutRow(&layout, true); /* Decorations are added manually here. */ uiLayoutSetPropDecorate(row, false); @@ -821,8 +820,8 @@ static void ui_node_draw_input( if (lnode && (lnode->inputs.first || (lnode->typeinfo->draw_buttons && lnode->type != NODE_GROUP))) { - int icon = (input->flag & SOCK_COLLAPSED) ? ICON_DISCLOSURE_TRI_RIGHT : - ICON_DISCLOSURE_TRI_DOWN; + int icon = (input.flag & SOCK_COLLAPSED) ? ICON_DISCLOSURE_TRI_RIGHT : + ICON_DISCLOSURE_TRI_DOWN; uiItemR(sub, &inputptr, "show_expanded", UI_ITEM_R_ICON_ONLY, "", icon); } @@ -831,7 +830,7 @@ static void ui_node_draw_input( sub = uiLayoutRow(sub, true); uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT); - uiItemL(sub, IFACE_(input->name), ICON_NONE); + uiItemL(sub, IFACE_(input.name), ICON_NONE); } if (dependency_loop) { @@ -840,28 +839,28 @@ static void ui_node_draw_input( } else if (lnode) { /* input linked to a node */ - uiTemplateNodeLink(row, C, ntree, node, input); + uiTemplateNodeLink(row, &C, &ntree, &node, &input); add_dummy_decorator = true; - if (depth == 0 || !(input->flag & SOCK_COLLAPSED)) { + if (depth == 0 || !(input.flag & SOCK_COLLAPSED)) { if (depth == 0) { - uiItemS(layout); + uiItemS(&layout); } - ui_node_draw_node(layout, C, ntree, lnode, depth); + ui_node_draw_node(layout, C, ntree, *lnode, depth); } } else { uiLayout *sub = uiLayoutRow(row, true); - uiTemplateNodeLink(sub, C, ntree, node, input); + uiTemplateNodeLink(sub, &C, &ntree, &node, &input); - if (input->flag & SOCK_HIDE_VALUE) { + if (input.flag & SOCK_HIDE_VALUE) { add_dummy_decorator = true; } /* input not linked, show value */ else { - switch (input->type) { + switch (input.type) { case SOCK_VECTOR: uiItemS(sub); sub = uiLayoutColumn(sub, true); @@ -876,11 +875,11 @@ static void ui_node_draw_input( break; case SOCK_STRING: { const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id; - SpaceNode *snode = CTX_wm_space_node(C); + SpaceNode *snode = CTX_wm_space_node(&C); if (node_tree->type == NTREE_GEOMETRY && snode != nullptr) { /* Only add the attribute search in the node editor, in other places there is not * enough context. */ - node_geometry_add_attribute_search_button(*C, *node, inputptr, *sub); + node_geometry_add_attribute_search_button(C, node, inputptr, *sub); } else { uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE); @@ -899,10 +898,10 @@ static void ui_node_draw_input( uiItemDecoratorR(split_wrapper.decorate_column, nullptr, nullptr, 0); } - node_socket_add_tooltip(ntree, node, input, row); + node_socket_add_tooltip(ntree, node, input, *row); /* clear */ - node->flag &= ~NODE_TEST; + node.flag &= ~NODE_TEST; } } // namespace blender::ed::space_node @@ -924,9 +923,9 @@ void uiTemplateNodeView( } if (input) { - ui_node_draw_input(layout, C, ntree, node, input, 0); + ui_node_draw_input(*layout, *C, *ntree, *node, *input, 0); } else { - ui_node_draw_node(layout, C, ntree, node, 0); + ui_node_draw_node(*layout, *C, *ntree, *node, 0); } } diff --git a/source/blender/editors/space_node/node_view.cc b/source/blender/editors/space_node/node_view.cc index 6f30632244b..00756083580 100644 --- a/source/blender/editors/space_node/node_view.cc +++ b/source/blender/editors/space_node/node_view.cc @@ -177,7 +177,7 @@ void NODE_OT_view_selected(wmOperatorType *ot) * \{ */ struct NodeViewMove { - int mvalo[2]; + int2 mvalo; int xmin, ymin, xmax, ymax; /** Original Offset for cancel. */ float xof_orig, yof_orig; @@ -192,10 +192,10 @@ static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *e switch (event->type) { case MOUSEMOVE: - snode->xof -= (nvm->mvalo[0] - event->mval[0]); - snode->yof -= (nvm->mvalo[1] - event->mval[1]); - nvm->mvalo[0] = event->mval[0]; - nvm->mvalo[1] = event->mval[1]; + snode->xof -= (nvm->mvalo.x - event->mval[0]); + snode->yof -= (nvm->mvalo.y - event->mval[1]); + nvm->mvalo.x = event->mval[0]; + nvm->mvalo.y = event->mval[1]; /* prevent dragging image outside of the window and losing it! */ CLAMP(snode->xof, nvm->xmin, nvm->xmax); @@ -252,10 +252,10 @@ static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent * return OPERATOR_CANCELLED; } - nvm = MEM_cnew<NodeViewMove>("NodeViewMove struct"); + nvm = MEM_cnew<NodeViewMove>(__func__); op->customdata = nvm; - nvm->mvalo[0] = event->mval[0]; - nvm->mvalo[1] = event->mval[1]; + nvm->mvalo.x = event->mval[0]; + nvm->mvalo.y = event->mval[1]; nvm->xmin = -(region->winx / 2) - (ibuf->x * (0.5f * snode->zoom)) + pad; nvm->xmax = (region->winx / 2) + (ibuf->x * (0.5f * snode->zoom)) - pad; @@ -447,7 +447,7 @@ static void sample_draw(const bContext *C, ARegion *region, void *arg_info) } // namespace blender::ed::space_node bool ED_space_node_get_position( - Main *bmain, SpaceNode *snode, struct ARegion *region, const int mval[2], float fpos[2]) + Main *bmain, SpaceNode *snode, ARegion *region, const int mval[2], float fpos[2]) { if (!ED_node_is_compositor(snode) || (snode->flag & SNODE_BACKDRAW) == 0) { return false; @@ -645,7 +645,7 @@ static int sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* Don't handle events intended for nodes (which rely on click/drag distinction). * which this operator would use since sampling is normally activated on press, see: T98191. */ - if (node_or_socket_isect_event(C, event)) { + if (node_or_socket_isect_event(*C, *event)) { return OPERATOR_PASS_THROUGH; } diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index 412611776a7..17fc02e98a8 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -302,7 +302,7 @@ static void node_free(SpaceLink *sl) } /* spacetype; init callback */ -static void node_init(struct wmWindowManager *UNUSED(wm), ScrArea *area) +static void node_init(wmWindowManager *UNUSED(wm), ScrArea *area) { SpaceNode *snode = (SpaceNode *)area->spacedata.first; @@ -511,7 +511,7 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) } } -static void node_area_refresh(const struct bContext *C, ScrArea *area) +static void node_area_refresh(const bContext *C, ScrArea *area) { /* default now: refresh node is starting preview */ SpaceNode *snode = (SpaceNode *)area->spacedata.first; @@ -526,7 +526,7 @@ static void node_area_refresh(const struct bContext *C, ScrArea *area) if (snode->runtime->recalc_auto_compositing) { snode->runtime->recalc_auto_compositing = false; snode->runtime->recalc_regular_compositing = false; - node_render_changed_exec((struct bContext *)C, nullptr); + node_render_changed_exec((bContext *)C, nullptr); } else if (snode->runtime->recalc_regular_compositing) { snode->runtime->recalc_regular_compositing = false; @@ -973,9 +973,7 @@ static void node_id_remap_cb(ID *old_id, ID *new_id, void *user_data) } } -static void node_id_remap(ScrArea *UNUSED(area), - SpaceLink *slink, - const struct IDRemapper *mappings) +static void node_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, const IDRemapper *mappings) { /* Although we should be able to perform all the mappings in a single go this lead to issues when * running the python test cases. Somehow the nodetree/edittree weren't updated to the new diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 82d9af34920..4663df00a92 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -455,14 +455,14 @@ static void outliner_do_libdata_operation(bContext *C, }); } -typedef enum eOutlinerLibOpSelectionSet { +enum eOutlinerLibOpSelectionSet { /* Only selected items. */ OUTLINER_LIB_SELECTIONSET_SELECTED, /* Only content 'inside' selected items (their sub-tree). */ OUTLINER_LIB_LIB_SELECTIONSET_CONTENT, /* Combining both options above. */ OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT, -} eOutlinerLibOpSelectionSet; +}; static const EnumPropertyItem prop_lib_op_selection_set[] = { {OUTLINER_LIB_SELECTIONSET_SELECTED, diff --git a/source/blender/editors/space_view3d/view3d_select.cc b/source/blender/editors/space_view3d/view3d_select.cc index 35c56323510..714eff79777 100644 --- a/source/blender/editors/space_view3d/view3d_select.cc +++ b/source/blender/editors/space_view3d/view3d_select.cc @@ -440,24 +440,24 @@ static bool view3d_selectable_data(bContext *C) Object *ob = CTX_data_active_object(C); if (!ED_operator_region_view3d_active(C)) { - return 0; + return false; } if (ob) { if (ob->mode & OB_MODE_EDIT) { if (ob->type == OB_FONT) { - return 0; + return false; } } else { if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) && !BKE_paint_select_elem_test(ob)) { - return 0; + return false; } } } - return 1; + return true; } /* helper also for box_select */ @@ -472,21 +472,21 @@ static bool edge_inside_rect(const rctf *rect, const float v1[2], const float v2 /* check points in rect */ if (edge_fully_inside_rect(rect, v1, v2)) { - return 1; + return true; } /* check points completely out rect */ if (v1[0] < rect->xmin && v2[0] < rect->xmin) { - return 0; + return false; } if (v1[0] > rect->xmax && v2[0] > rect->xmax) { - return 0; + return false; } if (v1[1] < rect->ymin && v2[1] < rect->ymin) { - return 0; + return false; } if (v1[1] > rect->ymax && v2[1] > rect->ymax) { - return 0; + return false; } /* simple check lines intersecting. */ @@ -496,13 +496,13 @@ static bool edge_inside_rect(const rctf *rect, const float v1[2], const float v2 d4 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymin); if (d1 < 0 && d2 < 0 && d3 < 0 && d4 < 0) { - return 0; + return false; } if (d1 > 0 && d2 > 0 && d3 > 0 && d4 > 0) { - return 0; + return false; } - return 1; + return true; } static void do_lasso_select_pose__do_tag(void *userData, @@ -1278,7 +1278,7 @@ static bool view3d_lasso_select(bContext *C, Object *ob = CTX_data_active_object(C); bool changed_multi = false; - wmGenericUserData wm_userdata_buf = {0}; + wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false}; wmGenericUserData *wm_userdata = &wm_userdata_buf; if (vc->obedit == nullptr) { /* Object Mode */ @@ -1545,11 +1545,11 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot) RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE)); ot->prop = prop; - prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", ""); + prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); + prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", ""); + prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } @@ -1735,11 +1735,11 @@ void VIEW3D_OT_bone_select_menu(wmOperatorType *ot) RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE)); ot->prop = prop; - prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", ""); + prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); + prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", ""); + prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } @@ -3017,21 +3017,25 @@ void VIEW3D_OT_select(wmOperatorType *ot) prop = RNA_def_boolean( ot->srna, "center", - 0, + false, "Center", "Use the object center when selecting, in edit mode used to extend object selection"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean( - ot->srna, "enumerate", 0, "Enumerate", "List objects under the mouse (object mode only)"); + prop = RNA_def_boolean(ot->srna, + "enumerate", + false, + "Enumerate", + "List objects under the mouse (object mode only)"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "object", 0, "Object", "Use object selection (edit mode only)"); + prop = RNA_def_boolean( + ot->srna, "object", false, "Object", "Use object selection (edit mode only)"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); /* Needed for select-through to usefully drag handles, see: T98254. * NOTE: this option may be removed and become default behavior, see design task: T98552. */ prop = RNA_def_boolean(ot->srna, "vert_without_handles", - 0, + false, "Control Point Without Handles", "Only select the curve control point, not it's handles"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); @@ -3778,7 +3782,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) rcti rect; bool changed_multi = false; - wmGenericUserData wm_userdata_buf = {0}; + wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false}; wmGenericUserData *wm_userdata = &wm_userdata_buf; view3d_operator_needs_opengl(C); @@ -4279,9 +4283,9 @@ static bool pchan_circle_doSelectJoint(void *userData, else { pchan->bone->flag &= ~BONE_SELECTED; } - return 1; + return true; } - return 0; + return false; } static void do_circle_select_pose__doSelectBone(void *userData, bPoseChannel *pchan, @@ -4389,9 +4393,9 @@ static bool armature_circle_doSelectJoint(void *userData, ebone->flag &= ~BONE_TIPSEL; } } - return 1; + return true; } - return 0; + return false; } static void do_circle_select_armature__doSelectBone(void *userData, EditBone *ebone, @@ -4677,7 +4681,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) /* Allow each selection type to allocate their own data that's used between executions. */ wmGesture *gesture = static_cast<wmGesture *>(op->customdata); /* nullptr when non-modal. */ - wmGenericUserData wm_userdata_buf = {0}; + wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false}; wmGenericUserData *wm_userdata = gesture ? &gesture->user_data : &wm_userdata_buf; const eSelectOp sel_op = ED_select_op_modal( diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 42afd3bfd13..6c3fadc1b29 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -5250,11 +5250,11 @@ static void lineart_gpencil_generate(LineartCache *cache, (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED)))) { continue; } - else if ((shaodow_selection == LRT_SHADOW_FILTER_SHADED && - (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_SHADED)))) { + if ((shaodow_selection == LRT_SHADOW_FILTER_SHADED && + (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_SHADED)))) { continue; } - else if (shaodow_selection == LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES) { + if (shaodow_selection == LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES) { uint32_t test_bits = ec->shadow_mask_bits & LRT_SHADOW_TEST_SHAPE_BITS; if ((test_bits != LRT_SHADOW_MASK_ILLUMINATED) && (test_bits != (LRT_SHADOW_MASK_SHADED | LRT_SHADOW_MASK_ILLUMINATED_SHAPE))) { diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 7ae9eae6d44..2f16d788b9d 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -27,6 +27,7 @@ set(INC # For *_info.hh includes. ../draw/engines/eevee_next + ../draw/intern # For node muting stuff. ../nodes diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 8f524f72fa1..4935ced7f48 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -70,6 +70,8 @@ typedef struct GPUBatch { GPUVertBuf *inst[GPU_BATCH_INST_VBO_MAX_LEN]; /** NULL if element list not needed */ GPUIndexBuf *elem; + /** Resource ID attribute workaround. */ + GPUStorageBuf *resource_id_buf; /** Bookkeeping. */ eGPUBatchFlag flag; /** Type of geometry to draw. */ @@ -126,6 +128,11 @@ bool GPU_batch_vertbuf_has(GPUBatch *, GPUVertBuf *); #define GPU_batch_vertbuf_add(batch, verts) GPU_batch_vertbuf_add_ex(batch, verts, false) +/** + * Set resource id buffer to bind as instance attribute to workaround the lack of gl_BaseInstance. + */ +void GPU_batch_resource_id_buf_set(GPUBatch *batch, GPUStorageBuf *resource_id_buf); + void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader); /** * Bind program bound to IMM to the batch. diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 8c1cb425f0d..023221543ec 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -77,12 +77,20 @@ typedef enum eGPUMaterialFlag { GPU_MATFLAG_HOLDOUT = (1 << 6), GPU_MATFLAG_SHADER_TO_RGBA = (1 << 7), GPU_MATFLAG_AO = (1 << 8), + GPU_MATFLAG_CLEARCOAT = (1 << 9), GPU_MATFLAG_OBJECT_INFO = (1 << 10), GPU_MATFLAG_AOV = (1 << 11), GPU_MATFLAG_BARYCENTRIC = (1 << 20), + /* Optimization to only add the branches of the principled shader that are necessary. */ + GPU_MATFLAG_PRINCIPLED_CLEARCOAT = (1 << 21), + GPU_MATFLAG_PRINCIPLED_METALLIC = (1 << 22), + GPU_MATFLAG_PRINCIPLED_DIELECTRIC = (1 << 23), + GPU_MATFLAG_PRINCIPLED_GLASS = (1 << 24), + GPU_MATFLAG_PRINCIPLED_ANY = (1 << 25), + /* Tells the render engine the material was just compiled or updated. */ GPU_MATFLAG_UPDATED = (1 << 29), @@ -141,7 +149,10 @@ GPUNodeLink *GPU_attribute_with_default(GPUMaterial *mat, eCustomDataType type, const char *name, eGPUDefaultValue default_value); -GPUNodeLink *GPU_uniform_attribute(GPUMaterial *mat, const char *name, bool use_dupli); +GPUNodeLink *GPU_uniform_attribute(GPUMaterial *mat, + const char *name, + bool use_dupli, + uint32_t *r_hash); GPUNodeLink *GPU_image(GPUMaterial *mat, struct Image *ima, struct ImageUser *iuser, @@ -318,10 +329,10 @@ typedef struct GPUUniformAttrList { unsigned int count, hash_code; } GPUUniformAttrList; -GPUUniformAttrList *GPU_material_uniform_attributes(const GPUMaterial *material); +const GPUUniformAttrList *GPU_material_uniform_attributes(const GPUMaterial *material); struct GHash *GPU_uniform_attr_list_hash_new(const char *info); -void GPU_uniform_attr_list_copy(GPUUniformAttrList *dest, GPUUniformAttrList *src); +void GPU_uniform_attr_list_copy(GPUUniformAttrList *dest, const GPUUniformAttrList *src); void GPU_uniform_attr_list_free(GPUUniformAttrList *set); /* A callback passed to GPU_material_from_callbacks to construct the material graph by adding and diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc index 9092ad5110c..c871004deac 100644 --- a/source/blender/gpu/intern/gpu_batch.cc +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -200,6 +200,13 @@ bool GPU_batch_vertbuf_has(GPUBatch *batch, GPUVertBuf *verts) return false; } +void GPU_batch_resource_id_buf_set(GPUBatch *batch, GPUStorageBuf *resource_id_buf) +{ + BLI_assert(resource_id_buf); + batch->flag |= GPU_BATCH_DIRTY; + batch->resource_id_buf = resource_id_buf; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc index b6194c0816f..f774f33e03d 100644 --- a/source/blender/gpu/intern/gpu_codegen.cc +++ b/source/blender/gpu/intern/gpu_codegen.cc @@ -199,8 +199,7 @@ static std::ostream &operator<<(std::ostream &stream, const GPUOutput *output) } /* Trick type to change overload and keep a somewhat nice syntax. */ -struct GPUConstant : public GPUInput { -}; +struct GPUConstant : public GPUInput {}; /* Print data constructor (i.e: vec2(1.0f, 1.0f)). */ static std::ostream &operator<<(std::ostream &stream, const GPUConstant *input) @@ -208,9 +207,10 @@ static std::ostream &operator<<(std::ostream &stream, const GPUConstant *input) stream << input->type << "("; for (int i = 0; i < input->type; i++) { char formated_float[32]; - /* Print with the maximum precision for single precision float using scientific notation. - * See https://stackoverflow.com/questions/16839658/#answer-21162120 */ - SNPRINTF(formated_float, "%.9g", input->vec[i]); + /* Use uint representation to allow exact same bit pattern even if NaN. This is because we can + * pass UINTs as floats for constants. */ + const uint32_t *uint_vec = reinterpret_cast<const uint32_t *>(input->vec); + SNPRINTF(formated_float, "uintBitsToFloat(%uu)", uint_vec[i]); stream << formated_float; if (i < input->type - 1) { stream << ", "; @@ -353,6 +353,24 @@ void GPUCodegen::generate_resources() { GPUCodegenCreateInfo &info = *create_info; + /* Ref. T98190: Defines are optimizations for old compilers. + * Might become unecessary with EEVEE-Next. */ + if (GPU_material_flag_get(&mat, GPU_MATFLAG_PRINCIPLED_CLEARCOAT)) { + info.define("PRINCIPLED_CLEARCOAT"); + } + if (GPU_material_flag_get(&mat, GPU_MATFLAG_PRINCIPLED_METALLIC)) { + info.define("PRINCIPLED_METALLIC"); + } + if (GPU_material_flag_get(&mat, GPU_MATFLAG_PRINCIPLED_DIELECTRIC)) { + info.define("PRINCIPLED_DIELECTRIC"); + } + if (GPU_material_flag_get(&mat, GPU_MATFLAG_PRINCIPLED_GLASS)) { + info.define("PRINCIPLED_GLASS"); + } + if (GPU_material_flag_get(&mat, GPU_MATFLAG_PRINCIPLED_ANY)) { + info.define("PRINCIPLED_ANY"); + } + std::stringstream ss; /* Textures. */ diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index d0297127ffc..75066b21e7b 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -225,9 +225,9 @@ ListBase GPU_material_textures(GPUMaterial *material) return material->graph.textures; } -GPUUniformAttrList *GPU_material_uniform_attributes(const GPUMaterial *material) +const GPUUniformAttrList *GPU_material_uniform_attributes(const GPUMaterial *material) { - GPUUniformAttrList *attrs = &material->graph.uniform_attrs; + const GPUUniformAttrList *attrs = &material->graph.uniform_attrs; return attrs->count > 0 ? attrs : NULL; } diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index 6d6fad4e80f..f82af7538b5 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -292,7 +292,7 @@ struct GHash *GPU_uniform_attr_list_hash_new(const char *info) return BLI_ghash_new(uniform_attr_list_hash, uniform_attr_list_cmp, info); } -void GPU_uniform_attr_list_copy(GPUUniformAttrList *dest, GPUUniformAttrList *src) +void GPU_uniform_attr_list_copy(GPUUniformAttrList *dest, const GPUUniformAttrList *src) { dest->count = src->count; dest->hash_code = src->hash_code; @@ -320,20 +320,7 @@ void gpu_node_graph_finalize_uniform_attrs(GPUNodeGraph *graph) LISTBASE_FOREACH (GPUUniformAttr *, attr, &attrs->list) { attr->id = next_id++; - - attr->hash_code = BLI_ghashutil_strhash_p(attr->name); - - if (attr->use_dupli) { - attr->hash_code ^= BLI_ghashutil_uinthash(attr->id); - } - - attrs->hash_code ^= attr->hash_code; - - { - char attr_name_esc[sizeof(attr->name) * 2]; - BLI_str_escape(attr_name_esc, attr->name, sizeof(attr_name_esc)); - SNPRINTF(attr->name_id_prop, "[\"%s\"]", attr_name_esc); - } + attrs->hash_code ^= BLI_ghashutil_uinthash(attr->hash_code + (1 << (attr->id + 1))); } } @@ -428,7 +415,13 @@ static GPUUniformAttr *gpu_node_graph_add_uniform_attribute(GPUNodeGraph *graph, if (attr == NULL && attrs->count < GPU_MAX_UNIFORM_ATTR) { attr = MEM_callocN(sizeof(*attr), __func__); STRNCPY(attr->name, name); + { + char attr_name_esc[sizeof(attr->name) * 2]; + BLI_str_escape(attr_name_esc, attr->name, sizeof(attr_name_esc)); + SNPRINTF(attr->name_id_prop, "[\"%s\"]", attr_name_esc); + } attr->use_dupli = use_dupli; + attr->hash_code = BLI_ghashutil_strhash_p(attr->name) << 1 | (attr->use_dupli ? 0 : 1); attr->id = -1; BLI_addtail(&attrs->list, attr); attrs->count++; @@ -532,16 +525,21 @@ GPUNodeLink *GPU_attribute_with_default(GPUMaterial *mat, return link; } -GPUNodeLink *GPU_uniform_attribute(GPUMaterial *mat, const char *name, bool use_dupli) +GPUNodeLink *GPU_uniform_attribute(GPUMaterial *mat, + const char *name, + bool use_dupli, + uint32_t *r_hash) { GPUNodeGraph *graph = gpu_material_node_graph(mat); GPUUniformAttr *attr = gpu_node_graph_add_uniform_attribute(graph, name, use_dupli); /* Dummy fallback if out of slots. */ if (attr == NULL) { + *r_hash = 0; static const float zero_data[GPU_MAX_CONSTANT_DATA] = {0.0f}; return GPU_constant(zero_data); } + *r_hash = attr->hash_code; GPUNodeLink *link = gpu_node_link_create(); link->link_type = GPU_NODE_LINK_UNIFORM_ATTR; diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc index 110b77f1f52..a18fdcd32df 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.cc +++ b/source/blender/gpu/intern/gpu_shader_create_info.cc @@ -300,6 +300,11 @@ void gpu_shader_create_info_init() draw_modelmat = draw_modelmat_legacy; } + /* WORKAROUND: Replace the use of gpu_BaseInstance by an instance attribute. */ + if (GPU_shader_draw_parameters_support() == false) { + draw_resource_id_new = draw_resource_id_fallback; + } + for (ShaderCreateInfo *info : g_create_infos->values()) { if (info->do_static_compilation_) { info->builtins_ |= gpu_shader_dependency_get_builtins(info->vertex_source_); diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc index d836b73f5d8..6897ac9f4a2 100644 --- a/source/blender/gpu/opengl/gl_vertex_array.cc +++ b/source/blender/gpu/opengl/gl_vertex_array.cc @@ -11,6 +11,7 @@ #include "gl_batch.hh" #include "gl_context.hh" #include "gl_index_buffer.hh" +#include "gl_storage_buffer.hh" #include "gl_vertex_buffer.hh" #include "gl_vertex_array.hh" @@ -118,6 +119,18 @@ void GLVertArray::update_bindings(const GLuint vao, } } + if (batch->resource_id_buf) { + const ShaderInput *input = interface->attr_get("drw_ResourceID"); + if (input) { + dynamic_cast<GLStorageBuf *>(unwrap(batch->resource_id_buf))->bind_as(GL_ARRAY_BUFFER); + glEnableVertexAttribArray(input->location); + glVertexAttribDivisor(input->location, 1); + glVertexAttribIPointer( + input->location, 1, to_gl(GPU_COMP_I32), sizeof(uint32_t), (GLvoid *)nullptr); + attr_mask &= ~(1 << input->location); + } + } + if (attr_mask != 0 && GLContext::vertex_attrib_binding_support) { for (uint16_t mask = 1, a = 0; a < 16; a++, mask <<= 1) { if (attr_mask & mask) { diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl index 6091a5c834a..c0821085c8d 100644 --- a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl +++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl @@ -187,8 +187,10 @@ struct ClosureTransparency { struct GlobalData { /** World position. */ vec3 P; - /** Surface Normal. */ + /** Surface Normal. Normalized, overriden by bump displacement. */ vec3 N; + /** Raw interpolated normal (non-normalized) data. */ + vec3 Ni; /** Geometric Normal. */ vec3 Ng; /** Curve Tangent Space. */ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_attribute.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_attribute.glsl index 2ae53b35b3f..bacf089deb1 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_attribute.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_attribute.glsl @@ -12,6 +12,23 @@ void node_attribute_temperature(vec4 attr, out vec4 out_attr) out_attr.w = 1.0; } +void node_attribute_density(vec4 attr, out float out_attr) +{ + out_attr = attr.x; +} + +void node_attribute_flame(vec4 attr, out float out_attr) +{ + out_attr = attr.x; +} + +void node_attribute_uniform(vec4 attr, const float attr_hash, out vec4 out_attr) +{ + /* Temporary solution to support both old UBO attribs and new SSBO loading. + * Old UBO load is already done through `attr` and will just be passed through. */ + out_attr = attr_load_uniform(attr, floatBitsToUint(attr_hash)); +} + void node_attribute( vec4 attr, out vec4 outcol, out vec3 outvec, out float outf, out float outalpha) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl index cdcdbe50917..52b4edf665f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl @@ -1,6 +1,6 @@ void node_displacement_object(float height, float midlevel, float scale, vec3 N, out vec3 result) { - N = transform_direction(ModelMatrix, N); + N = transform_direction(ModelMatrixInverse, N); result = (height - midlevel) * scale * normalize(N); /* Apply object scale and orientation. */ result = transform_direction(ModelMatrix, result); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl index a54dc59ddfe..3fc4992f7c4 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl @@ -3,13 +3,13 @@ void node_normal_map(vec4 tangent, vec3 texnormal, out vec3 outnormal) { if (all(equal(tangent, vec4(0.0, 0.0, 0.0, 1.0)))) { - outnormal = g_data.N; + outnormal = g_data.Ni; return; } tangent *= (FrontFacing ? 1.0 : -1.0); - vec3 B = tangent.w * cross(g_data.N, tangent.xyz) * sign(ObjectInfo.w); + vec3 B = tangent.w * cross(g_data.Ni, tangent.xyz) * sign(ObjectInfo.w); - outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * g_data.N; + outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * g_data.Ni; outnormal = normalize(outnormal); } #endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl index 2e695fa3e14..0d8f2272c10 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl @@ -149,25 +149,37 @@ void node_bsdf_principled(vec4 base_color, max(roughness, transmission_roughness); refraction_data.ior = ior; + /* Ref. T98190: Defines are optimizations for old compilers. + * Might become unecessary with EEVEE-Next. */ if (do_diffuse == 0.0 && do_refraction == 0.0 && do_clearcoat != 0.0) { +#ifdef PRINCIPLED_CLEARCOAT /* Metallic & Clearcoat case. */ result = closure_eval(reflection_data, clearcoat_data); +#endif } else if (do_diffuse == 0.0 && do_refraction == 0.0 && do_clearcoat == 0.0) { +#ifdef PRINCIPLED_METALLIC /* Metallic case. */ result = closure_eval(reflection_data); +#endif } else if (do_diffuse != 0.0 && do_refraction == 0.0 && do_clearcoat == 0.0) { +#ifdef PRINCIPLED_DIELECTRIC /* Dielectric case. */ result = closure_eval(diffuse_data, reflection_data); +#endif } else if (do_diffuse == 0.0 && do_refraction != 0.0 && do_clearcoat == 0.0) { +#ifdef PRINCIPLED_GLASS /* Glass case. */ result = closure_eval(reflection_data, refraction_data); +#endif } else { +#ifdef PRINCIPLED_ANY /* Un-optimized case. */ result = closure_eval(diffuse_data, reflection_data, clearcoat_data, refraction_data); +#endif } Closure emission_cl = closure_eval(emission_data); Closure transparency_cl = closure_eval(transparency_data); diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc index e7d8faaacfa..b5838ad9485 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_base.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_base.cc @@ -257,7 +257,7 @@ float GpencilIO::stroke_point_radius_get(bGPDlayer *gpl, bGPDstroke *gps) /* Radius. */ bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view( - rv3d_, gpd_, gpl, gps, 3, diff_mat_.values, 0.0f); + rv3d_->viewmat, gpd_, gpl, gps, 3, diff_mat_.values, 0.0f); pt = &gps_perimeter->points[0]; const float2 screen_ex = gpencil_3D_point_to_2D(&pt->x); diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc index 95e83769979..463032ebb9d 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc @@ -192,7 +192,7 @@ void GpencilExporterPDF::export_gpencil_layers() } else { bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view( - rv3d_, gpd_, gpl, gps_duplicate, 3, diff_mat_.values, 0.0f); + rv3d_->viewmat, gpd_, gpl, gps_duplicate, 3, diff_mat_.values, 0.0f); /* Sample stroke. */ if (params_.stroke_sample > 0.0f) { diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc index e0eded35ce9..58f12e9b8b1 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc @@ -217,7 +217,7 @@ void GpencilExporterSVG::export_gpencil_layers() } else { bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view( - rv3d_, gpd_, gpl, gps_duplicate, 3, diff_mat_.values, 0.0f); + rv3d_->viewmat, gpd_, gpl, gps_duplicate, 3, diff_mat_.values, 0.0f); /* Sample stroke. */ if (params_.stroke_sample > 0.0f) { diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc index 0aaf9048498..58d414a59ad 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc @@ -142,46 +142,38 @@ static Image *load_texture_image(Main *bmain, const MTLTexMap &tex_map, bool rel return image; } -typedef Vector<std::pair<int, int>> NodeLocations; +/* Nodes are arranged in columns by type, with manually placed x coordinates + * based on node widths. */ +const float node_locx_texcoord = -880.0f; +const float node_locx_mapping = -680.0f; +const float node_locx_image = -480.0f; +const float node_locx_normalmap = -200.0f; +const float node_locx_bsdf = 0.0f; +const float node_locx_output = 280.0f; -static std::pair<float, float> calc_location(int column, NodeLocations &r_locations) +/* Nodes are arranged in rows; one row for each image being used. */ +const float node_locy_top = 300.0f; +const float node_locy_step = 300.0f; + +/* Add a node of the given type at the given location. */ +static bNode *add_node(bNodeTree *ntree, int type, float x, float y) { - const float node_size = 300.f; - int row = 0; - bool found = false; - while (true) { - for (const auto &location : r_locations) { - if (location.first == column && location.second == row) { - row += 1; - found = true; - } - else { - found = false; - } - } - if (!found) { - r_locations.append({column, row}); - return {column * node_size, row * node_size * 2.0 / 3.0}; - } - } + bNode *node = nodeAddStaticNode(nullptr, ntree, type); + node->locx = x; + node->locy = y; + return node; } -/* Node layout columns: - * Texture Coordinates -> Mapping -> Image -> Normal Map -> BSDF -> Output */ -static void link_sockets(bNodeTree *nodetree, +static void link_sockets(bNodeTree *ntree, bNode *from_node, const char *from_node_id, bNode *to_node, - const char *to_node_id, - const int from_node_column, - NodeLocations &r_locations) + const char *to_node_id) { - std::tie(from_node->locx, from_node->locy) = calc_location(from_node_column, r_locations); - std::tie(to_node->locx, to_node->locy) = calc_location(from_node_column + 1, r_locations); bNodeSocket *from_sock{nodeFindSocket(from_node, SOCK_OUT, from_node_id)}; bNodeSocket *to_sock{nodeFindSocket(to_node, SOCK_IN, to_node_id)}; BLI_assert(from_sock && to_sock); - nodeAddLink(nodetree, from_node, from_sock, to_node, to_sock); + nodeAddLink(ntree, from_node, from_sock, to_node, to_sock); } static void set_bsdf_socket_values(bNode *bsdf, Material *mat, const MTLMaterial &mtl_mat) @@ -323,13 +315,13 @@ static void set_bsdf_socket_values(bNode *bsdf, Material *mat, const MTLMaterial } static void add_image_textures(Main *bmain, - bNodeTree *nodetree, + bNodeTree *ntree, bNode *bsdf, Material *mat, const MTLMaterial &mtl_mat, - bool relative_paths, - NodeLocations &r_locations) + bool relative_paths) { + float node_locy = node_locy_top; for (int key = 0; key < (int)MTLTexMapType::Count; ++key) { const MTLTexMap &value = mtl_mat.texture_maps[key]; if (!value.is_valid()) { @@ -337,47 +329,49 @@ static void add_image_textures(Main *bmain, continue; } - bNode *image_texture = nodeAddStaticNode(nullptr, nodetree, SH_NODE_TEX_IMAGE); - BLI_assert(image_texture); Image *image = load_texture_image(bmain, value, relative_paths); if (image == nullptr) { continue; } - image_texture->id = &image->id; - static_cast<NodeTexImage *>(image_texture->storage)->projection = value.projection_type; + + bNode *image_node = add_node(ntree, SH_NODE_TEX_IMAGE, node_locx_image, node_locy); + BLI_assert(image_node); + image_node->id = &image->id; + static_cast<NodeTexImage *>(image_node->storage)->projection = value.projection_type; /* Add normal map node if needed. */ bNode *normal_map = nullptr; if (key == (int)MTLTexMapType::bump) { - normal_map = nodeAddStaticNode(nullptr, nodetree, SH_NODE_NORMAL_MAP); + normal_map = add_node(ntree, SH_NODE_NORMAL_MAP, node_locx_normalmap, node_locy); const float bump = std::max(0.0f, mtl_mat.map_Bump_strength); set_property_of_socket(SOCK_FLOAT, "Strength", {bump}, normal_map); } /* Add UV mapping & coordinate nodes only if needed. */ if (value.translation != float3(0, 0, 0) || value.scale != float3(1, 1, 1)) { - bNode *mapping = nodeAddStaticNode(nullptr, nodetree, SH_NODE_MAPPING); - bNode *texture_coordinate = nodeAddStaticNode(nullptr, nodetree, SH_NODE_TEX_COORD); + bNode *texcoord = add_node(ntree, SH_NODE_TEX_COORD, node_locx_texcoord, node_locy); + bNode *mapping = add_node(ntree, SH_NODE_MAPPING, node_locx_mapping, node_locy); set_property_of_socket(SOCK_VECTOR, "Location", {value.translation, 3}, mapping); set_property_of_socket(SOCK_VECTOR, "Scale", {value.scale, 3}, mapping); - link_sockets(nodetree, texture_coordinate, "UV", mapping, "Vector", 0, r_locations); - link_sockets(nodetree, mapping, "Vector", image_texture, "Vector", 1, r_locations); + link_sockets(ntree, texcoord, "UV", mapping, "Vector"); + link_sockets(ntree, mapping, "Vector", image_node, "Vector"); } if (normal_map) { - link_sockets(nodetree, image_texture, "Color", normal_map, "Color", 2, r_locations); - link_sockets(nodetree, normal_map, "Normal", bsdf, "Normal", 3, r_locations); + link_sockets(ntree, image_node, "Color", normal_map, "Color"); + link_sockets(ntree, normal_map, "Normal", bsdf, "Normal"); } else if (key == (int)MTLTexMapType::d) { - link_sockets( - nodetree, image_texture, "Alpha", bsdf, tex_map_type_to_socket_id[key], 2, r_locations); + link_sockets(ntree, image_node, "Alpha", bsdf, tex_map_type_to_socket_id[key]); mat->blend_method = MA_BM_BLEND; } else { - link_sockets( - nodetree, image_texture, "Color", bsdf, tex_map_type_to_socket_id[key], 2, r_locations); + link_sockets(ntree, image_node, "Color", bsdf, tex_map_type_to_socket_id[key]); } + + /* Next layout row: goes downwards on the screen. */ + node_locy -= node_locy_step; } } @@ -386,17 +380,17 @@ bNodeTree *create_mtl_node_tree(Main *bmain, Material *mat, bool relative_paths) { - bNodeTree *nodetree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname); - bNode *bsdf = nodeAddStaticNode(nullptr, nodetree, SH_NODE_BSDF_PRINCIPLED); - bNode *shader_output = nodeAddStaticNode(nullptr, nodetree, SH_NODE_OUTPUT_MATERIAL); + bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname); + + bNode *bsdf = add_node(ntree, SH_NODE_BSDF_PRINCIPLED, node_locx_bsdf, node_locy_top); + bNode *output = add_node(ntree, SH_NODE_OUTPUT_MATERIAL, node_locx_output, node_locy_top); - NodeLocations node_locations; set_bsdf_socket_values(bsdf, mat, mtl); - add_image_textures(bmain, nodetree, bsdf, mat, mtl, relative_paths, node_locations); - link_sockets(nodetree, bsdf, "BSDF", shader_output, "Surface", 4, node_locations); - nodeSetActive(nodetree, shader_output); + add_image_textures(bmain, ntree, bsdf, mat, mtl, relative_paths); + link_sockets(ntree, bsdf, "BSDF", output, "Surface"); + nodeSetActive(ntree, output); - return nodetree; + return ntree; } } // namespace blender::io::obj diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index b3a07f7ff37..d64b3d361cf 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -655,62 +655,123 @@ enum { /** * id->tag (runtime-only). * - * Those flags belong to three different categories, - * which have different expected handling in code: + * Those flags belong to three different categories, which have different expected handling in + * code: * - * - RESET_BEFORE_USE: piece of code that wants to use such flag - * has to ensure they are properly 'reset' first. + * - RESET_BEFORE_USE: piece of code that wants to use such flag has to ensure they are properly + * 'reset' first. * - RESET_AFTER_USE: piece of code that wants to use such flag has to ensure they are properly - * 'reset' after usage - * (though 'lifetime' of those flags is a bit fuzzy, e.g. _RECALC ones are reset on depsgraph - * evaluation...). - * - RESET_NEVER: those flags are 'status' one, and never actually need any reset - * (except on initialization during .blend file reading). + * 'reset' after usage (though 'lifetime' of those flags is a bit fuzzy, e.g. _RECALC ones are + * reset on depsgraph evaluation...). + * - RESET_NEVER: these flags are 'status' ones, and never actually need any reset (except on + * initialization during .blend file reading). */ enum { - /* RESET_NEVER Datablock is from current .blend file. */ + /** + * ID is from current .blend file. + * + * RESET_NEVER + */ LIB_TAG_LOCAL = 0, - /* RESET_NEVER Datablock is from a library, - * but is used (linked) directly by current .blend file. */ + /** + * ID is from a library, but is used (linked) directly by current .blend file. + * + * RESET_NEVER + */ LIB_TAG_EXTERN = 1 << 0, - /* RESET_NEVER Datablock is from a library, - * and is only used (linked) indirectly through other libraries. */ + /** + * ID is from a library, and is only used (linked) indirectly through other libraries. + * + * RESET_NEVER + */ LIB_TAG_INDIRECT = 1 << 1, - /* RESET_AFTER_USE Flag used internally in readfile.c, - * to mark IDs needing to be expanded (only done once). */ + /** + * Tag used internally in readfile.c, to mark IDs needing to be expanded (only done once). + * + * RESET_AFTER_USE + */ LIB_TAG_NEED_EXPAND = 1 << 3, - /* RESET_AFTER_USE Flag used internally in readfile.c to mark ID - * placeholders for linked data-blocks needing to be read. */ + /** + * Tag used internally in readfile.c, to mark ID placeholders for linked data-blocks needing to + * be read. + * + * RESET_AFTER_USE + */ LIB_TAG_ID_LINK_PLACEHOLDER = 1 << 4, - /* RESET_AFTER_USE */ + /** + * Tag used internally in readfile.c, to mark IDs needing to be 'lib-linked', i.e. to get their + * pointers to other data-blocks updated from the 'UID' values stored in .blend files to the new, + * actual pointers. + * + * RESET_AFTER_USE + */ LIB_TAG_NEED_LINK = 1 << 5, - /* RESET_NEVER tag data-block as a place-holder - * (because the real one could not be linked from its library e.g.). */ + /** + * ID is a place-holder, an 'empty shell' (because the real one could not be linked from its + * library e.g.). + * + * RESET_NEVER + */ LIB_TAG_MISSING = 1 << 6, - /* RESET_NEVER tag data-block as being up-to-date regarding its reference. */ + /** + * ID is up-to-date regarding its reference (only for library overrides). + * + * RESET_NEVER + */ LIB_TAG_OVERRIDE_LIBRARY_REFOK = 1 << 9, - /* RESET_NEVER tag data-block as needing an auto-override execution, if enabled. */ + /** + * ID needs an auto-diffing execution, if enabled (only for library overrides). + * + * RESET_NEVER + */ LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH = 1 << 17, - /* tag data-block as having an extra user. */ + /** + * ID has an extra virtual user (aka 'ensured real', as set by e.g. some editors, not to be + * confused with the `LIB_FAKEUSER` flag). + * + * RESET_NEVER + * + * \note This tag does not necessarily mean the actual user count of the ID is increased, this is + * defined by #LIB_TAG_EXTRAUSER_SET. + */ LIB_TAG_EXTRAUSER = 1 << 2, - /* tag data-block as having actually increased user-count for the extra virtual user. */ + /** + * ID actually has increased user-count for the extra virtual user. + * + * RESET_NEVER + */ LIB_TAG_EXTRAUSER_SET = 1 << 7, - /* RESET_AFTER_USE tag newly duplicated/copied IDs (see #ID_NEW_SET macro above). - * Also used internally in readfile.c to mark data-blocks needing do_versions. */ + /** + * ID is newly duplicated/copied (see #ID_NEW_SET macro above). + * + * RESET_AFTER_USE + * + * \note Also used internally in readfile.c to mark data-blocks needing do_versions. + */ LIB_TAG_NEW = 1 << 8, - /* RESET_BEFORE_USE free test flag. - * TODO: make it a RESET_AFTER_USE too. */ + /** + * Free to use tag, often used in BKE code to mark IDs to be processed. + * + * RESET_BEFORE_USE + * + * \todo Make it a RESET_AFTER_USE too. + */ LIB_TAG_DOIT = 1 << 10, - /* RESET_AFTER_USE tag existing data before linking so we know what is new. */ + /** + * ID is already existing. Set before linking, to distinguish between existing data-blocks and + * newly linked ones. + * + * RESET_AFTER_USE + */ LIB_TAG_PRE_EXISTING = 1 << 11, /** - * The data-block is a copy-on-write/localized version. + * ID is a copy-on-write/localized version. * * RESET_NEVER * @@ -720,8 +781,8 @@ enum { */ LIB_TAG_COPIED_ON_WRITE = 1 << 12, /** - * The data-block is not the original COW ID created by the depsgraph, but has be re-allocated - * during the evaluation process of another ID. + * ID is not the original COW ID created by the depsgraph, but has been re-allocated during the + * evaluation process of another ID. * * RESET_NEVER * @@ -731,34 +792,58 @@ enum { LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT = 1 << 13, /** - * The data-block is fully outside of any ID management area, and should be considered as a - * purely independent data. + * ID is fully outside of any ID management area, and should be considered as a purely + * independent data. * * RESET_NEVER * - * NOTE: Only used by node-groups currently. + * \note Only used by node-trees currently. */ LIB_TAG_LOCALIZED = 1 << 14, - /* RESET_NEVER tag data-block for freeing etc. behavior - * (usually set when copying real one into temp/runtime one). */ - LIB_TAG_NO_MAIN = 1 << 15, /* Datablock is not listed in Main database. */ - LIB_TAG_NO_USER_REFCOUNT = 1 << 16, /* Datablock does not refcount usages of other IDs. */ - /* Datablock was not allocated by standard system (BKE_libblock_alloc), do not free its memory - * (usual type-specific freeing is called though). */ + /** General ID management info, for freeing or copying behavior e.g. */ + /** + * ID is not listed/stored in Main database. + * + * RESET_NEVER + */ + LIB_TAG_NO_MAIN = 1 << 15, + /** + * Datablock does not refcount usages of other IDs. + * + * RESET_NEVER + */ + LIB_TAG_NO_USER_REFCOUNT = 1 << 16, + /** + * ID was not allocated by standard system (BKE_libblock_alloc), do not free its memory + * (usual type-specific freeing is called though). + * + * RESET_NEVER + */ LIB_TAG_NOT_ALLOCATED = 1 << 18, - /* RESET_AFTER_USE Used by undo system to tag unchanged IDs re-used from old Main (instead of - * read from memfile). */ + /** + * ID is being re-used from the old Main (instead of read from memfile), during memfile undo + * processing. + * + * RESET_AFTER_USE + */ LIB_TAG_UNDO_OLD_ID_REUSED = 1 << 19, - /* This ID is part of a temporary #Main which is expected to be freed in a short time-frame. + /** + * ID is part of a temporary #Main which is expected to be freed in a short time-frame. + * + * RESET_NEVER + * * Don't allow assigning this to non-temporary members (since it's likely to cause errors). - * When set #ID.session_uuid isn't initialized, since the data isn't part of the session. */ + * When set #ID.session_uuid isn't initialized, since the data isn't part of the session. + */ LIB_TAG_TEMP_MAIN = 1 << 20, /** - * The data-block is a library override that needs re-sync to its linked reference. + * ID is a library override that needs re-sync to its linked reference. + * + * RESET_NEVER */ LIB_TAG_LIB_OVERRIDE_NEED_RESYNC = 1 << 21, }; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index dc461502b10..39fb3690da4 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -640,8 +640,8 @@ typedef struct UserDef_Experimental { char use_cycles_debug; char show_asset_debug_info; char no_asset_indexing; + char use_viewport_debug; char SANITIZE_AFTER_HERE; - char _pad0; /* The following options are automatically sanitized (set to 0) * when the release cycle is not alpha. */ char use_new_curves_tools; diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 0d281032b7e..1ba057d9c40 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -296,7 +296,9 @@ typedef struct View3D { char _pad6[2]; int layact DNA_DEPRECATED; unsigned short local_collections_uuid; - short _pad7[3]; + short _pad7[2]; + + short debug_flag; /** Optional bool for 3d cursor to define center. */ short ob_center_cursor; @@ -489,6 +491,11 @@ enum { V3D_SHADING_COMPOSITOR = (1 << 15), }; +/** #View3D.debug_flag */ +enum { + V3D_DEBUG_FREEZE_CULLING = (1 << 0), +}; + #define V3D_USES_SCENE_LIGHTS(v3d) \ ((((v3d)->shading.type == OB_MATERIAL) && ((v3d)->shading.flag & V3D_SHADING_SCENE_LIGHTS)) || \ (((v3d)->shading.type == OB_RENDER) && \ diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index e0bac0f85ec..0be1dd3117c 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -4954,7 +4954,7 @@ static void def_sh_mix(StructRNA *srna) prop = RNA_def_property(srna, "factor_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_mix_mode_items); - RNA_def_property_enum_default(prop, SOCK_FLOAT); + RNA_def_property_enum_default(prop, NODE_MIX_MODE_UNIFORM); RNA_def_property_ui_text(prop, "Factor Mode", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 9b08b6ef665..5f2e3c4d1a0 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -4736,6 +4736,13 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Opacity", "Vertex Paint mix factor"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update"); + + /* Developper Debug overlay */ + + prop = RNA_def_property(srna, "use_debug_freeze_view_culling", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "debug_flag", V3D_DEBUG_FREEZE_CULLING); + RNA_def_property_ui_text(prop, "Freeze Culling", "Freeze view culling bounds"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); } static void rna_def_space_view3d(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 324c0bb9006..61d4edccb06 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6372,6 +6372,14 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) prop = RNA_def_property(srna, "enable_eevee_next", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "enable_eevee_next", 1); RNA_def_property_ui_text(prop, "EEVEE Next", "Enable the new EEVEE codebase, requires restart"); + + prop = RNA_def_property(srna, "use_viewport_debug", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_viewport_debug", 1); + RNA_def_property_ui_text(prop, + "Viewport Debug", + "Enable viewport debugging options for developpers in the overlays " + "pop-over"); + RNA_def_property_update(prop, 0, "rna_userdef_ui_update"); } static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop) diff --git a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc index 282d3365fa5..12f81da3d1c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc +++ b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc @@ -18,6 +18,8 @@ namespace blender::nodes::node_composite_alpha_over_cc { +NODE_STORAGE_FUNCS(NodeTwoFloats) + static void cmp_node_alphaover_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Float>(N_("Fac")) @@ -86,7 +88,7 @@ class AlphaOverShaderNode : public ShaderNode { float get_premultiply_factor() { - return ((const NodeTwoFloats *)bnode().storage)->x; + return node_storage(bnode()).x; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc index 571415e75d8..ac9a6c89aa4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc @@ -21,6 +21,8 @@ namespace blender::nodes::node_composite_bilateralblur_cc { +NODE_STORAGE_FUNCS(NodeBilateralBlurData) + static void cmp_node_bilateralblur_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -94,18 +96,12 @@ class BilateralBlurOperation : public NodeOperation { int get_blur_radius() { - return math::ceil(get_node_bilateral_blur_data().iter + - get_node_bilateral_blur_data().sigma_space); + return math::ceil(node_storage(bnode()).iter + node_storage(bnode()).sigma_space); } float get_threshold() { - return get_node_bilateral_blur_data().sigma_color; - } - - const NodeBilateralBlurData &get_node_bilateral_blur_data() - { - return *static_cast<const NodeBilateralBlurData *>(bnode().storage); + return node_storage(bnode()).sigma_color; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc index 42dd17230b1..81cc8990d35 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc @@ -22,6 +22,8 @@ namespace blender::nodes::node_composite_bokehimage_cc { +NODE_STORAGE_FUNCS(NodeBokehImage) + static void cmp_node_bokehimage_declare(NodeDeclarationBuilder &b) { b.add_output<decl::Color>(N_("Image")); @@ -66,9 +68,9 @@ class BokehImageOperation : public NodeOperation { GPU_shader_uniform_1f(shader, "exterior_angle", get_exterior_angle()); GPU_shader_uniform_1f(shader, "rotation", get_rotation()); - GPU_shader_uniform_1f(shader, "roundness", get_node_bokeh_image().rounding); - GPU_shader_uniform_1f(shader, "catadioptric", get_node_bokeh_image().catadioptric); - GPU_shader_uniform_1f(shader, "lens_shift", get_node_bokeh_image().lensshift); + GPU_shader_uniform_1f(shader, "roundness", node_storage(bnode()).rounding); + GPU_shader_uniform_1f(shader, "catadioptric", node_storage(bnode()).catadioptric); + GPU_shader_uniform_1f(shader, "lens_shift", node_storage(bnode()).lensshift); Result &output = get_result("Image"); const Domain domain = compute_domain(); @@ -86,16 +88,11 @@ class BokehImageOperation : public NodeOperation { return Domain(int2(512)); } - const NodeBokehImage &get_node_bokeh_image() - { - return *static_cast<const NodeBokehImage *>(bnode().storage); - } - /* The exterior angle is the angle between each two consecutive vertices of the regular polygon * from its center. */ float get_exterior_angle() { - return (M_PI * 2.0f) / get_node_bokeh_image().flaps; + return (M_PI * 2.0f) / node_storage(bnode()).flaps; } float get_rotation() @@ -104,7 +101,7 @@ class BokehImageOperation : public NodeOperation { * y axis, which is 90 degrees minus the angle that it makes with the positive x axis assuming * the first vertex lies on the positive x axis. */ const float offset = M_PI_2 - get_exterior_angle(); - return get_node_bokeh_image().angle - offset; + return node_storage(bnode()).angle - offset; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc index 2b27b382b03..3cf0932e1b3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc @@ -23,6 +23,8 @@ namespace blender::nodes::node_composite_boxmask_cc { +NODE_STORAGE_FUNCS(NodeBoxMask) + static void cmp_node_boxmask_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f); @@ -123,24 +125,19 @@ class BoxMaskOperation : public NodeOperation { } } - const NodeBoxMask &get_node_box_mask() - { - return *static_cast<const NodeBoxMask *>(bnode().storage); - } - float2 get_location() { - return float2(get_node_box_mask().x, get_node_box_mask().y); + return float2(node_storage(bnode()).x, node_storage(bnode()).y); } float2 get_size() { - return float2(get_node_box_mask().width, get_node_box_mask().height); + return float2(node_storage(bnode()).width, node_storage(bnode()).height); } float get_angle() { - return get_node_box_mask().rotation; + return node_storage(bnode()).rotation; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc index a588a17d6c1..3b825017da8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc @@ -20,6 +20,8 @@ namespace blender::nodes::node_composite_channel_matte_cc { +NODE_STORAGE_FUNCS(NodeChroma) + static void cmp_node_channel_matte_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -130,15 +132,10 @@ class ChannelMatteShaderNode : public ShaderNode { return bnode().custom2 - 1; } - const NodeChroma *get_node_chroma() - { - return static_cast<const NodeChroma *>(bnode().storage); - } - /* Get the index of the channel used to compute the limit value. */ int get_limit_channel() { - return get_node_chroma()->channel - 1; + return node_storage(bnode()).channel - 1; } /* Get the indices of the channels used to compute the limit value. We always assume the limit @@ -146,7 +143,7 @@ class ChannelMatteShaderNode : public ShaderNode { * the maximum of two identical values is the same value. */ void get_limit_channels(float limit_channels[2]) { - if (get_node_chroma()->algorithm == CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX) { + if (node_storage(bnode()).algorithm == CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX) { /* If the algorithm is Max, store the indices of the other two channels other than the matte * channel. */ limit_channels[0] = (get_matte_channel() + 1) % 3; @@ -161,12 +158,12 @@ class ChannelMatteShaderNode : public ShaderNode { float get_max_limit() { - return get_node_chroma()->t1; + return node_storage(bnode()).t1; } float get_min_limit() { - return get_node_chroma()->t2; + return node_storage(bnode()).t2; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc index 2ea83340c2b..e5ce87169d4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc @@ -22,6 +22,8 @@ namespace blender::nodes::node_composite_chroma_matte_cc { +NODE_STORAGE_FUNCS(NodeChroma) + static void cmp_node_chroma_matte_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -86,24 +88,19 @@ class ChromaMatteShaderNode : public ShaderNode { GPU_uniform(&falloff)); } - const NodeChroma *get_node_chroma() - { - return static_cast<const NodeChroma *>(bnode().storage); - } - float get_acceptance() { - return std::tan(get_node_chroma()->t1) / 2.0f; + return std::tan(node_storage(bnode()).t1) / 2.0f; } float get_cutoff() { - return get_node_chroma()->t2; + return node_storage(bnode()).t2; } float get_falloff() { - return get_node_chroma()->fstrength; + return node_storage(bnode()).fstrength; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc index ec572c54fd7..08329601f14 100644 --- a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc @@ -18,6 +18,8 @@ namespace blender::nodes::node_composite_color_matte_cc { +NODE_STORAGE_FUNCS(NodeChroma) + static void cmp_node_color_matte_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -83,25 +85,20 @@ class ColorMatteShaderNode : public ShaderNode { GPU_uniform(&value_epsilon)); } - const NodeChroma *get_node_chroma() - { - return static_cast<const NodeChroma *>(bnode().storage); - } - float get_hue_epsilon() { /* Divide by 2 because the hue wraps around. */ - return get_node_chroma()->t1 / 2.0f; + return node_storage(bnode()).t1 / 2.0f; } float get_saturation_epsilon() { - return get_node_chroma()->t2; + return node_storage(bnode()).t2; } float get_value_epsilon() { - return get_node_chroma()->t3; + return node_storage(bnode()).t3; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc index 1ddf0df8ea7..29401d7b20f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc +++ b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc @@ -20,6 +20,8 @@ namespace blender::nodes::node_composite_color_spill_cc { +NODE_STORAGE_FUNCS(NodeColorspill) + static void cmp_node_color_spill_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -131,18 +133,13 @@ class ColorSpillShaderNode : public ShaderNode { return (CMPNodeColorSpillLimitAlgorithm)bnode().custom2; } - const NodeColorspill *get_node_color_spill() - { - return static_cast<const NodeColorspill *>(bnode().storage); - } - void get_spill_scale(float spill_scale[3]) { - const NodeColorspill *node_color_spill = get_node_color_spill(); - if (node_color_spill->unspill) { - spill_scale[0] = node_color_spill->uspillr; - spill_scale[1] = node_color_spill->uspillg; - spill_scale[2] = node_color_spill->uspillb; + const NodeColorspill &node_color_spill = node_storage(bnode()); + if (node_color_spill.unspill) { + spill_scale[0] = node_color_spill.uspillr; + spill_scale[1] = node_color_spill.uspillg; + spill_scale[2] = node_color_spill.uspillb; spill_scale[get_spill_channel()] *= -1.0f; } else { @@ -156,7 +153,7 @@ class ColorSpillShaderNode : public ShaderNode { /* Get the index of the channel used for limiting. */ int get_limit_channel() { - return get_node_color_spill()->limchan; + return node_storage(bnode()).limchan; } /* Get the indices of the channels used to compute the limit value. We always assume the limit @@ -179,7 +176,7 @@ class ColorSpillShaderNode : public ShaderNode { float get_limit_scale() { - return get_node_color_spill()->limscale; + return node_storage(bnode()).limscale; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc index e6e2a310eb4..e05fbf00a25 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc @@ -48,6 +48,8 @@ void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *UNUSED(ntree), bNode *node) namespace blender::nodes::node_composite_colorbalance_cc { +NODE_STORAGE_FUNCS(NodeColorBalance) + static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Float>(N_("Fac")) @@ -161,7 +163,7 @@ class ColorBalanceShaderNode : public ShaderNode { GPUNodeStack *inputs = get_inputs_array(); GPUNodeStack *outputs = get_outputs_array(); - const NodeColorBalance *node_color_balance = get_node_color_balance(); + const NodeColorBalance &node_color_balance = node_storage(bnode()); if (get_color_balance_method() == CMP_NODE_COLOR_BALANCE_LGG) { GPU_stack_link(material, @@ -169,9 +171,9 @@ class ColorBalanceShaderNode : public ShaderNode { "node_composite_color_balance_lgg", inputs, outputs, - GPU_uniform(node_color_balance->lift), - GPU_uniform(node_color_balance->gamma), - GPU_uniform(node_color_balance->gain)); + GPU_uniform(node_color_balance.lift), + GPU_uniform(node_color_balance.gamma), + GPU_uniform(node_color_balance.gain)); return; } @@ -180,21 +182,16 @@ class ColorBalanceShaderNode : public ShaderNode { "node_composite_color_balance_asc_cdl", inputs, outputs, - GPU_uniform(node_color_balance->offset), - GPU_uniform(node_color_balance->power), - GPU_uniform(node_color_balance->slope), - GPU_uniform(&node_color_balance->offset_basis)); + GPU_uniform(node_color_balance.offset), + GPU_uniform(node_color_balance.power), + GPU_uniform(node_color_balance.slope), + GPU_uniform(&node_color_balance.offset_basis)); } CMPNodeColorBalanceMethod get_color_balance_method() { return (CMPNodeColorBalanceMethod)bnode().custom1; } - - const NodeColorBalance *get_node_color_balance() - { - return static_cast<const NodeColorBalance *>(bnode().storage); - } }; static ShaderNode *get_compositor_shader_node(DNode node) diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc index f6bc3d1fdf2..92b10fc1877 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc @@ -20,6 +20,8 @@ namespace blender::nodes::node_composite_colorcorrection_cc { +NODE_STORAGE_FUNCS(NodeColorCorrection) + static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -294,7 +296,7 @@ class ColorCorrectionShaderNode : public ShaderNode { float luminance_coefficients[3]; IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); - const NodeColorCorrection *node_color_correction = get_node_color_correction(); + const NodeColorCorrection &node_color_correction = node_storage(bnode()); GPU_stack_link(material, &bnode(), @@ -302,28 +304,28 @@ class ColorCorrectionShaderNode : public ShaderNode { inputs, outputs, GPU_constant(enabled_channels), - GPU_uniform(&node_color_correction->startmidtones), - GPU_uniform(&node_color_correction->endmidtones), - GPU_uniform(&node_color_correction->master.saturation), - GPU_uniform(&node_color_correction->master.contrast), - GPU_uniform(&node_color_correction->master.gamma), - GPU_uniform(&node_color_correction->master.gain), - GPU_uniform(&node_color_correction->master.lift), - GPU_uniform(&node_color_correction->shadows.saturation), - GPU_uniform(&node_color_correction->shadows.contrast), - GPU_uniform(&node_color_correction->shadows.gamma), - GPU_uniform(&node_color_correction->shadows.gain), - GPU_uniform(&node_color_correction->shadows.lift), - GPU_uniform(&node_color_correction->midtones.saturation), - GPU_uniform(&node_color_correction->midtones.contrast), - GPU_uniform(&node_color_correction->midtones.gamma), - GPU_uniform(&node_color_correction->midtones.gain), - GPU_uniform(&node_color_correction->midtones.lift), - GPU_uniform(&node_color_correction->highlights.saturation), - GPU_uniform(&node_color_correction->highlights.contrast), - GPU_uniform(&node_color_correction->highlights.gamma), - GPU_uniform(&node_color_correction->highlights.gain), - GPU_uniform(&node_color_correction->highlights.lift), + GPU_uniform(&node_color_correction.startmidtones), + GPU_uniform(&node_color_correction.endmidtones), + GPU_uniform(&node_color_correction.master.saturation), + GPU_uniform(&node_color_correction.master.contrast), + GPU_uniform(&node_color_correction.master.gamma), + GPU_uniform(&node_color_correction.master.gain), + GPU_uniform(&node_color_correction.master.lift), + GPU_uniform(&node_color_correction.shadows.saturation), + GPU_uniform(&node_color_correction.shadows.contrast), + GPU_uniform(&node_color_correction.shadows.gamma), + GPU_uniform(&node_color_correction.shadows.gain), + GPU_uniform(&node_color_correction.shadows.lift), + GPU_uniform(&node_color_correction.midtones.saturation), + GPU_uniform(&node_color_correction.midtones.contrast), + GPU_uniform(&node_color_correction.midtones.gamma), + GPU_uniform(&node_color_correction.midtones.gain), + GPU_uniform(&node_color_correction.midtones.lift), + GPU_uniform(&node_color_correction.highlights.saturation), + GPU_uniform(&node_color_correction.highlights.contrast), + GPU_uniform(&node_color_correction.highlights.gamma), + GPU_uniform(&node_color_correction.highlights.gain), + GPU_uniform(&node_color_correction.highlights.lift), GPU_constant(luminance_coefficients)); } @@ -333,11 +335,6 @@ class ColorCorrectionShaderNode : public ShaderNode { enabled_channels[i] = (bnode().custom1 & (1 << i)) ? 1.0f : 0.0f; } } - - const NodeColorCorrection *get_node_color_correction() - { - return static_cast<const NodeColorCorrection *>(bnode().storage); - } }; static ShaderNode *get_compositor_shader_node(DNode node) diff --git a/source/blender/nodes/composite/nodes/node_composite_crop.cc b/source/blender/nodes/composite/nodes/node_composite_crop.cc index 466c842812c..13d02a707be 100644 --- a/source/blender/nodes/composite/nodes/node_composite_crop.cc +++ b/source/blender/nodes/composite/nodes/node_composite_crop.cc @@ -27,6 +27,8 @@ namespace blender::nodes::node_composite_crop_cc { +NODE_STORAGE_FUNCS(NodeTwoXYs) + static void cmp_node_crop_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -163,11 +165,6 @@ class CropOperation : public NodeOperation { return bnode().custom2; } - const NodeTwoXYs &get_node_two_xys() - { - return *static_cast<const NodeTwoXYs *>(bnode().storage); - } - /* Returns true if the operation does nothing and the input can be passed through. */ bool is_identity() { @@ -190,7 +187,7 @@ class CropOperation : public NodeOperation { void compute_cropping_bounds(int2 &lower_bound, int2 &upper_bound) { - const NodeTwoXYs &node_two_xys = get_node_two_xys(); + const NodeTwoXYs &node_two_xys = node_storage(bnode()); const int2 input_size = get_input("Image").domain().size; if (get_is_relative()) { diff --git a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc index 3c830f1deec..8912d00a9be 100644 --- a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc @@ -18,6 +18,8 @@ namespace blender::nodes::node_composite_diff_matte_cc { +NODE_STORAGE_FUNCS(NodeChroma) + static void cmp_node_diff_matte_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image 1")) @@ -71,19 +73,14 @@ class DifferenceMatteShaderNode : public ShaderNode { GPU_uniform(&falloff)); } - const NodeChroma *get_node_chroma() - { - return static_cast<const NodeChroma *>(bnode().storage); - } - float get_tolerance() { - return get_node_chroma()->t1; + return node_storage(bnode()).t1; } float get_falloff() { - return get_node_chroma()->t2; + return node_storage(bnode()).t2; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc index b662924acec..6e6bec70283 100644 --- a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc @@ -22,6 +22,8 @@ namespace blender::nodes::node_composite_directionalblur_cc { +NODE_STORAGE_FUNCS(NodeDBlurData) + static void cmp_node_directional_blur_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -109,28 +111,27 @@ class DirectionalBlurOperation : public NodeOperation { float2 get_translation() { const float diagonal_length = math::length(float2(get_input("Image").domain().size)); - const float translation_amount = diagonal_length * get_node_directional_blur_data().distance; - const float3x3 rotation = float3x3::from_rotation(-get_node_directional_blur_data().angle); + const float translation_amount = diagonal_length * node_storage(bnode()).distance; + const float3x3 rotation = float3x3::from_rotation(-node_storage(bnode()).angle); return rotation * float2(-translation_amount / get_iterations(), 0.0f); } /* Get the amount of rotation that will be applied on each iteration. */ float get_rotation() { - return get_node_directional_blur_data().spin / get_iterations(); + return node_storage(bnode()).spin / get_iterations(); } /* Get the amount of scale that will be applied on each iteration. The scale is identity when the * user supplies 0, so we add 1. */ float2 get_scale() { - return float2(1.0f + get_node_directional_blur_data().zoom / get_iterations()); + return float2(1.0f + node_storage(bnode()).zoom / get_iterations()); } float2 get_origin() { - const float2 center = float2(get_node_directional_blur_data().center_x, - get_node_directional_blur_data().center_y); + const float2 center = float2(node_storage(bnode()).center_x, node_storage(bnode()).center_y); return float2(get_input("Image").domain().size) * center; } @@ -151,7 +152,7 @@ class DirectionalBlurOperation : public NodeOperation { * is the number of diagonal pixels. */ int get_iterations() { - const int iterations = 2 << (get_node_directional_blur_data().iter - 1); + const int iterations = 2 << (node_storage(bnode()).iter - 1); const int upper_limit = math::ceil(math::length(float2(get_input("Image").domain().size))); return math::min(iterations, upper_limit); } @@ -166,25 +167,20 @@ class DirectionalBlurOperation : public NodeOperation { } /* If any of the following options are non-zero, then the operation is not an identity. */ - if (get_node_directional_blur_data().distance != 0.0f) { + if (node_storage(bnode()).distance != 0.0f) { return false; } - if (get_node_directional_blur_data().spin != 0.0f) { + if (node_storage(bnode()).spin != 0.0f) { return false; } - if (get_node_directional_blur_data().zoom != 0.0f) { + if (node_storage(bnode()).zoom != 0.0f) { return false; } return true; } - - const NodeDBlurData &get_node_directional_blur_data() - { - return *static_cast<const NodeDBlurData *>(bnode().storage); - } }; static NodeOperation *get_compositor_operation(Context &context, DNode node) diff --git a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc index be73412e027..6a786571f43 100644 --- a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc @@ -18,6 +18,8 @@ namespace blender::nodes::node_composite_distance_matte_cc { +NODE_STORAGE_FUNCS(NodeChroma) + static void cmp_node_distance_matte_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -90,24 +92,19 @@ class DistanceMatteShaderNode : public ShaderNode { GPU_uniform(&falloff)); } - const NodeChroma *get_node_chroma() - { - return static_cast<const NodeChroma *>(bnode().storage); - } - CMPNodeDistanceMatteColorSpace get_color_space() { - return (CMPNodeDistanceMatteColorSpace)get_node_chroma()->channel; + return (CMPNodeDistanceMatteColorSpace)node_storage(bnode()).channel; } float get_tolerance() { - return get_node_chroma()->t1; + return node_storage(bnode()).t1; } float get_falloff() { - return get_node_chroma()->t2; + return node_storage(bnode()).t2; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc index d9d8a888f24..7c031b354e5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc @@ -23,6 +23,8 @@ namespace blender::nodes::node_composite_ellipsemask_cc { +NODE_STORAGE_FUNCS(NodeEllipseMask) + static void cmp_node_ellipsemask_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f); @@ -121,24 +123,19 @@ class EllipseMaskOperation : public NodeOperation { } } - const NodeEllipseMask &get_node_ellipse_mask() - { - return *static_cast<const NodeEllipseMask *>(bnode().storage); - } - float2 get_location() { - return float2(get_node_ellipse_mask().x, get_node_ellipse_mask().y); + return float2(node_storage(bnode()).x, node_storage(bnode()).y); } float2 get_size() { - return float2(get_node_ellipse_mask().width, get_node_ellipse_mask().height); + return float2(node_storage(bnode()).width, node_storage(bnode()).height); } float get_angle() { - return get_node_ellipse_mask().rotation; + return node_storage(bnode()).rotation; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc index 2d4c0afcda7..260fccf66d0 100644 --- a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc +++ b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc @@ -32,6 +32,8 @@ namespace blender::nodes::node_composite_lensdist_cc { +NODE_STORAGE_FUNCS(NodeLensDist) + static void cmp_node_lensdist_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -197,22 +199,17 @@ class LensDistortionOperation : public NodeOperation { bool get_is_projector() { - return get_node_lens_distortion().proj; + return node_storage(bnode()).proj; } bool get_is_jitter() { - return get_node_lens_distortion().jit; + return node_storage(bnode()).jit; } bool get_is_fit() { - return get_node_lens_distortion().fit; - } - - NodeLensDist &get_node_lens_distortion() - { - return *static_cast<NodeLensDist *>(bnode().storage); + return node_storage(bnode()).fit; } /* Returns true if the operation does nothing and the input can be passed through. */ diff --git a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc index 092a12a7ea4..59ae62ec411 100644 --- a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc @@ -20,6 +20,8 @@ namespace blender::nodes::node_composite_luma_matte_cc { +NODE_STORAGE_FUNCS(NodeChroma) + static void cmp_node_luma_matte_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -74,19 +76,14 @@ class LuminanceMatteShaderNode : public ShaderNode { GPU_constant(luminance_coefficients)); } - NodeChroma *get_node_chroma() - { - return static_cast<NodeChroma *>(bnode().storage); - } - float get_high() { - return get_node_chroma()->t1; + return node_storage(bnode()).t1; } float get_low() { - return get_node_chroma()->t2; + return node_storage(bnode()).t2; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_map_value.cc b/source/blender/nodes/composite/nodes/node_composite_map_value.cc index 2b0aebbede8..e30de39605d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_map_value.cc +++ b/source/blender/nodes/composite/nodes/node_composite_map_value.cc @@ -22,6 +22,8 @@ namespace blender::nodes::node_composite_map_value_cc { +NODE_STORAGE_FUNCS(TexMapping) + static void cmp_node_map_value_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Float>(N_("Value")) @@ -69,7 +71,7 @@ class MapValueShaderNode : public ShaderNode { GPUNodeStack *inputs = get_inputs_array(); GPUNodeStack *outputs = get_outputs_array(); - const TexMapping *texture_mapping = get_texture_mapping(); + const TexMapping &texture_mapping = node_storage(bnode()); const float use_min = get_use_min(); const float use_max = get_use_max(); @@ -79,27 +81,22 @@ class MapValueShaderNode : public ShaderNode { "node_composite_map_value", inputs, outputs, - GPU_uniform(texture_mapping->loc), - GPU_uniform(texture_mapping->size), + GPU_uniform(texture_mapping.loc), + GPU_uniform(texture_mapping.size), GPU_constant(&use_min), - GPU_uniform(texture_mapping->min), + GPU_uniform(texture_mapping.min), GPU_constant(&use_max), - GPU_uniform(texture_mapping->max)); - } - - const TexMapping *get_texture_mapping() - { - return static_cast<const TexMapping *>(bnode().storage); + GPU_uniform(texture_mapping.max)); } bool get_use_min() { - return get_texture_mapping()->flag & TEXMAP_CLIP_MIN; + return node_storage(bnode()).flag & TEXMAP_CLIP_MIN; } bool get_use_max() { - return get_texture_mapping()->flag & TEXMAP_CLIP_MAX; + return node_storage(bnode()).flag & TEXMAP_CLIP_MAX; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc index 7c1a61cedc4..b9d9620a214 100644 --- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc +++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc @@ -239,7 +239,7 @@ class MovieClipOperation : public NodeOperation { GPUTexture *get_movie_clip_texture() { MovieClip *movie_clip = get_movie_clip(); - MovieClipUser *movie_clip_user = static_cast<MovieClipUser *>(bnode().storage); + MovieClipUser *movie_clip_user = get_movie_clip_user(); BKE_movieclip_user_set_frame(movie_clip_user, context().get_frame_number()); return BKE_movieclip_get_gpu_texture(movie_clip, movie_clip_user); } @@ -256,6 +256,11 @@ class MovieClipOperation : public NodeOperation { { return (MovieClip *)bnode().id; } + + MovieClipUser *get_movie_clip_user() + { + return static_cast<MovieClipUser *>(bnode().storage); + } }; static NodeOperation *get_compositor_operation(Context &context, DNode node) diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc index d1f0b7977f8..f6792d7ce61 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc @@ -62,6 +62,8 @@ static void node_cmp_combsep_color_label(const ListBase *sockets, CMPNodeCombSep namespace blender::nodes::node_composite_separate_color_cc { +NODE_STORAGE_FUNCS(NodeCMPCombSepColor) + static void cmp_node_separate_color_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -93,14 +95,9 @@ class SeparateColorShaderNode : public ShaderNode { GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); } - NodeCMPCombSepColor *get_node_combine_separate_color() - { - return static_cast<NodeCMPCombSepColor *>(bnode().storage); - } - const char *get_shader_function_name() { - switch (get_node_combine_separate_color()->mode) { + switch (node_storage(bnode()).mode) { case CMP_NODE_COMBSEP_COLOR_RGB: return "node_composite_separate_rgba"; case CMP_NODE_COMBSEP_COLOR_HSV: @@ -110,7 +107,7 @@ class SeparateColorShaderNode : public ShaderNode { case CMP_NODE_COMBSEP_COLOR_YUV: return "node_composite_separate_yuva_itu_709"; case CMP_NODE_COMBSEP_COLOR_YCC: - switch (get_node_combine_separate_color()->ycc_mode) { + switch (node_storage(bnode()).ycc_mode) { case BLI_YCC_ITU_BT601: return "node_composite_separate_ycca_itu_601"; case BLI_YCC_ITU_BT709: @@ -153,6 +150,8 @@ void register_node_type_cmp_separate_color() namespace blender::nodes::node_composite_combine_color_cc { +NODE_STORAGE_FUNCS(NodeCMPCombSepColor) + static void cmp_node_combine_color_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Float>(N_("Red")) @@ -202,14 +201,9 @@ class CombineColorShaderNode : public ShaderNode { GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); } - NodeCMPCombSepColor *get_node_combine_separate_color() - { - return static_cast<NodeCMPCombSepColor *>(bnode().storage); - } - const char *get_shader_function_name() { - switch (get_node_combine_separate_color()->mode) { + switch (node_storage(bnode()).mode) { case CMP_NODE_COMBSEP_COLOR_RGB: return "node_composite_combine_rgba"; case CMP_NODE_COMBSEP_COLOR_HSV: @@ -219,7 +213,7 @@ class CombineColorShaderNode : public ShaderNode { case CMP_NODE_COMBSEP_COLOR_YUV: return "node_composite_combine_yuva_itu_709"; case CMP_NODE_COMBSEP_COLOR_YCC: - switch (get_node_combine_separate_color()->ycc_mode) { + switch (node_storage(bnode()).ycc_mode) { case BLI_YCC_ITU_BT601: return "node_composite_combine_ycca_itu_601"; case BLI_YCC_ITU_BT709: diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc index 383b4bcd0ca..df3aca2c665 100644 --- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc +++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc @@ -18,6 +18,8 @@ namespace blender::nodes::node_composite_setalpha_cc { +NODE_STORAGE_FUNCS(NodeSetAlpha) + static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -54,18 +56,13 @@ class SetAlphaShaderNode : public ShaderNode { GPUNodeStack *inputs = get_inputs_array(); GPUNodeStack *outputs = get_outputs_array(); - if (get_node_set_alpha()->mode == CMP_NODE_SETALPHA_MODE_APPLY) { + if (node_storage(bnode()).mode == CMP_NODE_SETALPHA_MODE_APPLY) { GPU_stack_link(material, &bnode(), "node_composite_set_alpha_apply", inputs, outputs); return; } GPU_stack_link(material, &bnode(), "node_composite_set_alpha_replace", inputs, outputs); } - - const NodeSetAlpha *get_node_set_alpha() - { - return static_cast<const NodeSetAlpha *>(bnode().storage); - } }; static ShaderNode *get_compositor_shader_node(DNode node) diff --git a/source/blender/nodes/composite/nodes/node_composite_translate.cc b/source/blender/nodes/composite/nodes/node_composite_translate.cc index dcb67b9be90..e0f87ff604a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_translate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_translate.cc @@ -19,6 +19,8 @@ namespace blender::nodes::node_composite_translate_cc { +NODE_STORAGE_FUNCS(NodeTranslateData) + static void cmp_node_translate_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -76,24 +78,19 @@ class TranslateOperation : public NodeOperation { result.get_realization_options().repeat_y = get_repeat_y(); } - const NodeTranslateData &get_node_translate() - { - return *static_cast<const NodeTranslateData *>(bnode().storage); - } - bool get_use_relative() { - return get_node_translate().relative; + return node_storage(bnode()).relative; } bool get_repeat_x() { - return ELEM(get_node_translate().wrap_axis, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY); + return ELEM(node_storage(bnode()).wrap_axis, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY); } bool get_repeat_y() { - return ELEM(get_node_translate().wrap_axis, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY); + return ELEM(node_storage(bnode()).wrap_axis, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY); } }; diff --git a/source/blender/nodes/shader/nodes/node_shader_attribute.cc b/source/blender/nodes/shader/nodes/node_shader_attribute.cc index d01271c15d3..65d053e6379 100644 --- a/source/blender/nodes/shader/nodes/node_shader_attribute.cc +++ b/source/blender/nodes/shader/nodes/node_shader_attribute.cc @@ -36,6 +36,7 @@ static int node_shader_gpu_attribute(GPUMaterial *mat, { NodeShaderAttribute *attr = static_cast<NodeShaderAttribute *>(node->storage); bool is_varying = attr->type == SHD_ATTRIBUTE_GEOMETRY; + float attr_hash = 0.0f; GPUNodeLink *cd_attr; @@ -43,7 +44,12 @@ static int node_shader_gpu_attribute(GPUMaterial *mat, cd_attr = GPU_attribute(mat, CD_AUTO_FROM_NAME, attr->name); } else { - cd_attr = GPU_uniform_attribute(mat, attr->name, attr->type == SHD_ATTRIBUTE_INSTANCER); + cd_attr = GPU_uniform_attribute(mat, + attr->name, + attr->type == SHD_ATTRIBUTE_INSTANCER, + reinterpret_cast<uint32_t *>(&attr_hash)); + + GPU_link(mat, "node_attribute_uniform", cd_attr, GPU_constant(&attr_hash), &cd_attr); } if (STREQ(attr->name, "color")) { @@ -55,9 +61,11 @@ static int node_shader_gpu_attribute(GPUMaterial *mat, GPU_stack_link(mat, node, "node_attribute", in, out, cd_attr); - int i; - LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) { - node_shader_gpu_bump_tex_coord(mat, node, &out[i].link); + if (is_varying) { + int i; + LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) { + node_shader_gpu_bump_tex_coord(mat, node, &out[i].link); + } } return 1; diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc index a63c7aede04..2f75b7b533f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc @@ -167,6 +167,27 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, if (use_transparency) { flag |= GPU_MATFLAG_TRANSPARENT; } + if (use_clear) { + flag |= GPU_MATFLAG_CLEARCOAT; + } + + /* Ref. T98190: Defines are optimizations for old compilers. + * Might become unecessary with EEVEE-Next. */ + if (use_diffuse == false && use_refract == false && use_clear == true) { + flag |= GPU_MATFLAG_PRINCIPLED_CLEARCOAT; + } + else if (use_diffuse == false && use_refract == false && use_clear == false) { + flag |= GPU_MATFLAG_PRINCIPLED_METALLIC; + } + else if (use_diffuse == true && use_refract == false && use_clear == false) { + flag |= GPU_MATFLAG_PRINCIPLED_DIELECTRIC; + } + else if (use_diffuse == false && use_refract == true && use_clear == false) { + flag |= GPU_MATFLAG_PRINCIPLED_GLASS; + } + else { + flag |= GPU_MATFLAG_PRINCIPLED_ANY; + } if (use_subsurf) { bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2); diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_info.cc b/source/blender/nodes/shader/nodes/node_shader_volume_info.cc index a202312f8d8..1f31e9c605f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_volume_info.cc +++ b/source/blender/nodes/shader/nodes/node_shader_volume_info.cc @@ -25,9 +25,11 @@ static int node_shader_gpu_volume_info(GPUMaterial *mat, } if (out[1].hasoutput) { out[1].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "density"); + GPU_link(mat, "node_attribute_density", out[1].link, &out[1].link); } if (out[2].hasoutput) { out[2].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "flame"); + GPU_link(mat, "node_attribute_flame", out[2].link, &out[2].link); } if (out[3].hasoutput) { out[3].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "temperature"); diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h index 66057c06058..6007a64a054 100644 --- a/source/blender/render/RE_pipeline.h +++ b/source/blender/render/RE_pipeline.h @@ -461,9 +461,9 @@ bool RE_allow_render_generic_object(struct Object *ob); /******* defined in render_result.c *********/ -bool RE_HasCombinedLayer(const RenderResult *res); -bool RE_HasFloatPixels(const RenderResult *res); -bool RE_RenderResult_is_stereo(const RenderResult *res); +bool RE_HasCombinedLayer(const RenderResult *result); +bool RE_HasFloatPixels(const RenderResult *result); +bool RE_RenderResult_is_stereo(const RenderResult *result); struct RenderView *RE_RenderViewGetById(struct RenderResult *rr, int view_id); struct RenderView *RE_RenderViewGetByName(struct RenderResult *rr, const char *viewname); diff --git a/source/blender/render/intern/engine.cc b/source/blender/render/intern/engine.cc index 97aad39d5db..a440b34af78 100644 --- a/source/blender/render/intern/engine.cc +++ b/source/blender/render/intern/engine.cc @@ -5,9 +5,9 @@ * \ingroup render */ -#include <stddef.h> -#include <stdlib.h> -#include <string.h> +#include <cstddef> +#include <cstdlib> +#include <cstring> #include "MEM_guardedalloc.h" @@ -499,7 +499,7 @@ bool RE_engine_test_break(RenderEngine *engine) return re->test_break(re->tbh); } - return 0; + return false; } /* Statistics */ @@ -642,7 +642,7 @@ void RE_engine_get_camera_model_matrix(RenderEngine *engine, bool RE_engine_get_spherical_stereo(RenderEngine *engine, Object *camera) { Render *re = engine->re; - return BKE_camera_multiview_spherical_stereo(re ? &re->r : nullptr, camera) ? 1 : 0; + return BKE_camera_multiview_spherical_stereo(re ? &re->r : nullptr, camera) ? true : false; } rcti *RE_engine_get_current_tiles(Render *re, int *r_total_tiles, bool *r_needs_free) @@ -1292,16 +1292,12 @@ bool RE_engine_gpu_context_enable(RenderEngine *engine) DRW_render_context_enable(engine->re); return true; } - else { - if (engine->gpu_context) { - BLI_mutex_lock(&engine->gpu_context_mutex); - WM_opengl_context_activate(engine->gpu_context); - return true; - } - else { - return false; - } + if (engine->gpu_context) { + BLI_mutex_lock(&engine->gpu_context_mutex); + WM_opengl_context_activate(engine->gpu_context); + return true; } + return false; } void RE_engine_gpu_context_disable(RenderEngine *engine) diff --git a/source/blender/render/intern/initrender.cc b/source/blender/render/intern/initrender.cc index a2a6a5815a0..cc05aa8621e 100644 --- a/source/blender/render/intern/initrender.cc +++ b/source/blender/render/intern/initrender.cc @@ -7,10 +7,10 @@ /* Global includes */ -#include <math.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> +#include <cmath> +#include <cstdio> +#include <cstdlib> +#include <cstring> #include "MEM_guardedalloc.h" diff --git a/source/blender/render/intern/pipeline.cc b/source/blender/render/intern/pipeline.cc index 746adeaddb2..d9ffb09c5a4 100644 --- a/source/blender/render/intern/pipeline.cc +++ b/source/blender/render/intern/pipeline.cc @@ -5,12 +5,12 @@ * \ingroup render */ -#include <errno.h> -#include <limits.h> -#include <math.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> +#include <cerrno> +#include <climits> +#include <cmath> +#include <cstddef> +#include <cstdlib> +#include <cstring> #include "DNA_anim_types.h" #include "DNA_collection_types.h" @@ -1037,7 +1037,7 @@ static void do_render_compositor_scene(Render *re, Scene *sce, int cfra) BKE_scene_camera_switch_update(sce); /* exception: scene uses own size (unfinished code) */ - if (0) { + if (false) { BKE_render_resolution(&sce->r, false, &winx, &winy); } @@ -1570,7 +1570,7 @@ bool RE_is_rendering_allowed(Scene *scene, if (scene->r.border.xmax <= scene->r.border.xmin || scene->r.border.ymax <= scene->r.border.ymin) { BKE_report(reports, RPT_ERROR, "No border area selected"); - return 0; + return false; } } @@ -1585,28 +1585,28 @@ bool RE_is_rendering_allowed(Scene *scene, /* Compositor */ if (!scene->nodetree) { BKE_report(reports, RPT_ERROR, "No node tree in scene"); - return 0; + return false; } if (!check_compositor_output(scene)) { BKE_report(reports, RPT_ERROR, "No render output node in scene"); - return 0; + return false; } } else { /* Regular Render */ if (!render_scene_has_layers_to_render(scene, single_layer)) { BKE_report(reports, RPT_ERROR, "All render layers are disabled"); - return 0; + return false; } } /* check valid camera, without camera render is OK (compo, seq) */ if (!check_valid_camera(scene, camera_override, reports)) { - return 0; + return false; } - return 1; + return true; } static void update_physics_cache(Render *re, @@ -1690,7 +1690,7 @@ static int render_init_from_main(Render *re, * can be later set as render profile option * and default for background render. */ - if (0) { + if (false) { /* make sure dynamics are up to date */ ViewLayer *view_layer = BKE_view_layer_context_active_PLACEHOLDER(scene); update_physics_cache(re, scene, view_layer, anim_init); @@ -2534,13 +2534,13 @@ void RE_result_load_from_file(RenderResult *result, ReportList *reports, const c } } -bool RE_layers_have_name(struct RenderResult *rr) +bool RE_layers_have_name(struct RenderResult *result) { - switch (BLI_listbase_count_at_most(&rr->layers, 2)) { + switch (BLI_listbase_count_at_most(&result->layers, 2)) { case 0: return false; case 1: - return (((RenderLayer *)rr->layers.first)->name[0] != '\0'); + return (((RenderLayer *)result->layers.first)->name[0] != '\0'); default: return true; } diff --git a/source/blender/render/intern/render_result.cc b/source/blender/render/intern/render_result.cc index 8edd91e0953..86ee9ad779a 100644 --- a/source/blender/render/intern/render_result.cc +++ b/source/blender/render/intern/render_result.cc @@ -5,10 +5,10 @@ * \ingroup render */ -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> +#include <cerrno> +#include <cstdio> +#include <cstdlib> +#include <cstring> #include "MEM_guardedalloc.h" @@ -1134,13 +1134,13 @@ void render_result_rect_get_pixels(RenderResult *rr, /*************************** multiview functions *****************************/ -bool RE_HasCombinedLayer(const RenderResult *rr) +bool RE_HasCombinedLayer(const RenderResult *result) { - if (rr == nullptr) { + if (result == nullptr) { return false; } - const RenderView *rv = static_cast<RenderView *>(rr->views.first); + const RenderView *rv = static_cast<RenderView *>(result->views.first); if (rv == nullptr) { return false; } @@ -1148,9 +1148,9 @@ bool RE_HasCombinedLayer(const RenderResult *rr) return (rv->rect32 || rv->rectf); } -bool RE_HasFloatPixels(const RenderResult *rr) +bool RE_HasFloatPixels(const RenderResult *result) { - LISTBASE_FOREACH (const RenderView *, rview, &rr->views) { + LISTBASE_FOREACH (const RenderView *, rview, &result->views) { if (rview->rect32 && !rview->rectf) { return false; } @@ -1159,13 +1159,13 @@ bool RE_HasFloatPixels(const RenderResult *rr) return true; } -bool RE_RenderResult_is_stereo(const RenderResult *rr) +bool RE_RenderResult_is_stereo(const RenderResult *result) { - if (!BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name))) { + if (!BLI_findstring(&result->views, STEREO_LEFT_NAME, offsetof(RenderView, name))) { return false; } - if (!BLI_findstring(&rr->views, STEREO_RIGHT_NAME, offsetof(RenderView, name))) { + if (!BLI_findstring(&result->views, STEREO_RIGHT_NAME, offsetof(RenderView, name))) { return false; } diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index 53a969d7fea..4548975574d 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -426,22 +426,22 @@ MetaStack *SEQ_meta_stack_active_get(const Editing *ed) return ed->metastack.last; } -void SEQ_meta_stack_set(const Scene *scene, Sequence *seqm) +void SEQ_meta_stack_set(const Scene *scene, Sequence *dst_seq) { Editing *ed = SEQ_editing_get(scene); /* Clear metastack */ BLI_freelistN(&ed->metastack); - if (seqm != NULL) { + if (dst_seq != NULL) { /* Allocate meta stack in a way, that represents meta hierarchy in timeline. */ - seq_meta_stack_alloc(scene, seqm); - Sequence *meta_parent = seqm; + seq_meta_stack_alloc(scene, dst_seq); + Sequence *meta_parent = dst_seq; while ((meta_parent = seq_sequence_lookup_meta_by_seq(scene, meta_parent))) { seq_meta_stack_alloc(scene, meta_parent); } - SEQ_seqbase_active_set(ed, &seqm->seqbase); - SEQ_channels_displayed_set(ed, &seqm->channels); + SEQ_seqbase_active_set(ed, &dst_seq->seqbase); + SEQ_channels_displayed_set(ed, &dst_seq->channels); } else { /* Go to top level, exiting meta strip. */ |