WIP: eevee-next-world-irradiance #108304

Closed
Jeroen Bakker wants to merge 79 commits from Jeroen-Bakker:eevee-next-world-irradiance into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
24 changed files with 684 additions and 63 deletions
Showing only changes of commit f14c7e804c - Show all commits

View File

@ -465,7 +465,9 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_light_eval_lib.glsl
engines/eevee_next/shaders/eevee_light_iter_lib.glsl
engines/eevee_next/shaders/eevee_light_lib.glsl
engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl
engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl
engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl
engines/eevee_next/shaders/eevee_lightprobe_lib.glsl
engines/eevee_next/shaders/eevee_ltc_lib.glsl
engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl

View File

@ -71,6 +71,10 @@
#define MOTION_BLUR_GROUP_SIZE 32
#define MOTION_BLUR_DILATE_GROUP_SIZE 512
/* Irradiance Cache. */
/** Maximum number of entities inside the cache. */
#define IRRADIANCE_GRID_MAX 64
/* Depth Of Field. */
#define DOF_TILES_SIZE 8
#define DOF_TILES_FLATTEN_GROUP_SIZE DOF_TILES_SIZE
@ -89,17 +93,20 @@
/* IrradianceBake. */
#define SURFEL_GROUP_SIZE 256
#define SURFEL_LIST_GROUP_SIZE 256
#define IRRADIANCE_GRID_GROUP_SIZE 4 /* In each dimension, do 4x4x4 workgroup size. */
#define IRRADIANCE_GRID_GROUP_SIZE 4 /* In each dimension, so 4x4x4 workgroup size. */
#define IRRADIANCE_GRID_BRICK_SIZE 4 /* In each dimension, so 4x4x4 brick size. */
/* Resource bindings. */
/* Texture. */
#define IRRADIANCE_ATLAS_TEX_SLOT 11
#define SHADOW_TILEMAPS_TEX_SLOT 12
/* Only during surface shading. */
#define SHADOW_ATLAS_TEX_SLOT 13
/* Only during shadow rendering. */
#define SHADOW_RENDER_MAP_SLOT 13
#define RBUFS_UTILITY_TEX_SLOT 14
#define HIZ_TEX_SLOT 15
/* Images. */
#define RBUFS_NORMAL_SLOT 0
@ -115,6 +122,8 @@
#define GBUF_COLOR_SLOT RBUFS_DIFF_COLOR_SLOT
/* Uniform Buffers. */
#define IRRADIANCE_GRID_BUF_SLOT 3
#define HIZ_BUF_SLOT 5
/* Only during pre-pass. */
#define VELOCITY_CAMERA_PREV_BUF 3
#define VELOCITY_CAMERA_CURR_BUF 4
@ -127,6 +136,7 @@
#define LIGHT_BUF_SLOT 1
#define LIGHT_ZBIN_BUF_SLOT 2
#define LIGHT_TILE_BUF_SLOT 3
#define IRRADIANCE_BRICK_BUF_SLOT 4
/* Only during surface capture. */
#define SURFEL_BUF_SLOT 4
/* Only during surface capture. */

View File

@ -474,7 +474,6 @@ void Instance::light_bake_irradiance(Object &probe,
};
custom_pipeline_wrapper([&]() {
GPU_debug_capture_begin();
irradiance_cache.bake.surfel_raster_views_sync(probe);
/* TODO: lightprobe visibility group option. */
manager->begin_sync();
@ -483,7 +482,6 @@ void Instance::light_bake_irradiance(Object &probe,
irradiance_cache.bake.surfels_create(probe);
irradiance_cache.bake.surfels_lights_eval();
GPU_debug_capture_end();
});
int bounce_len = scene->eevee.gi_diffuse_bounces;

View File

@ -22,7 +22,44 @@ namespace blender::eevee {
void IrradianceCache::init()
{
display_grids_enabled_ = DRW_state_draw_support() &&
inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE;
(inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE);
/* TODO option. */
int atlas_byte_size = 1024 * 1024 * 16;
/* This might become an option in the future. */
bool use_l2_band = false;
int sh_coef_len = use_l2_band ? 9 : 4;
int texel_byte_size = 8; /* Assumes GPU_RGBA16F. */
int3 atlas_extent(IRRADIANCE_GRID_BRICK_SIZE);
atlas_extent.z *= sh_coef_len;
int atlas_col_count = 256;
atlas_extent.x *= atlas_col_count;
/* Determine the row count depending on the scene settings. */
int row_byte_size = atlas_extent.x * atlas_extent.y * atlas_extent.z * texel_byte_size;
int atlas_row_count = divide_ceil_u(atlas_byte_size, row_byte_size);
atlas_extent.y *= atlas_row_count;
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_WRITE | GPU_TEXTURE_USAGE_SHADER_READ;
do_full_update_ = irradiance_atlas_tx_.ensure_3d(GPU_RGBA16F, atlas_extent, usage);
if (do_full_update_) {
/* Delete all references to existing bricks. */
for (IrradianceGrid &grid : inst_.light_probes.grid_map_.values()) {
grid.bricks.clear();
}
brick_pool_.clear();
/* Fill with all the available bricks. */
for (auto i : IndexRange(atlas_row_count * atlas_col_count)) {
IrradianceBrick brick;
brick.atlas_coord = uint2(i % atlas_col_count, i / atlas_col_count) *
IRRADIANCE_GRID_BRICK_SIZE;
brick_pool_.append(irradiance_brick_pack(brick));
}
}
if (irradiance_atlas_tx_.is_valid() == false) {
inst_.info = "Irradiance Atlas texture could not be created";
}
}
void IrradianceCache::sync()
@ -32,6 +69,165 @@ void IrradianceCache::sync()
}
}
Vector<IrradianceBrickPacked> IrradianceCache::bricks_alloc(int brick_len)
{
if (brick_pool_.size() < brick_len) {
/* Fail allocation. Not enough brick in the atlas. */
return {};
}
Vector<IrradianceBrickPacked> allocated;
allocated.resize(brick_len);
/* Copy bricks to return vector. */
allocated.as_mutable_span().copy_from(brick_pool_.as_span().take_back(brick_len));
/* Remove bricks from the pool. */
brick_pool_.resize(brick_pool_.size() - brick_len);
return allocated;
}
void IrradianceCache::bricks_free(Vector<IrradianceBrickPacked> &bricks)
{
brick_pool_.extend(bricks.as_span());
bricks.clear();
}
void IrradianceCache::set_view(View & /*view*/)
{
Vector<IrradianceGrid *> grid_updates;
/* First allocate the needed bricks and populate the brick buffer. */
int grids_len = 0;
bricks_infos_buf_.clear();
for (IrradianceGrid &grid : inst_.light_probes.grid_map_.values()) {
LightProbeGridCacheFrame *cache = grid.cache ? grid.cache->grid_static_cache : nullptr;
if (cache == nullptr) {
continue;
}
if (cache->baking.L0 == nullptr && cache->irradiance.L0 == nullptr) {
/* No data. */
continue;
}
int3 grid_size = int3(cache->size);
if (grid_size.x <= 0 || grid_size.y <= 0 || grid_size.z <= 0) {
inst_.info = "Error: Malformed irradiance grid data";
continue;
}
/* TODO frustum cull and only load visible grids. */
if (grids_len >= IRRADIANCE_GRID_MAX) {
inst_.info = "Error: Too many grid visible";
continue;
}
if (grid.bricks.is_empty()) {
int3 grid_size_in_bricks = math::divide_ceil(grid_size,
int3(IRRADIANCE_GRID_BRICK_SIZE - 1));
int brick_len = grid_size_in_bricks.x * grid_size_in_bricks.y * grid_size_in_bricks.z;
grid.bricks = bricks_alloc(brick_len);
if (grid.bricks.is_empty()) {
inst_.info = "Error: Irradiance grid allocation failed";
continue;
}
grid_updates.append(&grid);
}
grid.brick_offset = bricks_infos_buf_.size();
bricks_infos_buf_.extend(grid.bricks);
if (grid_size.x <= 0 || grid_size.y <= 0 || grid_size.z <= 0) {
inst_.info = "Error: Malformed irradiance grid data";
continue;
}
float4x4 grid_to_world = grid.object_to_world * math::from_location<float4x4>(float3(-1.0f)) *
math::from_scale<float4x4>(float3(2.0f / float3(grid_size))) *
math::from_location<float4x4>(float3(0.0f));
grid.world_to_grid_transposed = float3x4(math::transpose(math::invert(grid_to_world)));
grid.grid_size = grid_size;
grid.grid_index = grids_len;
grids_infos_buf_[grids_len++] = grid;
}
/* TODO(fclem): Insert world grid here. */
if (grids_len < IRRADIANCE_GRID_MAX) {
/* Tag last grid as invalid to stop the iteration. */
grids_infos_buf_[grids_len].grid_size = int3(-1);
}
bricks_infos_buf_.push_update();
grids_infos_buf_.push_update();
/* Upload data for each grid that need to be inserted in the atlas. */
for (IrradianceGrid *grid : grid_updates) {
LightProbeGridCacheFrame *cache = grid->cache->grid_static_cache;
/* Staging textures are recreated for each light grid to avoid increasing VRAM usage. */
draw::Texture irradiance_a_tx = {"irradiance_a_tx"};
draw::Texture irradiance_b_tx = {"irradiance_b_tx"};
draw::Texture irradiance_c_tx = {"irradiance_c_tx"};
draw::Texture irradiance_d_tx = {"irradiance_d_tx"};
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ;
int3 grid_size = int3(cache->size);
if (cache->baking.L0) {
irradiance_a_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L0);
irradiance_b_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_a);
irradiance_c_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_b);
irradiance_d_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_c);
}
else if (cache->irradiance.L0) {
irradiance_a_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L0);
irradiance_b_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_a);
irradiance_c_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_b);
irradiance_d_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_c);
}
else {
continue;
}
if (irradiance_a_tx.is_valid() == false) {
inst_.info = "Error: Could not allocate irradiance staging texture";
/* Avoid undefined behavior with uninitialized values. */
irradiance_a_tx.clear(float4(0.0f));
irradiance_b_tx.clear(float4(0.0f));
irradiance_c_tx.clear(float4(0.0f));
irradiance_d_tx.clear(float4(0.0f));
}
grid_upload_ps_.init();
grid_upload_ps_.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_LOAD));
grid_upload_ps_.push_constant("grid_index", grid->grid_index);
grid_upload_ps_.bind_ubo("grids_infos_buf", &grids_infos_buf_);
grid_upload_ps_.bind_ssbo("bricks_infos_buf", &bricks_infos_buf_);
grid_upload_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx);
grid_upload_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx);
grid_upload_ps_.bind_texture("irradiance_c_tx", &irradiance_c_tx);
grid_upload_ps_.bind_texture("irradiance_d_tx", &irradiance_d_tx);
grid_upload_ps_.bind_image("irradiance_atlas_img", &irradiance_atlas_tx_);
/* Note that we take into account the padding border of each brick. */
int3 grid_size_in_bricks = math::divide_ceil(grid_size, int3(IRRADIANCE_GRID_BRICK_SIZE - 1));
grid_upload_ps_.dispatch(grid_size_in_bricks);
inst_.manager->submit(grid_upload_ps_);
irradiance_a_tx.free();
irradiance_b_tx.free();
irradiance_c_tx.free();
irradiance_d_tx.free();
}
do_full_update_ = false;
}
void IrradianceCache::viewport_draw(View &view, GPUFrameBuffer *view_fb)
{
if (!inst_.is_baking()) {
@ -105,24 +301,24 @@ void IrradianceCache::display_pass_draw(View &view, GPUFrameBuffer *view_fb)
}
/* Display texture. Updated for each individual light grid to avoid increasing VRAM usage. */
draw::Texture irradiance_a_tx_ = {"irradiance_a_tx"};
draw::Texture irradiance_b_tx_ = {"irradiance_b_tx"};
draw::Texture irradiance_c_tx_ = {"irradiance_c_tx"};
draw::Texture irradiance_d_tx_ = {"irradiance_d_tx"};
draw::Texture irradiance_a_tx = {"irradiance_a_tx"};
draw::Texture irradiance_b_tx = {"irradiance_b_tx"};
draw::Texture irradiance_c_tx = {"irradiance_c_tx"};
draw::Texture irradiance_d_tx = {"irradiance_d_tx"};
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ;
int3 grid_size = int3(cache->size);
if (cache->baking.L0) {
irradiance_a_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L0);
irradiance_b_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_a);
irradiance_c_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_b);
irradiance_d_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_c);
irradiance_a_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L0);
irradiance_b_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_a);
irradiance_c_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_b);
irradiance_d_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_c);
}
else if (cache->irradiance.L0) {
irradiance_a_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L0);
irradiance_b_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_a);
irradiance_c_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_b);
irradiance_d_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_c);
irradiance_a_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L0);
irradiance_b_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_a);
irradiance_c_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_b);
irradiance_d_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_c);
}
else {
continue;
@ -139,10 +335,10 @@ void IrradianceCache::display_pass_draw(View &view, GPUFrameBuffer *view_fb)
display_grids_ps_.push_constant("grid_to_world", grid.object_to_world);
display_grids_ps_.push_constant("world_to_grid", grid.world_to_object);
display_grids_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx_);
display_grids_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx_);
display_grids_ps_.bind_texture("irradiance_c_tx", &irradiance_c_tx_);
display_grids_ps_.bind_texture("irradiance_d_tx", &irradiance_d_tx_);
display_grids_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx);
display_grids_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx);
display_grids_ps_.bind_texture("irradiance_c_tx", &irradiance_c_tx);
display_grids_ps_.bind_texture("irradiance_d_tx", &irradiance_d_tx);
int sample_count = static_cast<int>(BKE_lightprobe_grid_cache_frame_sample_count(cache));
int triangle_count = sample_count * 2;
@ -150,10 +346,10 @@ void IrradianceCache::display_pass_draw(View &view, GPUFrameBuffer *view_fb)
inst_.manager->submit(display_grids_ps_, view);
irradiance_a_tx_.free();
irradiance_b_tx_.free();
irradiance_c_tx_.free();
irradiance_d_tx_.free();
irradiance_a_tx.free();
irradiance_b_tx.free();
irradiance_c_tx.free();
irradiance_d_tx.free();
}
}

View File

@ -128,6 +128,18 @@ class IrradianceCache {
private:
Instance &inst_;
/** Atlas 3D texture containing all loaded grid data. */
Texture irradiance_atlas_tx_ = {"irradiance_atlas_tx_"};
/** Data structure used to index irradiance cache pages inside the atlas. */
IrradianceGridDataBuf grids_infos_buf_ = {"grids_infos_buf_"};
IrradianceBrickBuf bricks_infos_buf_ = {"bricks_infos_buf_"};
/** Pool of atlas regions to allocate to different grids. */
Vector<IrradianceBrickPacked> brick_pool_;
/** Stream data into the irradiance atlas texture. */
PassSimple grid_upload_ps_ = {"IrradianceCache.Upload"};
/** If true, will trigger the reupload of all grid data instead of just streaming new ones. */
bool do_full_update_ = true;
/** Display surfel debug data. */
PassSimple debug_surfels_ps_ = {"IrradianceCache.Debug"};
/** Debug surfel elements copied from the light cache. */
@ -143,8 +155,19 @@ class IrradianceCache {
void init();
void sync();
void set_view(View &view);
void viewport_draw(View &view, GPUFrameBuffer *view_fb);
Vector<IrradianceBrickPacked> bricks_alloc(int brick_len);
void bricks_free(Vector<IrradianceBrickPacked> &bricks);
template<typename T> void bind_resources(draw::detail::PassBase<T> *pass)
{
pass->bind_ubo(IRRADIANCE_GRID_BUF_SLOT, &grids_infos_buf_);
pass->bind_ssbo(IRRADIANCE_BRICK_BUF_SLOT, &bricks_infos_buf_);
pass->bind_texture(IRRADIANCE_ATLAS_TEX_SLOT, &irradiance_atlas_tx_);
}
private:
void debug_pass_draw(View &view, GPUFrameBuffer *view_fb);
void display_pass_draw(View &view, GPUFrameBuffer *view_fb);

View File

@ -13,6 +13,8 @@
#include "eevee_instance.hh"
#include "eevee_lightprobe.hh"
#include "draw_debug.hh"
namespace blender::eevee {
void LightProbeModule::begin_sync()
@ -31,7 +33,10 @@ void LightProbeModule::sync_grid(const Object *ob, ObjectHandle &handle)
grid.object_to_world = float4x4(ob->object_to_world);
grid.world_to_object = float4x4(
math::normalize(math::transpose(float3x3(grid.object_to_world))));
grid.cache = ob->lightprobe_cache;
/* Force reupload. */
inst_.irradiance_cache.bricks_free(grid.bricks);
}
}
@ -71,9 +76,11 @@ void LightProbeModule::end_sync()
for (auto it = grid_map_.items().begin(); it != it_end; ++it) {
IrradianceGrid &grid = (*it).value;
if (grid.updated) {
grid.updated = false;
grid_update_ = true;
}
if (!grid.used) {
inst_.irradiance_cache.bricks_free(grid.bricks);
grid_map_.remove(it);
grid_update_ = true;
continue;
@ -89,6 +96,7 @@ void LightProbeModule::end_sync()
for (auto it = cube_map_.items().begin(); it != it_end; ++it) {
ReflectionCube &cube = (*it).value;
if (cube.updated) {
cube.updated = false;
cube_update_ = true;
}
if (!cube.used) {
@ -101,6 +109,7 @@ void LightProbeModule::end_sync()
}
}
#if 0 /* TODO make this work with new per object light cache. */
/* If light-cache auto-update is enable we tag the relevant part
* of the cache to update and fire up a baking job. */
if (auto_bake_enabled_ && (grid_update_ || cube_update_)) {
@ -123,6 +132,7 @@ void LightProbeModule::end_sync()
WM_event_add_notifier(DRW_context_state_get()->evil_C, NC_LIGHTPROBE, original_scene);
}
}
#endif
}
} // namespace blender::eevee

View File

@ -24,7 +24,7 @@ struct LightProbe {
bool updated = false;
};
struct IrradianceGrid : public LightProbe {
struct IrradianceGrid : public LightProbe, IrradianceGridData {
/** Copy of the transform matrix. */
float4x4 object_to_world;
/** Precomputed inverse transform with normalized axes. No position. Used for rotating SH. */
@ -36,6 +36,10 @@ struct IrradianceGrid : public LightProbe {
* pruning have been done.
*/
const struct LightProbeObjectCache *cache = nullptr;
/** List of associated atlas bricks that are used by this grid. */
Vector<IrradianceBrickPacked> bricks;
/** Index of the grid inside the grid UBO. */
int grid_index;
};
struct ReflectionCube : public LightProbe {};

View File

@ -263,6 +263,7 @@ void ForwardPipeline::render(View &view,
// }
inst_.shadows.set_view(view);
inst_.irradiance_cache.set_view(view);
GPU_framebuffer_bind(combined_fb);
inst_.manager->submit(opaque_ps_, view);
@ -385,6 +386,7 @@ void DeferredLayer::end_sync()
inst_.shadows.bind_resources(&eval_light_ps_);
inst_.sampling.bind_resources(&eval_light_ps_);
inst_.hiz_buffer.bind_resources(&eval_light_ps_);
inst_.irradiance_cache.bind_resources(&eval_light_ps_);
eval_light_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS);
eval_light_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
@ -425,6 +427,7 @@ void DeferredLayer::render(View &view,
inst_.hiz_buffer.set_dirty();
inst_.shadows.set_view(view);
inst_.irradiance_cache.set_view(view);
inst_.gbuffer.acquire(extent, closure_bits_);

View File

@ -150,6 +150,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_light_culling_zbin";
case LIGHTPROBE_IRRADIANCE_RAY:
return "eevee_lightprobe_irradiance_ray";
case LIGHTPROBE_IRRADIANCE_LOAD:
return "eevee_lightprobe_irradiance_load";
case SHADOW_CLIPMAP_CLEAR:
return "eevee_shadow_clipmap_clear";
case SHADOW_DEBUG:

View File

@ -64,6 +64,7 @@ enum eShaderType {
LIGHT_CULLING_ZBIN,
LIGHTPROBE_IRRADIANCE_RAY,
LIGHTPROBE_IRRADIANCE_LOAD,
MOTION_BLUR_GATHER,
MOTION_BLUR_TILE_DILATE,

View File

@ -892,6 +892,37 @@ struct SurfelListInfoData {
};
BLI_STATIC_ASSERT_ALIGN(SurfelListInfoData, 16)
struct IrradianceGridData {
/** World to non-normalized local grid space [0..size-1]. Stored transposed for compactness. */
float3x4 world_to_grid_transposed;
/** Number of bricks for this grid. */
int3 grid_size;
/** Index in brick descriptor list of the first brick of this grid. */
int brick_offset;
};
BLI_STATIC_ASSERT_ALIGN(IrradianceGridData, 16)
struct IrradianceBrick {
/* Offset in pixel to the start of the data inside the atlas texture. */
uint2 atlas_coord;
};
/** \note Stored packed as a uint. */
#define IrradianceBrickPacked uint
static inline IrradianceBrickPacked irradiance_brick_pack(IrradianceBrick brick)
{
uint2 data = (uint2(brick.atlas_coord) & 0xFFFFu) << uint2(0u, 16u);
IrradianceBrickPacked brick_packed = data.x | data.y;
return brick_packed;
}
static inline IrradianceBrick irradiance_brick_unpack(IrradianceBrickPacked brick_packed)
{
IrradianceBrick brick;
brick.atlas_coord = (uint2(brick_packed) >> uint2(0u, 16u)) & uint2(0xFFFFu);
return brick;
}
/** \} */
/* -------------------------------------------------------------------- */
@ -1002,6 +1033,8 @@ using DepthOfFieldScatterListBuf = draw::StorageArrayBuffer<ScatterRect, 16, tru
using DrawIndirectBuf = draw::StorageBuffer<DrawCommand, true>;
using FilmDataBuf = draw::UniformBuffer<FilmData>;
using HiZDataBuf = draw::UniformBuffer<HiZData>;
using IrradianceGridDataBuf = draw::UniformArrayBuffer<IrradianceGridData, IRRADIANCE_GRID_MAX>;
using IrradianceBrickBuf = draw::StorageVectorBuffer<IrradianceBrickPacked, 16>;
using LightCullingDataBuf = draw::StorageBuffer<LightCullingData>;
using LightCullingKeyBuf = draw::StorageArrayBuffer<uint, LIGHT_CHUNK, true>;
using LightCullingTileBuf = draw::StorageArrayBuffer<uint, LIGHT_CHUNK, true>;

View File

@ -9,6 +9,7 @@
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl)
void main()
{
@ -52,6 +53,8 @@ void main()
vec3 diffuse_light = vec3(0.0);
vec3 reflection_light = vec3(0.0);
lightprobe_eval(diffuse_data, reflection_data, P, Ng, V, diffuse_light, reflection_light);
light_eval(
diffuse_data, reflection_data, P, Ng, V, vP_z, thickness, diffuse_light, reflection_light);

View File

@ -22,6 +22,6 @@ void main()
vec3 N = normal_view_to_world(vN);
vec3 lN = transform_direction(world_to_grid, N);
vec4 irradiance = spherical_harmonics_evaluate_lambert(lN, sh);
out_color = vec4(irradiance.rgb, 0.0);
vec3 irradiance = spherical_harmonics_evaluate_lambert(lN, sh);
out_color = vec4(irradiance, 0.0);
}

View File

@ -0,0 +1,80 @@
/**
* The resources expected to be defined are:
* - grids_infos_buf
* - bricks_infos_buf
* - irradiance_atlas_tx
*/
#pragma BLENDER_REQUIRE(eevee_lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
/**
* Return sample coordinates of the first SH coef in unormalized texture space.
*/
vec3 lightprobe_irradiance_grid_atlas_coord(IrradianceGridData grid_data, vec3 lP)
{
ivec3 brick_coord = ivec3((lP - 0.5) / float(IRRADIANCE_GRID_BRICK_SIZE - 1));
/* Avoid sampling adjacent bricks. */
brick_coord = max(brick_coord, ivec3(0));
/* Avoid sampling adjacent bricks. */
lP = max(lP, vec3(0.5));
/* Local position inside the brick (still in grid sample spacing unit). */
vec3 brick_lP = lP - vec3(brick_coord) * float(IRRADIANCE_GRID_BRICK_SIZE - 1);
int brick_index = lightprobe_irradiance_grid_brick_index_get(grid_data, brick_coord);
IrradianceBrick brick = irradiance_brick_unpack(bricks_infos_buf[brick_index]);
vec3 output_coord = vec3(brick.atlas_coord, 0.0) + brick_lP;
return output_coord;
}
vec4 textureUnormalizedCoord(sampler3D tx, vec3 co)
{
return texture(tx, co / vec3(textureSize(tx, 0)));
}
SphericalHarmonicL1 lightprobe_irradiance_sample(sampler3D atlas_tx, vec3 P)
{
vec3 lP;
int grid_index;
for (grid_index = 0; grid_index < IRRADIANCE_GRID_MAX; grid_index++) {
/* Last grid is tagged as invalid to stop the iteration. */
if (grids_infos_buf[grid_index].grid_size.x == -1) {
/* Sample the last grid instead. */
/* TODO: Avoid this by using a default world grid. */
grid_index -= 1;
break;
}
/* If. */
if (lightprobe_irradiance_grid_local_coord(grids_infos_buf[grid_index], P, lP)) {
break;
}
}
vec3 atlas_coord = lightprobe_irradiance_grid_atlas_coord(grids_infos_buf[grid_index], lP);
SphericalHarmonicL1 sh;
sh.L0.M0 = textureUnormalizedCoord(atlas_tx, atlas_coord);
atlas_coord.z += float(IRRADIANCE_GRID_BRICK_SIZE);
sh.L1.Mn1 = textureUnormalizedCoord(atlas_tx, atlas_coord);
atlas_coord.z += float(IRRADIANCE_GRID_BRICK_SIZE);
sh.L1.M0 = textureUnormalizedCoord(atlas_tx, atlas_coord);
atlas_coord.z += float(IRRADIANCE_GRID_BRICK_SIZE);
sh.L1.Mp1 = textureUnormalizedCoord(atlas_tx, atlas_coord);
return sh;
}
void lightprobe_eval(ClosureDiffuse diffuse,
ClosureReflection reflection,
vec3 P,
vec3 Ng,
vec3 V,
inout vec3 out_diffuse,
inout vec3 out_specular)
{
SphericalHarmonicL1 irradiance = lightprobe_irradiance_sample(irradiance_atlas_tx, P);
out_diffuse += spherical_harmonics_evaluate_lambert(diffuse.N, irradiance);
}

View File

@ -0,0 +1,51 @@
/**
* Load an input lightgrid cache texture into the atlas.
*
* Each thread group will load a brick worth of data and add the needed padding texels.
*/
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_lightprobe_lib.glsl)
void atlas_store(vec4 sh_coefficient, ivec2 atlas_coord, int layer)
{
imageStore(irradiance_atlas_img,
ivec3(atlas_coord, layer * IRRADIANCE_GRID_BRICK_SIZE) + ivec3(gl_LocalInvocationID),
sh_coefficient);
}
void main()
{
int brick_index = lightprobe_irradiance_grid_brick_index_get(grids_infos_buf[grid_index],
ivec3(gl_WorkGroupID));
/* Brick coordinate in the source grid. */
ivec3 brick_coord = ivec3(gl_WorkGroupID);
/* Add padding border to allow bilinear filtering. */
ivec3 texel_coord = brick_coord * (IRRADIANCE_GRID_BRICK_SIZE - 1) + ivec3(gl_LocalInvocationID);
ivec3 input_coord = min(texel_coord, textureSize(irradiance_a_tx, 0) - 1);
/* Brick coordinate in the destination atlas. */
IrradianceBrick brick = irradiance_brick_unpack(bricks_infos_buf[brick_index]);
ivec2 output_coord = ivec2(brick.atlas_coord);
SphericalHarmonicL1 sh;
sh.L0.M0 = texelFetch(irradiance_a_tx, input_coord, 0);
sh.L1.Mn1 = texelFetch(irradiance_b_tx, input_coord, 0);
sh.L1.M0 = texelFetch(irradiance_c_tx, input_coord, 0);
sh.L1.Mp1 = texelFetch(irradiance_d_tx, input_coord, 0);
/* Rotate Spherical Harmonic into world space. */
mat3 world_to_grid_transposed = mat3(grids_infos_buf[grid_index].world_to_grid_transposed);
mat3 rotation = normalize(world_to_grid_transposed);
spherical_harmonics_L1_rotate(rotation, sh.L1);
atlas_store(sh.L0.M0, output_coord, 0);
atlas_store(sh.L1.Mn1, output_coord, 1);
atlas_store(sh.L1.M0, output_coord, 2);
atlas_store(sh.L1.Mp1, output_coord, 3);
}

View File

@ -1,4 +1,6 @@
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
vec3 lightprobe_irradiance_grid_sample_position(mat4 grid_local_to_world,
ivec3 grid_resolution,
ivec3 cell_coord)
@ -8,3 +10,28 @@ vec3 lightprobe_irradiance_grid_sample_position(mat4 grid_local_to_world,
vec3 ws_cell_pos = (grid_local_to_world * vec4(ls_cell_pos, 1.0)).xyz;
return ws_cell_pos;
}
/**
* Return true if sample position is valid.
* \a r_lP is the local position in grid units [0..grid_size).
*/
bool lightprobe_irradiance_grid_local_coord(IrradianceGridData grid_data, vec3 P, out vec3 r_lP)
{
/* Position in cell units. */
/* NOTE: The vector-matrix multiplication swapped on purpose to cancel the matrix transpose. */
vec3 lP = (vec4(P, 1.0) * grid_data.world_to_grid_transposed).xyz;
r_lP = clamp(lP, vec3(0.0), vec3(grid_data.grid_size) - 1e-5);
/* Sample is valid if position wasn't clamped. */
return all(equal(lP, r_lP));
}
int lightprobe_irradiance_grid_brick_index_get(IrradianceGridData grid_data, ivec3 brick_coord)
{
int3 grid_size_in_bricks = divide_ceil(grid_data.grid_size,
int3(IRRADIANCE_GRID_BRICK_SIZE - 1));
int brick_index = grid_data.brick_offset;
brick_index += brick_coord.x;
brick_index += brick_coord.y * grid_size_in_bricks.x;
brick_index += brick_coord.z * grid_size_in_bricks.x * grid_size_in_bricks.y;
return brick_index;
}

View File

@ -0,0 +1,71 @@
/* Directive for resetting the line numbering so the failing tests lines can be printed.
* This conflict with the shader compiler error logging scheme.
* Comment out for correct compilation error line. */
#line 5
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_test_lib.glsl)
#define TEST(a, b) if (true)
void main()
{
TEST(eevee_lightprobe, IrradianceBrickIndex)
{
float near = 0.5, far = 1.0;
mat4 pers_mat = projection_perspective(-near, near, -near, near, near, far);
mat4 normal_mat = invert(transpose(pers_mat));
LightData light;
light.clip_near = floatBitsToInt(near);
light.clip_far = floatBitsToInt(far);
light.influence_radius_max = far;
light.type = LIGHT_SPOT;
light.normal_mat_packed.x = normal_mat[3][2];
light.normal_mat_packed.y = normal_mat[3][3];
vec2 atlas_size = vec2(SHADOW_TILEMAP_RES);
{
/* Simulate a "2D" plane crossing the frustum diagonaly. */
vec3 lP0 = vec3(-1.0, 0.0, -1.0);
vec3 lP1 = vec3(0.5, 0.0, -0.5);
vec3 lTg = normalize(lP1 - lP0);
vec3 lNg = vec3(-lTg.z, 0.0, lTg.x);
float expect = 1.0 / (SHADOW_TILEMAP_RES * SHADOW_PAGE_RES);
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 0), expect, 1e-4);
EXPECT_NEAR(
shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 1), expect * 2.0, 1e-4);
EXPECT_NEAR(
shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 2), expect * 4.0, 1e-4);
}
{
/* Simulate a "2D" plane crossing the near plane at the center diagonaly. */
vec3 lP0 = vec3(-1.0, 0.0, -1.0);
vec3 lP1 = vec3(0.0, 0.0, -0.5);
vec3 lTg = normalize(lP1 - lP0);
vec3 lNg = vec3(-lTg.z, 0.0, lTg.x);
float expect = 2.0 / (SHADOW_TILEMAP_RES * SHADOW_PAGE_RES);
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 0), expect, 1e-4);
EXPECT_NEAR(
shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 1), expect * 2.0, 1e-4);
EXPECT_NEAR(
shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 2), expect * 4.0, 1e-4);
}
{
/* Simulate a "2D" plane parallel to near clip plane. */
vec3 lP0 = vec3(-1.0, 0.0, -0.75);
vec3 lP1 = vec3(0.0, 0.0, -0.75);
vec3 lTg = normalize(lP1 - lP0);
vec3 lNg = vec3(-lTg.z, 0.0, lTg.x);
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 0), 0.0, 1e-4);
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 1), 0.0, 1e-4);
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 2), 0.0, 1e-4);
}
}
}

View File

@ -187,6 +187,40 @@ vec4 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Rotation
* \{ */
void spherical_harmonics_L0_rotate(mat3x3 rotation, inout SphericalHarmonicBandL0 L0)
{
/* L0 band being a constant function (i.e: there is no directionallity) there is nothing to
* rotate. This is a no-op. */
}
void spherical_harmonics_L1_rotate(mat3x3 rotation, inout SphericalHarmonicBandL1 L1)
{
/* Convert L1 coefficients to per channel column.
* Note the component shuffle to match blender coordinate system. */
mat4x3 per_channel = transpose(mat3x4(L1.Mp1, L1.Mn1, -L1.M0));
/* Rotate each channel. */
per_channel[0] = rotation * per_channel[0];
per_channel[1] = rotation * per_channel[1];
per_channel[2] = rotation * per_channel[2];
/* Convert back to L1 coefficients to per channel column.
* Note the component shuffle to match blender coordinate system. */
mat3x4 per_coef = transpose(per_channel);
L1.Mn1 = per_coef[1];
L1.M0 = -per_coef[2];
L1.Mp1 = per_coef[0];
}
void spherical_harmonics_L2_rotate(mat3x3 rotation, inout SphericalHarmonicBandL2 L2)
{
/* TODO */
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Evaluation
* \{ */
@ -197,20 +231,23 @@ vec4 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2)
* 2/3 and 1/4. See this reference for more explanation:
* https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
*/
vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL0 sh)
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL0 sh)
{
return spherical_harmonics_L0_evaluate(N, sh.L0);
vec3 radiance = spherical_harmonics_L0_evaluate(N, sh.L0).rgb;
return radiance;
}
vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL1 sh)
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL1 sh)
{
return spherical_harmonics_L0_evaluate(N, sh.L0) +
spherical_harmonics_L1_evaluate(N, sh.L1) * (2.0 / 3.0);
vec3 radiance = spherical_harmonics_L0_evaluate(N, sh.L0).rgb +
spherical_harmonics_L1_evaluate(N, sh.L1).rgb * (2.0 / 3.0);
return radiance;
}
vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh)
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh)
{
return spherical_harmonics_L0_evaluate(N, sh.L0) +
spherical_harmonics_L1_evaluate(N, sh.L1) * (2.0 / 3.0) +
spherical_harmonics_L2_evaluate(N, sh.L2) * (1.0 / 4.0);
vec3 radiance = spherical_harmonics_L0_evaluate(N, sh.L0).rgb +
spherical_harmonics_L1_evaluate(N, sh.L1).rgb * (2.0 / 3.0) +
spherical_harmonics_L2_evaluate(N, sh.L2).rgb * (1.0 / 4.0);
return radiance;
}
/** \} */

View File

@ -37,6 +37,7 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_light)
.additional_info("eevee_shared",
"eevee_utility_texture",
"eevee_light_data",
"eevee_lightprobe_data",
"eevee_shadow_data",
"eevee_deferred_base",
"eevee_hiz_data",

View File

@ -4,8 +4,8 @@
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(eevee_hiz_data)
.sampler(15, ImageType::FLOAT_2D, "hiz_tx")
.uniform_buf(5, "HiZData", "hiz_buf");
.sampler(HIZ_TEX_SLOT, ImageType::FLOAT_2D, "hiz_tx")
.uniform_buf(HIZ_BUF_SLOT, "HiZData", "hiz_buf");
GPU_SHADER_CREATE_INFO(eevee_hiz_update)
.do_static_compilation(true)

View File

@ -3,6 +3,10 @@
#include "eevee_defines.hh"
#include "gpu_shader_create_info.hh"
/* -------------------------------------------------------------------- */
/** \name Display
* \{ */
GPU_SHADER_INTERFACE_INFO(eevee_debug_surfel_iface, "")
.smooth(Type::VEC3, "P")
.flat(Type::INT, "surfel_index");
@ -18,6 +22,32 @@ GPU_SHADER_CREATE_INFO(eevee_debug_surfels)
.push_constant(Type::INT, "debug_mode")
.do_static_compilation(true);
GPU_SHADER_INTERFACE_INFO(eevee_display_probe_grid_iface, "")
.smooth(Type::VEC2, "lP")
.flat(Type::IVEC3, "cell");
GPU_SHADER_CREATE_INFO(eevee_display_probe_grid)
.additional_info("eevee_shared", "draw_view")
.vertex_source("eevee_display_probe_grid_vert.glsl")
.vertex_out(eevee_display_probe_grid_iface)
.fragment_source("eevee_display_probe_grid_frag.glsl")
.fragment_out(0, Type::VEC4, "out_color")
.push_constant(Type::FLOAT, "sphere_radius")
.push_constant(Type::IVEC3, "grid_resolution")
.push_constant(Type::MAT4, "grid_to_world")
.push_constant(Type::MAT4, "world_to_grid")
.sampler(0, ImageType::FLOAT_3D, "irradiance_a_tx")
.sampler(1, ImageType::FLOAT_3D, "irradiance_b_tx")
.sampler(2, ImageType::FLOAT_3D, "irradiance_c_tx")
.sampler(3, ImageType::FLOAT_3D, "irradiance_d_tx")
.do_static_compilation(true);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Baking
* \{ */
GPU_SHADER_CREATE_INFO(eevee_surfel_common)
.storage_buf(SURFEL_BUF_SLOT, Qualifier::READ_WRITE, "Surfel", "surfel_buf[]")
.storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ, "CaptureInfoData", "capture_info_buf");
@ -61,26 +91,6 @@ GPU_SHADER_CREATE_INFO(eevee_surfel_ray)
.compute_source("eevee_surfel_ray_comp.glsl")
.do_static_compilation(true);
GPU_SHADER_INTERFACE_INFO(eevee_display_probe_grid_iface, "")
.smooth(Type::VEC2, "lP")
.flat(Type::IVEC3, "cell");
GPU_SHADER_CREATE_INFO(eevee_display_probe_grid)
.additional_info("eevee_shared", "draw_view")
.vertex_source("eevee_display_probe_grid_vert.glsl")
.vertex_out(eevee_display_probe_grid_iface)
.fragment_source("eevee_display_probe_grid_frag.glsl")
.fragment_out(0, Type::VEC4, "out_color")
.push_constant(Type::FLOAT, "sphere_radius")
.push_constant(Type::IVEC3, "grid_resolution")
.push_constant(Type::MAT4, "grid_to_world")
.push_constant(Type::MAT4, "world_to_grid")
.sampler(0, ImageType::FLOAT_3D, "irradiance_a_tx")
.sampler(1, ImageType::FLOAT_3D, "irradiance_b_tx")
.sampler(2, ImageType::FLOAT_3D, "irradiance_c_tx")
.sampler(3, ImageType::FLOAT_3D, "irradiance_d_tx")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray)
.local_group_size(IRRADIANCE_GRID_GROUP_SIZE,
IRRADIANCE_GRID_GROUP_SIZE,
@ -94,3 +104,35 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray)
.image(3, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L1_c_img")
.compute_source("eevee_lightprobe_irradiance_ray_comp.glsl")
.do_static_compilation(true);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Runtime
* \{ */
GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_load)
.local_group_size(IRRADIANCE_GRID_BRICK_SIZE,
IRRADIANCE_GRID_BRICK_SIZE,
IRRADIANCE_GRID_BRICK_SIZE)
.additional_info("eevee_shared")
.push_constant(Type::INT, "grid_index")
.uniform_buf(0, "IrradianceGridData", "grids_infos_buf[IRRADIANCE_GRID_MAX]")
.storage_buf(0, Qualifier::READ, "uint", "bricks_infos_buf[]")
.sampler(0, ImageType::FLOAT_3D, "irradiance_a_tx")
.sampler(1, ImageType::FLOAT_3D, "irradiance_b_tx")
.sampler(2, ImageType::FLOAT_3D, "irradiance_c_tx")
.sampler(3, ImageType::FLOAT_3D, "irradiance_d_tx")
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_atlas_img")
.compute_source("eevee_lightprobe_irradiance_load_comp.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_lightprobe_data)
.uniform_buf(IRRADIANCE_GRID_BUF_SLOT,
"IrradianceGridData",
"grids_infos_buf[IRRADIANCE_GRID_MAX]")
/* NOTE: Use uint instead of IrradianceBrickPacked because Metal need to know the exact type.*/
.storage_buf(IRRADIANCE_BRICK_BUF_SLOT, Qualifier::READ, "uint", "bricks_infos_buf[]")
.sampler(IRRADIANCE_ATLAS_TEX_SLOT, ImageType::FLOAT_3D, "irradiance_atlas_tx");
/** \} */

View File

@ -176,8 +176,11 @@ class UniformCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable
#endif
public:
UniformCommon()
UniformCommon(const char *name = nullptr)
{
if (name) {
name_ = name;
}
ubo_ = GPU_uniformbuf_create_ex(sizeof(T) * len, nullptr, name_);
}
@ -276,7 +279,7 @@ template<
/* bool device_only = false */>
class UniformArrayBuffer : public detail::UniformCommon<T, len, false> {
public:
UniformArrayBuffer()
UniformArrayBuffer(const char *name = nullptr) : detail::UniformCommon<T, len, false>(name)
{
/* TODO(@fclem): We should map memory instead. */
this->data_ = (T *)MEM_mallocN_aligned(len * sizeof(T), 16, this->name_);
@ -295,7 +298,7 @@ template<
/* bool device_only = false */>
class UniformBuffer : public T, public detail::UniformCommon<T, 1, false> {
public:
UniformBuffer()
UniformBuffer(const char *name = nullptr) : detail::UniformCommon<T, 1, false>(name)
{
/* TODO(@fclem): How could we map this? */
this->data_ = static_cast<T *>(this);
@ -367,6 +370,11 @@ class StorageArrayBuffer : public detail::StorageCommon<T, len, device_only> {
return this->len_;
}
MutableSpan<T> as_span() const
{
return {this->data_, this->len_};
}
static void swap(StorageArrayBuffer &a, StorageArrayBuffer &b)
{
SWAP(T *, a.data_, b.data_);
@ -422,6 +430,14 @@ class StorageVectorBuffer : public StorageArrayBuffer<T, len, false> {
new (ptr) T(std::forward<ForwardT>(value)...);
}
void extend(const Span<T> &values)
{
/* TODO(fclem): Optimize to a single memcpy. */
for (auto v : values) {
this->append(v);
}
}
int64_t size() const
{
return item_len_;
@ -1032,8 +1048,7 @@ class TextureRef : public Texture {
* Dummy type to bind texture as image.
* It is just a GPUTexture in disguise.
*/
class Image {
};
class Image {};
static inline Image *as_image(GPUTexture *tex)
{

View File

@ -396,3 +396,12 @@ void drw_print_value(mat4 value)
drw_print(" ", value[3]);
drw_print(")");
}
void drw_print_value(mat3 value)
{
drw_print("mat3x3(");
drw_print(" ", value[0]);
drw_print(" ", value[1]);
drw_print(" ", value[2]);
drw_print(")");
}

View File

@ -48,6 +48,7 @@
# ifndef GPU_METAL
# define float2 vec2
# define float3 vec3
# define float3x4 mat3x4
# define float4 vec4
# define float4x4 mat4
# define int2 ivec2
@ -72,6 +73,7 @@
# include "BLI_math_vector_types.hh"
using blender::float2;
using blender::float3;
using blender::float3x4;
using blender::float4;
using blender::float4x4;
using blender::int2;
@ -91,6 +93,7 @@ typedef float float2[2];
typedef float float3[3];
typedef float float4[4];
typedef float float4x4[4][4];
typedef float float3x4[3][4];
typedef int int2[2];
typedef int int3[2];
typedef int int4[4];