Blender V5.0
eevee_light_shared.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#pragma once
10
11#include "eevee_defines.hh"
12#include "eevee_transform.hh"
13
14#ifndef GPU_SHADER
15# include "BLI_math_bits.h"
16
17namespace blender::eevee {
18#endif
19
20#define LIGHT_NO_SHADOW -1
21
22enum eLightType : uint32_t {
25 /* Point light. */
28 /* Spot light. */
31 /* Area light. */
34};
35
36static inline bool is_area_light(eLightType type)
37{
38 return type >= LIGHT_RECT;
39}
40
41static inline bool is_point_light(eLightType type)
42{
43 return type >= LIGHT_OMNI_SPHERE && type <= LIGHT_SPOT_DISK;
44}
45
46static inline bool is_spot_light(eLightType type)
47{
48 return type == LIGHT_SPOT_SPHERE || type == LIGHT_SPOT_DISK;
49}
50
51static inline bool is_sphere_light(eLightType type)
52{
53 return type == LIGHT_SPOT_SPHERE || type == LIGHT_OMNI_SPHERE;
54}
55
56static inline bool is_oriented_disk_light(eLightType type)
57{
58 return type == LIGHT_SPOT_DISK || type == LIGHT_OMNI_DISK;
59}
60
61static inline bool is_sun_light(eLightType type)
62{
63 return type < LIGHT_OMNI_SPHERE;
64}
65
66static inline bool is_local_light(eLightType type)
67{
68 return type >= LIGHT_OMNI_SPHERE;
69}
70
71/* Using define because GLSL doesn't have inheritance, and encapsulation forces us to add some
72 * unneeded padding. */
73#define LOCAL_LIGHT_COMMON \
74 \
75 \
76 packed_float3 shadow_position; \
77 float _pad0; \
78 \
79 float shadow_radius; \
80 \
81 float shape_radius; \
82 \
83 float influence_radius_max; \
84 \
85 float influence_radius_invsqr_surface; \
86 float influence_radius_invsqr_volume; \
87 \
88 int tilemaps_count;
89
90/* Untyped local light data. Gets reinterpreted to LightSpotData and LightAreaData.
91 * Allow access to local light common data without casting. */
94
95 float _pad1;
96 float _pad2;
97
99 float _pad4;
100 float _pad5;
101};
103
104/* Despite the name, is also used for omni light. */
118BLI_STATIC_ASSERT(sizeof(LightSpotData) == sizeof(LightLocalData), "Data size must match")
119
120struct LightAreaData {
122
123 float _pad2;
124 float _pad3;
125
127 float2 size;
129 float shadow_scale;
130 float _pad6;
131};
132BLI_STATIC_ASSERT(sizeof(LightAreaData) == sizeof(LightLocalData), "Data size must match")
133
134struct LightSunData {
135 /* Sun direction for shading. Use object_to_world for getting into shadow space. */
136 packed_float3 direction;
137 /* Radius of the sun disk, one unit away from a shading point. */
138 float shape_radius;
139
142 int2 clipmap_base_offset_neg;
143 int2 clipmap_base_offset_pos;
144
146 float shadow_angle;
147 float _pad5;
148 float _pad3;
149 float _pad4;
150
152 float2 clipmap_origin;
154 int clipmap_lod_min;
155 int clipmap_lod_max;
156};
157BLI_STATIC_ASSERT(sizeof(LightSunData) == sizeof(LightLocalData), "Data size must match")
158
159/* Enable when debugging. This is quite costly. */
160#define SAFE_UNION_ACCESS 0
161
162#ifndef GPU_SHADER
163/* C++ always uses union. */
164# define USE_LIGHT_UNION 1
165#elif defined(GPU_BACKEND_METAL) && !SAFE_UNION_ACCESS
166/* Metal supports union, but force usage of the getters if SAFE_UNION_ACCESS is enabled. */
167# define USE_LIGHT_UNION 1
168#else
169/* Use getter functions on GPU if not supported or if SAFE_UNION_ACCESS is enabled. */
170# define USE_LIGHT_UNION 0
171#endif
172
173struct LightData {
179 Transform object_to_world;
180
186 eLightType type;
187
190 int clip_near;
191 int clip_far;
193 int tilemap_index;
194 /* Radius in pixels for shadow filtering. */
195 float filter_radius;
196
197 /* Shadow Map resolution bias. */
198 float lod_bias;
199 /* Shadow Map resolution maximum resolution. */
200 float lod_min;
201 /* True if the light uses jittered soft shadows. */
202 bool32_t shadow_jitter;
203 float _pad2;
204 uint2 light_set_membership;
206 /* TODO(fclem): this should be part of #eevee::Light struct. But for some reason it gets cleared
207 * to zero after each sync cycle. */
208 uint2 shadow_set_membership;
209
210#if USE_LIGHT_UNION
211 union {
212 LightLocalData local;
213 LightSpotData spot;
214 LightAreaData area;
215 LightSunData sun;
216 };
217#else
218 /* Use `light_*_data_get(light)` to access typed data. */
219 LightLocalData do_not_access_directly;
220#endif
221};
223
224static inline float3 light_x_axis(LightData light)
225{
226 return transform_x_axis(light.object_to_world);
227}
228static inline float3 light_y_axis(LightData light)
229{
230 return transform_y_axis(light.object_to_world);
231}
232static inline float3 light_z_axis(LightData light)
233{
234 return transform_z_axis(light.object_to_world);
235}
237{
238 return transform_location(light.object_to_world);
239}
240
241#ifdef GPU_SHADER
242# define CHECK_TYPE_PAIR(a, b)
243# define CHECK_TYPE(a, b)
244# define FLOAT_AS_INT floatBitsToInt
245# define INT_AS_FLOAT intBitsToFloat
246# define TYPECAST_NOOP
247
248#else /* C++ */
249# define FLOAT_AS_INT float_as_int
250# define INT_AS_FLOAT int_as_float
251# define TYPECAST_NOOP
252#endif
253
254/* In addition to the static asserts that verify correct member assignment, also verify access on
255 * the GPU so that only lights of a certain type can read for the appropriate union member.
256 * Return cross platform garbage data as some platform can return cleared memory if we early exit.
257 */
258#if SAFE_UNION_ACCESS
259# ifdef GPU_SHADER
260/* Should result in a beautiful zebra pattern on invalid load. */
261# if defined(GPU_FRAGMENT_SHADER)
262# define GARBAGE_VALUE sin(gl_FragCoord.x + gl_FragCoord.y)
263# elif defined(GPU_COMPUTE_SHADER)
264# define GARBAGE_VALUE \
265 sin(float(gl_GlobalInvocationID.x + gl_GlobalInvocationID.y + gl_GlobalInvocationID.z))
266# else
267# define GARBAGE_VALUE sin(float(gl_VertexID))
268# endif
269
270/* Can be set to zero if zebra creates out-of-bound accesses and crashes. At least avoid UB. */
271// # define GARBAGE_VALUE 0.0
272
273# else /* C++ */
274# define GARBAGE_VALUE 0.0f
275# endif
276
277# define SAFE_BEGIN(dst_type, src_type, src_, check) \
278 src_type _src = src_; \
279 dst_type _dst; \
280 bool _validity_check = check; \
281 float _garbage = GARBAGE_VALUE;
282
283/* Assign garbage value if the light type check fails. */
284# define SAFE_ASSIGN_LIGHT_TYPE_CHECK(_type, _value) \
285 (_validity_check ? (_value) : _type(_garbage))
286#else
287# define SAFE_BEGIN(dst_type, src_type, src_, check) \
288 UNUSED_VARS(check); \
289 src_type _src = src_; \
290 dst_type _dst;
291
292# define SAFE_ASSIGN_LIGHT_TYPE_CHECK(_type, _value) _value
293#endif
294
295#if USE_LIGHT_UNION
296# define DATA_MEMBER local
297#else
298# define DATA_MEMBER do_not_access_directly
299#endif
300
301#define SAFE_READ_BEGIN(dst_type, light, check) \
302 SAFE_BEGIN(dst_type, LightLocalData, light.DATA_MEMBER, check)
303#define SAFE_READ_END() _dst
304
305#define SAFE_WRITE_BEGIN(src_type, src, check) SAFE_BEGIN(LightLocalData, src_type, src, check)
306#define SAFE_WRITE_END(light) light.DATA_MEMBER = _dst;
307
308#define ERROR_OFS(a, b) "Offset of " STRINGIFY(a) " mismatch offset of " STRINGIFY(b)
309
310/* This is a dangerous process, make sure to static assert every assignment. */
311#define SAFE_ASSIGN(a, reinterpret_fn, in_type, b) \
312 CHECK_TYPE_PAIR(_src.b, in_type(_dst.a)); \
313 CHECK_TYPE_PAIR(_dst.a, reinterpret_fn(_src.b)); \
314 _dst.a = reinterpret_fn(SAFE_ASSIGN_LIGHT_TYPE_CHECK(in_type, _src.b)); \
315 BLI_STATIC_ASSERT(offsetof(decltype(_dst), a) == offsetof(decltype(_src), b), ERROR_OFS(a, b))
316
317#define SAFE_ASSIGN_FLOAT(a, b) SAFE_ASSIGN(a, TYPECAST_NOOP, float, b);
318#define SAFE_ASSIGN_FLOAT2(a, b) SAFE_ASSIGN(a, TYPECAST_NOOP, float2, b);
319#define SAFE_ASSIGN_FLOAT3(a, b) SAFE_ASSIGN(a, TYPECAST_NOOP, float3, b);
320#define SAFE_ASSIGN_INT(a, b) SAFE_ASSIGN(a, TYPECAST_NOOP, int, b);
321#define SAFE_ASSIGN_FLOAT_AS_INT(a, b) SAFE_ASSIGN(a, FLOAT_AS_INT, float, b);
322#define SAFE_ASSIGN_INT_AS_FLOAT(a, b) SAFE_ASSIGN(a, INT_AS_FLOAT, int, b);
323
324#if !USE_LIGHT_UNION || !defined(GPU_SHADER)
325
326/* These functions are not meant to be used in C++ code. They are only defined on the C++ side for
327 * static assertions. Hide them. */
328# if !defined(GPU_SHADER)
329namespace do_not_use {
330# endif
331
332static inline LightSpotData light_local_data_get_ex(LightData light, bool check)
333{
334 SAFE_READ_BEGIN(LightSpotData, light, check)
335 SAFE_ASSIGN_FLOAT3(shadow_position, shadow_position)
336 SAFE_ASSIGN_FLOAT(_pad0, _pad0)
337 SAFE_ASSIGN_FLOAT(shadow_radius, shadow_radius)
338 SAFE_ASSIGN_FLOAT(shape_radius, shape_radius)
339 SAFE_ASSIGN_FLOAT(influence_radius_max, influence_radius_max)
340 SAFE_ASSIGN_FLOAT(influence_radius_invsqr_surface, influence_radius_invsqr_surface)
341 SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, influence_radius_invsqr_volume)
342 SAFE_ASSIGN_INT(tilemaps_count, tilemaps_count)
343 SAFE_ASSIGN_FLOAT(spot_mul, _pad2)
344 SAFE_ASSIGN_FLOAT2(spot_size_inv, _pad3)
345 SAFE_ASSIGN_FLOAT(spot_tan, _pad4)
346 SAFE_ASSIGN_FLOAT(spot_bias, _pad5)
347 return SAFE_READ_END();
348}
349
351{
352 SAFE_WRITE_BEGIN(LightSpotData, spot_data, is_local_light(light.type))
353 SAFE_ASSIGN_FLOAT3(shadow_position, shadow_position)
354 SAFE_ASSIGN_FLOAT(_pad0, _pad0)
355 SAFE_ASSIGN_FLOAT(shadow_radius, shadow_radius)
356 SAFE_ASSIGN_FLOAT(shape_radius, shape_radius)
357 SAFE_ASSIGN_FLOAT(influence_radius_max, influence_radius_max)
358 SAFE_ASSIGN_FLOAT(influence_radius_invsqr_surface, influence_radius_invsqr_surface)
359 SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, influence_radius_invsqr_volume)
360 SAFE_ASSIGN_INT(tilemaps_count, tilemaps_count)
361 SAFE_ASSIGN_FLOAT(_pad2, spot_mul)
362 SAFE_ASSIGN_FLOAT2(_pad3, spot_size_inv)
363 SAFE_ASSIGN_FLOAT(_pad4, spot_tan)
364 SAFE_ASSIGN_FLOAT(_pad5, spot_bias)
365 SAFE_WRITE_END(light)
366 return light;
367}
368
370{
371 return light_local_data_get_ex(light, is_local_light(light.type));
372}
373
375{
376 return light_local_data_get_ex(light, is_spot_light(light.type) || is_point_light(light.type));
377}
378
379static inline LightAreaData light_area_data_get(LightData light)
380{
381 SAFE_READ_BEGIN(LightAreaData, light, is_area_light(light.type))
382 SAFE_ASSIGN_FLOAT(shape_radius, shape_radius)
383 SAFE_ASSIGN_FLOAT(influence_radius_max, influence_radius_max)
384 SAFE_ASSIGN_FLOAT(influence_radius_invsqr_surface, influence_radius_invsqr_surface)
385 SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, influence_radius_invsqr_volume)
386 SAFE_ASSIGN_FLOAT3(shadow_position, shadow_position)
387 SAFE_ASSIGN_FLOAT(shadow_radius, shadow_radius)
388 SAFE_ASSIGN_INT(tilemaps_count, tilemaps_count)
390 SAFE_ASSIGN_FLOAT(shadow_scale, _pad4)
391 return SAFE_READ_END();
392}
393
394static inline LightSunData light_sun_data_get(LightData light)
395{
396 SAFE_READ_BEGIN(LightSunData, light, is_sun_light(light.type))
397 SAFE_ASSIGN_FLOAT3(direction, shadow_position)
398 SAFE_ASSIGN_FLOAT(shape_radius, _pad0)
399 SAFE_ASSIGN_FLOAT_AS_INT(clipmap_base_offset_neg.x, shadow_radius)
400 SAFE_ASSIGN_FLOAT_AS_INT(clipmap_base_offset_neg.y, shape_radius)
401 SAFE_ASSIGN_FLOAT_AS_INT(clipmap_base_offset_pos.x, influence_radius_max)
402 SAFE_ASSIGN_FLOAT_AS_INT(clipmap_base_offset_pos.y, influence_radius_invsqr_surface)
403 SAFE_ASSIGN_FLOAT(shadow_angle, influence_radius_invsqr_volume)
404 SAFE_ASSIGN_FLOAT2(clipmap_origin, _pad3)
405 SAFE_ASSIGN_FLOAT_AS_INT(clipmap_lod_min, _pad4)
406 SAFE_ASSIGN_FLOAT_AS_INT(clipmap_lod_max, _pad5)
407 return SAFE_READ_END();
408}
409
410static inline LightData light_sun_data_set(LightData light, LightSunData sun_data)
411{
412 SAFE_WRITE_BEGIN(LightSunData, sun_data, is_sun_light(light.type))
413 SAFE_ASSIGN_FLOAT3(shadow_position, direction)
414 SAFE_ASSIGN_FLOAT(_pad0, shape_radius)
415 SAFE_ASSIGN_INT_AS_FLOAT(shadow_radius, clipmap_base_offset_neg.x)
416 SAFE_ASSIGN_INT_AS_FLOAT(shape_radius, clipmap_base_offset_neg.y)
417 SAFE_ASSIGN_INT_AS_FLOAT(influence_radius_max, clipmap_base_offset_pos.x)
418 SAFE_ASSIGN_INT_AS_FLOAT(influence_radius_invsqr_surface, clipmap_base_offset_pos.y)
419 SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, shadow_angle)
420 SAFE_ASSIGN_FLOAT2(_pad3, clipmap_origin)
421 SAFE_ASSIGN_INT_AS_FLOAT(_pad4, clipmap_lod_min)
422 SAFE_ASSIGN_INT_AS_FLOAT(_pad5, clipmap_lod_max)
423 SAFE_WRITE_END(light)
424 return light;
425}
426
427# if !defined(GPU_SHADER)
428} // namespace do_not_use
429# endif
430
431#endif
432
433#if USE_LIGHT_UNION
434# define light_local_data_get(light) light.local
435# define light_spot_data_get(light) light.spot
436# define light_area_data_get(light) light.area
437# define light_sun_data_get(light) light.sun
438#endif
439
440#undef DATA_MEMBER
441#undef GARBAGE_VALUE
442#undef FLOAT_AS_INT
443#undef TYPECAST_NOOP
444#undef SAFE_BEGIN
445#undef SAFE_ASSIGN_LIGHT_TYPE_CHECK
446#undef ERROR_OFS
447#undef SAFE_ASSIGN
448#undef SAFE_ASSIGN_FLOAT
449#undef SAFE_ASSIGN_FLOAT2
450#undef SAFE_ASSIGN_INT
451#undef SAFE_ASSIGN_FLOAT_AS_INT
452#undef SAFE_ASSIGN_INT_AS_FLOAT
453
454static inline int light_tilemap_max_get(LightData light)
455{
456 /* This is not something we need in performance critical code. */
457 if (is_sun_light(light.type)) {
458 return light.tilemap_index +
459 (light_sun_data_get(light).clipmap_lod_max - light_sun_data_get(light).clipmap_lod_min);
460 }
461 return light.tilemap_index + light_local_data_get(light).tilemaps_count - 1;
462}
463
464/* Return the number of tilemap needed for a local light. */
465static inline int light_local_tilemap_count(LightData light)
466{
467 if (is_spot_light(light.type)) {
468 return (light_spot_data_get(light).spot_tan > tanf(EEVEE_PI / 4.0)) ? 5 : 1;
469 }
470 if (is_area_light(light.type)) {
471 return 5;
472 }
473 return 6;
474}
475
476/* -------------------------------------------------------------------- */
479
480/* Number of items we can cull. Limited by how we store CullingZBin. */
481#define CULLING_MAX_ITEM 65536
482/* Fine grained subdivision in the Z direction. Limited by the LDS in z-binning compute shader. */
483#define CULLING_ZBIN_COUNT 4096
484/* Max tile map resolution per axes. */
485#define CULLING_TILE_RES 16
486
515
516
517
518#ifndef GPU_SHADER
519} // namespace blender::eevee
520#endif
#define BLI_STATIC_ASSERT_ALIGN(st, align)
Definition BLI_assert.h:86
unsigned int uint
int32_t bool32_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
#define EEVEE_PI
#define SAFE_ASSIGN_FLOAT(a, b)
#define SAFE_ASSIGN_INT_AS_FLOAT(a, b)
#define light_area_data_get(light)
#define SAFE_WRITE_END(light)
#define SAFE_ASSIGN_FLOAT3(a, b)
#define SAFE_WRITE_BEGIN(src_type, src, check)
#define light_sun_data_get(light)
#define light_local_data_get(light)
#define light_spot_data_get(light)
#define LOCAL_LIGHT_COMMON
#define SAFE_READ_BEGIN(dst_type, light, check)
#define SAFE_ASSIGN_FLOAT_AS_INT(a, b)
#define SAFE_ASSIGN_FLOAT2(a, b)
#define SAFE_ASSIGN_INT(a, b)
#define SAFE_READ_END()
ccl_device_inline float2 power(const float2 v, const float e)
BLI_STATIC_ASSERT(MBC_BATCH_LEN< 64, "Number of batches exceeded the limit of bit fields")
static LightData light_local_data_set(LightData light, LightSpotData spot_data)
static LightData light_sun_data_set(LightData light, LightSunData sun_data)
static LightSpotData light_local_data_get_ex(LightData light, bool check)
static bool is_sphere_light(eLightType type)
static bool is_area_light(eLightType type)
static int light_local_tilemap_count(LightData light)
static float3 transform_x_axis(Transform t)
static float3 light_position_get(LightData light)
static float3 light_y_axis(LightData light)
static bool is_sun_light(eLightType type)
static int light_tilemap_max_get(LightData light)
static float3 transform_z_axis(Transform t)
static float3 light_z_axis(LightData light)
static bool is_spot_light(eLightType type)
static float3 transform_y_axis(Transform t)
static bool is_oriented_disk_light(eLightType type)
static float3 light_x_axis(LightData light)
static bool is_local_light(eLightType type)
static bool is_point_light(eLightType type)
static float3 transform_location(Transform t)
VecBase< uint32_t, 2 > uint2
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< float, 3 > float3
#define tanf
LOCAL_LIGHT_COMMON float _pad1
LOCAL_LIGHT_COMMON float _pad1