507 lines
17 KiB
C++
507 lines
17 KiB
C++
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup eevee
|
|
*
|
|
* The shadow module manages shadow update tagging & shadow rendering.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "BLI_pool.hh"
|
|
#include "BLI_vector.hh"
|
|
|
|
#include "GPU_batch.h"
|
|
|
|
#include "eevee_camera.hh"
|
|
#include "eevee_material.hh"
|
|
#include "eevee_shader.hh"
|
|
#include "eevee_shader_shared.hh"
|
|
#include "eevee_sync.hh"
|
|
|
|
namespace blender::eevee {
|
|
|
|
class Instance;
|
|
class ShadowModule;
|
|
class ShadowPipeline;
|
|
struct Light;
|
|
|
|
enum eCubeFace {
|
|
/* Ordering by culling order. If cone aperture is shallow, we cull the later view. */
|
|
Z_NEG = 0,
|
|
X_POS,
|
|
X_NEG,
|
|
Y_POS,
|
|
Y_NEG,
|
|
Z_POS,
|
|
};
|
|
|
|
/* To be applied after view matrix. Follow same order as eCubeFace. */
|
|
constexpr static const float shadow_face_mat[6][4][4] = {
|
|
{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}, /* Z_NEG */
|
|
{{0, 0, -1, 0}, {-1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* X_POS */
|
|
{{0, 0, 1, 0}, {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* X_NEG */
|
|
{{1, 0, 0, 0}, {0, 0, -1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* Y_POS */
|
|
{{-1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* Y_NEG */
|
|
{{1, 0, 0, 0}, {0, -1, 0, 0}, {0, 0, -1, 0}, {0, 0, 0, 1}}, /* Z_POS */
|
|
};
|
|
|
|
/* Converts to [-SHADOW_TILEMAP_RES / 2..SHADOW_TILEMAP_RES / 2] for XY and [0..1] for Z. */
|
|
constexpr static const float shadow_clipmap_scale_mat[4][4] = {{SHADOW_TILEMAP_RES / 2, 0, 0, 0},
|
|
{0, SHADOW_TILEMAP_RES / 2, 0, 0},
|
|
{0, 0, 0.5, 0},
|
|
{0, 0, 0.5, 1}};
|
|
|
|
/* Technique used for updating the virtual shadow map contents. */
|
|
enum class ShadowTechnique {
|
|
/* Default virtual shadow map update using large virtual framebuffer to rasterize geometry with
|
|
* per-fragment textureAtomicMin to perform depth-test and indirectly store nearest depth value
|
|
* in the shadow atlas. */
|
|
ATOMIC_RASTER = 0,
|
|
|
|
/* Tile-architecture optimized virtual shadow map update, leveraging on-tile memory for clearing
|
|
* and depth-testing during geometry rasterization to avoid atomic operations, simplify mesh
|
|
* depth shader and only perform a single storage operation per pixel. This technique performs
|
|
* a 3-pass solution, first clearing tiles, updating depth and storing final results. */
|
|
TILE_COPY = 1,
|
|
};
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Tile-Map
|
|
*
|
|
* Stores indirection table and states of each tile of a virtual shadow-map.
|
|
* One tile-map has the effective resolution of `pagesize * tile_map_resolution`.
|
|
* Each tile-map overhead is quite small if they do not have any pages allocated.
|
|
*
|
|
* \{ */
|
|
|
|
struct ShadowTileMap : public ShadowTileMapData {
|
|
static constexpr int64_t tile_map_resolution = SHADOW_TILEMAP_RES;
|
|
static constexpr int64_t tiles_count = tile_map_resolution * tile_map_resolution;
|
|
|
|
/** Level of detail for clipmap. */
|
|
int level = INT_MAX;
|
|
/** Cube face index. */
|
|
eCubeFace cubeface = Z_NEG;
|
|
/** Cached, used for detecting updates. */
|
|
float4x4 object_mat;
|
|
|
|
public:
|
|
ShadowTileMap(int tiles_index_)
|
|
{
|
|
tiles_index = tiles_index_;
|
|
/* For now just the same index. */
|
|
clip_data_index = tiles_index_ / SHADOW_TILEDATA_PER_TILEMAP;
|
|
this->set_dirty();
|
|
}
|
|
|
|
void sync_orthographic(const float4x4 &object_mat_,
|
|
int2 origin_offset,
|
|
int clipmap_level,
|
|
float lod_bias_,
|
|
eShadowProjectionType projection_type_);
|
|
|
|
void sync_cubeface(const float4x4 &object_mat,
|
|
float near,
|
|
float far,
|
|
float side,
|
|
float shift,
|
|
eCubeFace face,
|
|
float lod_bias_);
|
|
|
|
void debug_draw() const;
|
|
|
|
void set_dirty()
|
|
{
|
|
grid_shift = int2(SHADOW_TILEMAP_RES);
|
|
}
|
|
|
|
void set_updated()
|
|
{
|
|
grid_shift = int2(0);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The tile-maps are managed on CPU and associated with each light shadow object.
|
|
*
|
|
* The number of tile-maps & tiles is unbounded (to the limit of SSBOs), but the actual number
|
|
* used for rendering is caped to 4096. This is to simplify tile-maps management on CPU.
|
|
*
|
|
* At sync end, all tile-maps are grouped by light inside the ShadowTileMapDataBuf so that each
|
|
* light has a contiguous range of tile-maps to refer to.
|
|
*/
|
|
struct ShadowTileMapPool {
|
|
public:
|
|
/** Limit the width of the texture. */
|
|
static constexpr int64_t maps_per_row = SHADOW_TILEMAP_PER_ROW;
|
|
|
|
/** Vector containing available offset to tile range in the ShadowTileDataBuf. */
|
|
Vector<uint> free_indices;
|
|
/** Pool containing shadow tile structure on CPU. */
|
|
Pool<ShadowTileMap> tilemap_pool;
|
|
/** Sorted descriptions for each tile-map in the pool. Updated each frame. */
|
|
ShadowTileMapDataBuf tilemaps_data = {"tilemaps_data"};
|
|
/** Previously used tile-maps that needs to release their tiles/pages. Updated each frame. */
|
|
ShadowTileMapDataBuf tilemaps_unused = {"tilemaps_unused"};
|
|
/** All possible tiles. A range of tiles tile is referenced by a tile-map. */
|
|
ShadowTileDataBuf tiles_data = {"tiles_data"};
|
|
/** Clip range for directional shadows. Updated on GPU. Persistent. */
|
|
ShadowTileMapClipBuf tilemaps_clip = {"tilemaps_clip"};
|
|
/** Texture equivalent of ShadowTileDataBuf but grouped by light. */
|
|
Texture tilemap_tx = {"tilemap_tx"};
|
|
/** Number of free tile-maps at the end of the previous sync. */
|
|
int64_t last_free_len = 0;
|
|
|
|
public:
|
|
ShadowTileMapPool();
|
|
|
|
ShadowTileMap *acquire();
|
|
|
|
/**
|
|
* Push the given list of ShadowTileMap onto the free stack. Their pages will be free.
|
|
*/
|
|
void release(Span<ShadowTileMap *> free_list);
|
|
|
|
void end_sync(ShadowModule &module);
|
|
};
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Shadow Casters & Receivers
|
|
*
|
|
* \{ */
|
|
|
|
/* Can be either a shadow caster or a shadow receiver. */
|
|
struct ShadowObject {
|
|
ResourceHandle resource_handle = {0};
|
|
bool used = true;
|
|
};
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name ShadowModule
|
|
*
|
|
* Manages shadow atlas and shadow region data.
|
|
* \{ */
|
|
|
|
class ShadowModule {
|
|
friend ShadowPunctual;
|
|
friend ShadowDirectional;
|
|
friend ShadowPipeline;
|
|
friend ShadowTileMapPool;
|
|
|
|
public:
|
|
/* Shadowing technique. */
|
|
static ShadowTechnique shadow_technique;
|
|
|
|
/** Need to be first because of destructor order. */
|
|
ShadowTileMapPool tilemap_pool;
|
|
|
|
Pool<ShadowPunctual> punctual_pool;
|
|
Pool<ShadowDirectional> directional_pool;
|
|
|
|
private:
|
|
Instance &inst_;
|
|
|
|
ShadowSceneData &data_;
|
|
|
|
/** Map of shadow casters to track deletion & update of intersected shadows. */
|
|
Map<ObjectKey, ShadowObject> objects_;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Tile-map Management
|
|
* \{ */
|
|
|
|
PassSimple tilemap_setup_ps_ = {"TilemapSetup"};
|
|
PassMain tilemap_usage_ps_ = {"TagUsage"};
|
|
PassSimple tilemap_update_ps_ = {"TilemapUpdate"};
|
|
|
|
PassMain::Sub *tilemap_usage_transparent_ps_ = nullptr;
|
|
GPUBatch *box_batch_ = nullptr;
|
|
|
|
Framebuffer usage_tag_fb;
|
|
|
|
/** List of Resource IDs (to get bounds) for tagging passes. */
|
|
StorageVectorBuffer<uint, 128> past_casters_updated_ = {"PastCastersUpdated"};
|
|
StorageVectorBuffer<uint, 128> curr_casters_updated_ = {"CurrCastersUpdated"};
|
|
/** List of Resource IDs (to get bounds) for getting minimum clip-maps bounds. */
|
|
StorageVectorBuffer<uint, 128> curr_casters_ = {"CurrCasters"};
|
|
|
|
/** Indirect arguments for page clearing. */
|
|
DispatchIndirectBuf clear_dispatch_buf_ = {"clear_dispatch_buf"};
|
|
/** Indirect arguments for TBDR Tile Page passes. */
|
|
DrawIndirectBuf tile_draw_buf_ = {"tile_draw_buf"};
|
|
/** A compact stream of rendered tile coordinates in the shadow atlas. */
|
|
StorageArrayBuffer<uint, SHADOW_RENDER_MAP_SIZE, true> dst_coord_buf_ = {"dst_coord_buf"};
|
|
/** A compact stream of rendered tile coordinates in the framebuffer. */
|
|
StorageArrayBuffer<uint, SHADOW_RENDER_MAP_SIZE, true> src_coord_buf_ = {"src_coord_buf"};
|
|
/** Same as dst_coord_buf_ but is not compact. More like a linear texture. */
|
|
StorageArrayBuffer<uint, SHADOW_RENDER_MAP_SIZE, true> render_map_buf_ = {"render_map_buf"};
|
|
/** View to viewport index mapping. */
|
|
StorageArrayBuffer<uint, SHADOW_VIEW_MAX, true> viewport_index_buf_ = {"viewport_index_buf"};
|
|
|
|
int3 dispatch_depth_scan_size_;
|
|
/* Ratio between tile-map pixel world "radius" and film pixel world "radius". */
|
|
float tilemap_projection_ratio_;
|
|
float pixel_world_radius_;
|
|
int2 usage_tag_fb_resolution_;
|
|
int usage_tag_fb_lod_ = 5;
|
|
|
|
/* Statistics that are read back to CPU after a few frame (to avoid stall). */
|
|
SwapChain<ShadowStatisticsBuf, 5> statistics_buf_;
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Page Management
|
|
* \{ */
|
|
|
|
static constexpr eGPUTextureFormat atlas_type = GPU_R32UI;
|
|
/** Atlas containing all physical pages. */
|
|
Texture atlas_tx_ = {"shadow_atlas_tx_"};
|
|
|
|
/** Pool of unallocated pages waiting to be assigned to specific tiles in the tile-map atlas. */
|
|
ShadowPageHeapBuf pages_free_data_ = {"PagesFreeBuf"};
|
|
/** Pool of cached tiles waiting to be reused. */
|
|
ShadowPageCacheBuf pages_cached_data_ = {"PagesCachedBuf"};
|
|
/** Infos for book keeping and debug. */
|
|
ShadowPagesInfoDataBuf pages_infos_data_ = {"PagesInfosBuf"};
|
|
|
|
int3 copy_dispatch_size_;
|
|
int3 scan_dispatch_size_;
|
|
int rendering_tilemap_;
|
|
int rendering_lod_;
|
|
bool do_full_update = true;
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Rendering
|
|
* \{ */
|
|
|
|
/** Multi-View containing a maximum of 64 view to be rendered with the shadow pipeline. */
|
|
View shadow_multi_view_ = {"ShadowMultiView", SHADOW_VIEW_MAX, true};
|
|
/** Framebuffer with the atlas_tx attached. */
|
|
Framebuffer render_fb_ = {"shadow_write_framebuffer"};
|
|
|
|
/* NOTE(Metal): Metal requires memoryless textures to be created which represent attachments in
|
|
* the shadow write framebuffer. These textures do not occupy any physical memory, but require a
|
|
* Texture object containing its parameters.*/
|
|
Texture shadow_depth_fb_tx_ = {"shadow_depth_fb_tx_"};
|
|
Texture shadow_depth_accum_tx_ = {"shadow_depth_accum_tx_"};
|
|
|
|
/** Arrays of viewports to rendering each tile to. */
|
|
std::array<int4, 16> multi_viewports_;
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Debugging
|
|
* \{ */
|
|
|
|
/** Display information about the virtual shadows. */
|
|
PassSimple debug_draw_ps_ = {"Shadow.Debug"};
|
|
|
|
/** \} */
|
|
|
|
/** Scene immutable parameters. */
|
|
|
|
/** For now, needs to be hardcoded. */
|
|
int shadow_page_size_ = SHADOW_PAGE_RES;
|
|
/** Amount of bias to apply to the LOD computed at the tile usage tagging stage. */
|
|
float lod_bias_ = 0.0f;
|
|
/** Maximum number of allocated pages. Maximum value is SHADOW_MAX_TILEMAP. */
|
|
int shadow_page_len_ = SHADOW_MAX_TILEMAP;
|
|
/** Global switch. */
|
|
bool enabled_ = true;
|
|
|
|
public:
|
|
ShadowModule(Instance &inst, ShadowSceneData &data);
|
|
~ShadowModule(){};
|
|
|
|
void init();
|
|
|
|
void begin_sync();
|
|
/** Register a shadow caster or receiver. */
|
|
void sync_object(const ObjectHandle &handle,
|
|
const ResourceHandle &resource_handle,
|
|
bool is_shadow_caster,
|
|
bool is_alpha_blend);
|
|
void end_sync();
|
|
|
|
void set_lights_data();
|
|
|
|
void set_view(View &view);
|
|
|
|
void debug_end_sync();
|
|
void debug_draw(View &view, GPUFrameBuffer *view_fb);
|
|
|
|
template<typename PassType> void bind_resources(PassType &pass)
|
|
{
|
|
pass.bind_texture(SHADOW_ATLAS_TEX_SLOT, &atlas_tx_);
|
|
pass.bind_texture(SHADOW_TILEMAPS_TEX_SLOT, &tilemap_pool.tilemap_tx);
|
|
}
|
|
|
|
private:
|
|
void remove_unused();
|
|
void debug_page_map_call(DRWPass *pass);
|
|
|
|
/** Compute approximate screen pixel space radius. */
|
|
float screen_pixel_radius(const View &view, const int2 &extent);
|
|
/** Compute approximate punctual shadow pixel world space radius, 1 unit away of the light. */
|
|
float tilemap_pixel_radius();
|
|
};
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Shadow
|
|
*
|
|
* A shadow component is associated to a `eevee::Light` and manages its associated Tile-maps.
|
|
* \{ */
|
|
|
|
class ShadowPunctual : public NonCopyable, NonMovable {
|
|
private:
|
|
ShadowModule &shadows_;
|
|
/** Tile-map for each cube-face needed (in eCubeFace order). */
|
|
Vector<ShadowTileMap *> tilemaps_;
|
|
/** Area light size. */
|
|
float size_x_, size_y_;
|
|
/** Shape type. */
|
|
eLightType light_type_;
|
|
/** Light position. */
|
|
float3 position_;
|
|
/** Used to compute near and far clip distances. */
|
|
float max_distance_, light_radius_;
|
|
/** Number of tile-maps needed to cover the light angular extents. */
|
|
int tilemaps_needed_;
|
|
/** Scaling factor to the light shape for shadow ray casting. */
|
|
float softness_factor_;
|
|
|
|
public:
|
|
ShadowPunctual(ShadowModule &module) : shadows_(module){};
|
|
ShadowPunctual(ShadowPunctual &&other)
|
|
: shadows_(other.shadows_), tilemaps_(std::move(other.tilemaps_)){};
|
|
|
|
~ShadowPunctual()
|
|
{
|
|
shadows_.tilemap_pool.release(tilemaps_);
|
|
}
|
|
|
|
/**
|
|
* Sync shadow parameters but do not allocate any shadow tile-maps.
|
|
*/
|
|
void sync(eLightType light_type,
|
|
const float4x4 &object_mat,
|
|
float cone_aperture,
|
|
float light_shape_radius,
|
|
float max_distance,
|
|
float softness_factor);
|
|
|
|
/**
|
|
* Release the tile-maps that will not be used in the current frame.
|
|
*/
|
|
void release_excess_tilemaps();
|
|
|
|
/**
|
|
* Allocate shadow tile-maps and setup views for rendering.
|
|
*/
|
|
void end_sync(Light &light, float lod_bias);
|
|
|
|
private:
|
|
/**
|
|
* Compute the projection matrix inputs.
|
|
* Make sure that the projection encompass all possible rays that can start in the projection
|
|
* quadrant.
|
|
*/
|
|
void compute_projection_boundaries(float light_radius,
|
|
float shadow_radius,
|
|
float max_lit_distance,
|
|
float &near,
|
|
float &far,
|
|
float &side);
|
|
};
|
|
|
|
class ShadowDirectional : public NonCopyable, NonMovable {
|
|
private:
|
|
ShadowModule &shadows_;
|
|
/** Tile-map for each clip-map level. */
|
|
Vector<ShadowTileMap *> tilemaps_;
|
|
/** User minimum resolution. */
|
|
float min_resolution_;
|
|
/** Copy of object matrix. Normalized. */
|
|
float4x4 object_mat_;
|
|
/** Current range of clip-map / cascades levels covered by this shadow. */
|
|
IndexRange levels_range;
|
|
/** Radius of the shadowed light shape. Might be scaled compared to the shading disk. */
|
|
float disk_shape_angle_;
|
|
/** Maximum distance a shadow map ray can be travel. */
|
|
float trace_distance_;
|
|
|
|
public:
|
|
ShadowDirectional(ShadowModule &module) : shadows_(module){};
|
|
ShadowDirectional(ShadowDirectional &&other)
|
|
: shadows_(other.shadows_), tilemaps_(std::move(other.tilemaps_)){};
|
|
|
|
~ShadowDirectional()
|
|
{
|
|
shadows_.tilemap_pool.release(tilemaps_);
|
|
}
|
|
|
|
/**
|
|
* Sync shadow parameters but do not allocate any shadow tile-maps.
|
|
*/
|
|
void sync(const float4x4 &object_mat,
|
|
float min_resolution,
|
|
float shadow_disk_angle,
|
|
float trace_distance);
|
|
|
|
/**
|
|
* Release the tile-maps that will not be used in the current frame.
|
|
*/
|
|
void release_excess_tilemaps(const Camera &camera, float lod_bias);
|
|
|
|
/**
|
|
* Allocate shadow tile-maps and setup views for rendering.
|
|
*/
|
|
void end_sync(Light &light, const Camera &camera, float lod_bias);
|
|
|
|
/* Return coverage of the whole tile-map in world unit. */
|
|
static float coverage_get(int lvl)
|
|
{
|
|
/* This function should be kept in sync with shadow_directional_level(). */
|
|
/* \note: If we would to introduce a global scaling option it would be here. */
|
|
return exp2(lvl);
|
|
}
|
|
|
|
/* Return coverage of a single tile for a tile-map of this LOD in world unit. */
|
|
static float tile_size_get(int lvl)
|
|
{
|
|
return coverage_get(lvl) / SHADOW_TILEMAP_RES;
|
|
}
|
|
|
|
private:
|
|
IndexRange clipmap_level_range(const Camera &camera);
|
|
IndexRange cascade_level_range(const Camera &camera, float lod_bias);
|
|
|
|
void cascade_tilemaps_distribution(Light &light, const Camera &camera);
|
|
void clipmap_tilemaps_distribution(Light &light, const Camera &camera, float lod_bias);
|
|
|
|
void cascade_tilemaps_distribution_near_far_points(const Camera &camera,
|
|
float3 &near_point,
|
|
float3 &far_point);
|
|
|
|
/* Choose between clip-map and cascade distribution of shadow-map precision depending on the
|
|
* camera projection type and bounds. */
|
|
static eShadowProjectionType directional_distribution_type_get(const Camera &camera);
|
|
};
|
|
|
|
/** \} */
|
|
|
|
} // namespace blender::eevee
|