feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -30,6 +30,7 @@
#include "lightmapper_rd.h"
#include "core/string/print_string.h"
#include "lm_blendseams.glsl.gen.h"
#include "lm_compute.glsl.gen.h"
#include "lm_raster.glsl.gen.h"
@ -40,10 +41,14 @@
#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
#include "servers/rendering/rendering_device_binds.h"
#include "servers/rendering/rendering_server_globals.h"
#if defined(VULKAN_ENABLED)
#include "drivers/vulkan/rendering_context_driver_vulkan.h"
#endif
#if defined(METAL_ENABLED)
#include "drivers/metal/rendering_context_driver_metal.h"
#endif
//uncomment this if you want to see textures from all the process saved
//#define DEBUG_TEXTURES
@ -59,7 +64,7 @@ void LightmapperRD::add_mesh(const MeshData &p_mesh) {
mesh_instances.push_back(mi);
}
void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_angular_distance, float p_shadow_blur) {
void LightmapperRD::add_directional_light(const String &p_name, bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_angular_distance, float p_shadow_blur) {
Light l;
l.type = LIGHT_TYPE_DIRECTIONAL;
l.direction[0] = p_direction.x;
@ -74,9 +79,10 @@ void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direct
l.size = Math::tan(Math::deg_to_rad(p_angular_distance));
l.shadow_blur = p_shadow_blur;
lights.push_back(l);
light_names.push_back(p_name);
}
void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) {
void LightmapperRD::add_omni_light(const String &p_name, bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) {
Light l;
l.type = LIGHT_TYPE_OMNI;
l.position[0] = p_position.x;
@ -93,9 +99,10 @@ void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, con
l.size = p_size;
l.shadow_blur = p_shadow_blur;
lights.push_back(l);
light_names.push_back(p_name);
}
void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) {
void LightmapperRD::add_spot_light(const String &p_name, bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) {
Light l;
l.type = LIGHT_TYPE_SPOT;
l.position[0] = p_position.x;
@ -117,6 +124,7 @@ void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, con
l.size = p_size;
l.shadow_blur = p_shadow_blur;
lights.push_back(l);
light_names.push_back(p_name);
}
void LightmapperRD::add_probe(const Vector3 &p_position) {
@ -226,14 +234,14 @@ void LightmapperRD::_sort_triangle_clusters(uint32_t p_cluster_size, uint32_t p_
}
}
Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_size, int p_denoiser_range, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, BakeStepFunc p_step_function, void *p_bake_userdata) {
Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_size, int p_denoiser_range, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, float p_supersampling_factor, BakeStepFunc p_step_function, void *p_bake_userdata) {
Vector<Size2i> sizes;
for (int m_i = 0; m_i < mesh_instances.size(); m_i++) {
MeshInstance &mi = mesh_instances.write[m_i];
Size2i s = Size2i(mi.data.albedo_on_uv2->get_width(), mi.data.albedo_on_uv2->get_height());
sizes.push_back(s);
atlas_size = atlas_size.max(s + Size2i(2, 2).maxi(p_denoiser_range));
atlas_size = atlas_size.max(s + Size2i(2, 2).maxi(p_denoiser_range) * p_supersampling_factor);
}
int max = nearest_power_of_2_templated(atlas_size.width);
@ -244,7 +252,9 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
}
if (p_step_function) {
p_step_function(0.1, RTR("Determining optimal atlas size"), p_bake_userdata, true);
if (p_step_function(0.1, RTR("Determining optimal atlas size"), p_bake_userdata, true)) {
return BAKE_ERROR_USER_ABORTED;
}
}
atlas_size = Size2i(max, max);
@ -261,7 +271,10 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
source_sizes.resize(sizes.size());
source_indices.resize(sizes.size());
for (int i = 0; i < source_indices.size(); i++) {
source_sizes.write[i] = sizes[i] + Vector2i(2, 2).maxi(p_denoiser_range); // Add padding between lightmaps.
// Add padding between lightmaps.
// Scale the padding if the lightmap will be downsampled at the end of the baking process
// Otherwise the padding would be insufficient.
source_sizes.write[i] = sizes[i] + Vector2i(2, 2).maxi(p_denoiser_range) * p_supersampling_factor;
source_indices.write[i] = i;
}
Vector<Vector3i> atlas_offsets;
@ -321,7 +334,9 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
emission_images.resize(atlas_slices);
if (p_step_function) {
p_step_function(0.2, RTR("Blitting albedo and emission"), p_bake_userdata, true);
if (p_step_function(0.2, RTR("Blitting albedo and emission"), p_bake_userdata, true)) {
return BAKE_ERROR_USER_ABORTED;
}
}
for (int i = 0; i < atlas_slices; i++) {
@ -467,7 +482,14 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
t.max_bounds[0] = taabb.position.x + MAX(taabb.size.x, 0.0001);
t.max_bounds[1] = taabb.position.y + MAX(taabb.size.y, 0.0001);
t.max_bounds[2] = taabb.position.z + MAX(taabb.size.z, 0.0001);
t.pad0 = t.pad1 = 0; //make valgrind not complain
t.cull_mode = RS::CULL_MODE_BACK;
RID material = mi.data.material[i];
if (material.is_valid()) {
t.cull_mode = RSG::material_storage->material_get_cull_mode(material);
}
t.pad1 = 0; //make valgrind not complain
triangles.push_back(t);
slice_triangle_count.write[t.slice]++;
}
@ -715,7 +737,7 @@ void LightmapperRD::_raster_geometry(RenderingDevice *rd, Size2i atlas_size, int
raster_push_constant.uv_offset[0] = -0.5f / float(atlas_size.x);
raster_push_constant.uv_offset[1] = -0.5f / float(atlas_size.y);
RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::DRAW_CLEAR_ALL, clear_colors, 1.0f, 0, Rect2(), RDD::BreadcrumbMarker::LIGHTMAPPER_PASS);
//draw opaque
rd->draw_list_bind_render_pipeline(draw_list, raster_pipeline);
rd->draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0);
@ -790,9 +812,38 @@ LightmapperRD::BakeError LightmapperRD::_dilate(RenderingDevice *rd, Ref<RDShade
return BAKE_OK;
}
Error LightmapperRD::_store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_index, const Size2i &p_atlas_size, const String &p_name) {
LightmapperRD::BakeError LightmapperRD::_pack_l1(RenderingDevice *rd, Ref<RDShaderFile> &compute_shader, RID &compute_base_uniform_set, PushConstant &push_constant, RID &source_light_tex, RID &dest_light_tex, const Size2i &atlas_size, int atlas_slices) {
Vector<RD::Uniform> uniforms = dilate_or_denoise_common_uniforms(source_light_tex, dest_light_tex);
RID compute_shader_pack = rd->shader_create_from_spirv(compute_shader->get_spirv_stages("pack_coeffs"));
ERR_FAIL_COND_V(compute_shader_pack.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen
RID compute_shader_pack_pipeline = rd->compute_pipeline_create(compute_shader_pack);
RID dilate_uniform_set = rd->uniform_set_create(uniforms, compute_shader_pack, 1);
RD::ComputeListID compute_list = rd->compute_list_begin();
rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_pack_pipeline);
rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
rd->compute_list_bind_uniform_set(compute_list, dilate_uniform_set, 1);
push_constant.region_ofs[0] = 0;
push_constant.region_ofs[1] = 0;
Vector3i group_size(Math::division_round_up(atlas_size.x, 8), Math::division_round_up(atlas_size.y, 8), 1); //restore group size
for (int i = 0; i < atlas_slices; i++) {
push_constant.atlas_slice = i;
rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z);
//no barrier, let them run all together
}
rd->compute_list_end();
rd->free(compute_shader_pack);
return BAKE_OK;
}
Error LightmapperRD::_store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_index, const Size2i &p_atlas_size, const String &p_name, bool p_shadowmask) {
Vector<uint8_t> data = p_rd->texture_get_data(p_atlas_tex, p_index);
Ref<Image> img = Image::create_from_data(p_atlas_size.width, p_atlas_size.height, false, Image::FORMAT_RGBAH, data);
Ref<Image> img = Image::create_from_data(p_atlas_size.width, p_atlas_size.height, false, p_shadowmask ? Image::FORMAT_RGBA8 : Image::FORMAT_RGBAH, data);
img->convert(Image::FORMAT_RGBF);
Vector<uint8_t> data_float = img->get_data();
@ -812,7 +863,7 @@ Error LightmapperRD::_store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_in
return OK;
}
Ref<Image> LightmapperRD::_read_pfm(const String &p_name) {
Ref<Image> LightmapperRD::_read_pfm(const String &p_name, bool p_shadowmask) {
Error err = OK;
Ref<FileAccess> file = FileAccess::open(p_name, FileAccess::READ, &err);
ERR_FAIL_COND_V_MSG(err, Ref<Image>(), vformat("Can't load PFM at path: '%s'.", p_name));
@ -845,23 +896,23 @@ Ref<Image> LightmapperRD::_read_pfm(const String &p_name) {
}
#endif
Ref<Image> img = Image::create_from_data(new_width, new_height, false, Image::FORMAT_RGBF, new_data);
img->convert(Image::FORMAT_RGBAH);
img->convert(p_shadowmask ? Image::FORMAT_RGBA8 : Image::FORMAT_RGBAH);
return img;
}
LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, const String &p_exe) {
LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, bool p_shadowmask, const String &p_exe) {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < p_atlas_slices; i++) {
String fname_norm_in = EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("temp_norm_%d.pfm", i));
_store_pfm(p_rd, p_source_normal_tex, i, p_atlas_size, fname_norm_in);
_store_pfm(p_rd, p_source_normal_tex, i, p_atlas_size, fname_norm_in, false);
for (int j = 0; j < (p_bake_sh ? 4 : 1); j++) {
int index = i * (p_bake_sh ? 4 : 1) + j;
String fname_light_in = EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("temp_light_%d.pfm", index));
String fname_out = EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("temp_denoised_%d.pfm", index));
_store_pfm(p_rd, p_source_light_tex, index, p_atlas_size, fname_light_in);
_store_pfm(p_rd, p_source_light_tex, index, p_atlas_size, fname_light_in, p_shadowmask);
List<String> args;
args.push_back("--device");
@ -870,7 +921,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID
args.push_back("--filter");
args.push_back("RTLightmap");
args.push_back("--hdr");
args.push_back(p_shadowmask ? "--ldr" : "--hdr");
args.push_back(fname_light_in);
args.push_back("--nrm");
@ -892,7 +943,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID
ERR_FAIL_V_MSG(BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES, vformat("OIDN denoiser failed, return code: %d", exitcode));
}
Ref<Image> img = _read_pfm(fname_out);
Ref<Image> img = _read_pfm(fname_out, p_shadowmask);
da->remove(fname_out);
ERR_FAIL_COND_V(img.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES);
@ -915,7 +966,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID
return BAKE_OK;
}
LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDShaderFile> &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, float p_denoiser_strength, int p_denoiser_range, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function) {
LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDShaderFile> &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, float p_denoiser_strength, int p_denoiser_range, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function, void *p_bake_userdata) {
RID denoise_params_buffer = p_rd->uniform_buffer_create(sizeof(DenoiseParams));
DenoiseParams denoise_params;
denoise_params.spatial_bandwidth = 5.0f;
@ -951,7 +1002,8 @@ LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDSh
// We denoise in fixed size regions and synchronize execution to avoid GPU timeouts.
// We use a region with 1/4 the amount of pixels if we're denoising SH lightmaps, as
// all four of them are denoised in the shader in one dispatch.
const int max_region_size = p_bake_sh ? 512 : 1024;
const int user_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/lightmapping/bake_performance/region_size")));
const int max_region_size = p_bake_sh ? user_region_size / 2 : user_region_size;
int x_regions = Math::division_round_up(p_atlas_size.width, max_region_size);
int y_regions = Math::division_round_up(p_atlas_size.height, max_region_size);
for (int s = 0; s < p_atlas_slices; s++) {
@ -978,6 +1030,13 @@ LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDSh
p_rd->sync();
}
}
if (p_step_function) {
int percent = (s + 1) * 100 / p_atlas_slices;
float p = float(s) / p_atlas_slices * 0.1;
if (p_step_function(0.8 + p, vformat(RTR("Denoising %d%%"), percent), p_bake_userdata, false)) {
return BAKE_ERROR_USER_ABORTED;
}
}
}
p_rd->free(compute_shader_denoise);
@ -986,7 +1045,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDSh
return BAKE_OK;
}
LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_denoiser_range, int p_bounces, float p_bounce_indirect_energy, float p_bias, int p_max_texture_size, bool p_bake_sh, bool p_texture_for_bounces, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function, void *p_bake_userdata, float p_exposure_normalization) {
LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_denoiser_range, int p_bounces, float p_bounce_indirect_energy, float p_bias, int p_max_texture_size, bool p_bake_sh, bool p_bake_shadowmask, bool p_texture_for_bounces, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function, void *p_bake_userdata, float p_exposure_normalization, float p_supersampling_factor) {
int denoiser = GLOBAL_GET("rendering/lightmapping/denoising/denoiser");
String oidn_path = EDITOR_GET("filesystem/tools/oidn/oidn_denoise_path");
@ -1007,7 +1066,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
if (p_step_function) {
p_step_function(0.0, RTR("Begin Bake"), p_bake_userdata, true);
}
bake_textures.clear();
lightmap_textures.clear();
shadowmask_textures.clear();
int grid_size = 128;
/* STEP 1: Fetch material textures and compute the bounds */
@ -1018,11 +1078,40 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
Vector<Ref<Image>> albedo_images;
Vector<Ref<Image>> emission_images;
BakeError bake_error = _blit_meshes_into_atlas(p_max_texture_size, p_denoiser_range, albedo_images, emission_images, bounds, atlas_size, atlas_slices, p_step_function, p_bake_userdata);
BakeError bake_error = _blit_meshes_into_atlas(p_max_texture_size, p_denoiser_range, albedo_images, emission_images, bounds, atlas_size, atlas_slices, p_supersampling_factor, p_step_function, p_bake_userdata);
if (bake_error != BAKE_OK) {
return bake_error;
}
// The index of the directional light used for shadowmasking.
int shadowmask_light_idx = -1;
// If there are no valid directional lights for shadowmasking, the entire
// scene would be shadowed and this saves baking time.
if (p_bake_shadowmask) {
int shadowmask_lights_count = 0;
for (int i = 0; i < lights.size(); i++) {
if (lights[i].type == LightType::LIGHT_TYPE_DIRECTIONAL && !lights[i].static_bake) {
if (shadowmask_light_idx < 0) {
shadowmask_light_idx = i;
}
shadowmask_lights_count += 1;
}
}
if (shadowmask_light_idx < 0) {
p_bake_shadowmask = false;
WARN_PRINT("Shadowmask disabled: no directional light with their bake mode set to dynamic exists.");
} else if (shadowmask_lights_count > 1) {
WARN_PRINT(
vformat("%d directional lights detected for shadowmask baking. Only %s will be used.",
shadowmask_lights_count, light_names[shadowmask_light_idx]));
}
}
#ifdef DEBUG_TEXTURES
for (int i = 0; i < atlas_slices; i++) {
albedo_images[i]->save_png("res://0_albedo_" + itos(i) + ".png");
@ -1038,10 +1127,16 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
RenderingDevice *rd = RenderingServer::get_singleton()->create_local_rendering_device();
if (rd == nullptr) {
#if defined(RD_ENABLED)
#if defined(VULKAN_ENABLED)
rcd = memnew(RenderingContextDriverVulkan);
#if defined(METAL_ENABLED)
rcd = memnew(RenderingContextDriverMetal);
rd = memnew(RenderingDevice);
#endif
#if defined(VULKAN_ENABLED)
if (rcd == nullptr) {
rcd = memnew(RenderingContextDriverVulkan);
rd = memnew(RenderingDevice);
}
#endif
#endif
if (rcd != nullptr && rd != nullptr) {
err = rcd->initialize();
@ -1070,17 +1165,23 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
RID light_accum_tex;
RID light_accum_tex2;
RID light_environment_tex;
RID shadowmask_tex;
RID shadowmask_tex2;
#define FREE_TEXTURES \
rd->free(albedo_array_tex); \
rd->free(emission_array_tex); \
rd->free(normal_tex); \
rd->free(position_tex); \
rd->free(unocclude_tex); \
rd->free(light_source_tex); \
rd->free(light_accum_tex2); \
rd->free(light_accum_tex); \
rd->free(light_environment_tex);
#define FREE_TEXTURES \
rd->free(albedo_array_tex); \
rd->free(emission_array_tex); \
rd->free(normal_tex); \
rd->free(position_tex); \
rd->free(unocclude_tex); \
rd->free(light_source_tex); \
rd->free(light_accum_tex2); \
rd->free(light_accum_tex); \
rd->free(light_environment_tex); \
if (p_bake_shadowmask) { \
rd->free(shadowmask_tex); \
rd->free(shadowmask_tex2); \
}
{ // create all textures
@ -1112,9 +1213,22 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
position_tex = rd->texture_create(tf, RD::TextureView());
unocclude_tex = rd->texture_create(tf, RD::TextureView());
tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
// shadowmask
if (p_bake_shadowmask) {
tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
shadowmask_tex = rd->texture_create(tf, RD::TextureView());
rd->texture_clear(shadowmask_tex, Color(0, 0, 0, 0), 0, 1, 0, atlas_slices);
shadowmask_tex2 = rd->texture_create(tf, RD::TextureView());
rd->texture_clear(shadowmask_tex2, Color(0, 0, 0, 0), 0, 1, 0, atlas_slices);
}
// lightmap
tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
light_source_tex = rd->texture_create(tf, RD::TextureView());
rd->texture_clear(light_source_tex, Color(0, 0, 0, 0), 0, 1, 0, atlas_slices);
@ -1217,12 +1331,24 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
bake_parameters.exposure_normalization = p_exposure_normalization;
bake_parameters.bounces = p_bounces;
bake_parameters.bounce_indirect_energy = p_bounce_indirect_energy;
bake_parameters.shadowmask_light_idx = shadowmask_light_idx;
// Same number of rays for transparency regardless of quality (it's more of a retry rather than shooting new ones).
bake_parameters.transparency_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_transparency_rays");
bake_parameters.supersampling_factor = p_supersampling_factor;
bake_parameters_buffer = rd->uniform_buffer_create(sizeof(BakeParameters));
rd->buffer_update(bake_parameters_buffer, 0, sizeof(BakeParameters), &bake_parameters);
if (p_step_function) {
p_step_function(0.47, RTR("Preparing shaders"), p_bake_userdata, true);
if (p_step_function(0.47, RTR("Preparing shaders"), p_bake_userdata, true)) {
FREE_TEXTURES
FREE_BUFFERS
memdelete(rd);
if (rcd != nullptr) {
memdelete(rcd);
}
return BAKE_ERROR_USER_ABORTED;
}
}
//shaders
@ -1362,6 +1488,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
tf.texture_type = RD::TEXTURE_TYPE_2D;
tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
tf.format = RD::DATA_FORMAT_D32_SFLOAT;
tf.is_discardable = true;
raster_depth_buffer = rd->texture_create(tf, RD::TextureView());
}
@ -1405,6 +1532,10 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
defines += "\n#define USE_LIGHT_TEXTURE_FOR_BOUNCES\n";
}
if (p_bake_shadowmask) {
defines += "\n#define USE_SHADOWMASK\n";
}
compute_shader.instantiate();
err = compute_shader->parse_versions_from_text(lm_compute_shader_glsl, defines);
if (err != OK) {
@ -1454,7 +1585,17 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
rd->sync();
if (p_step_function) {
p_step_function(0.49, RTR("Un-occluding geometry"), p_bake_userdata, true);
if (p_step_function(0.49, RTR("Un-occluding geometry"), p_bake_userdata, true)) {
FREE_TEXTURES
FREE_BUFFERS
FREE_RASTER_RESOURCES
FREE_COMPUTE_RESOURCES
memdelete(rd);
if (rcd != nullptr) {
memdelete(rcd);
}
return BAKE_ERROR_USER_ABORTED;
}
}
PushConstant push_constant;
@ -1496,9 +1637,23 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
}
if (p_step_function) {
p_step_function(0.5, RTR("Plot direct lighting"), p_bake_userdata, true);
if (p_step_function(0.5, RTR("Plot direct lighting"), p_bake_userdata, true)) {
FREE_TEXTURES
FREE_BUFFERS
FREE_RASTER_RESOURCES
FREE_COMPUTE_RESOURCES
memdelete(rd);
if (rcd != nullptr) {
memdelete(rcd);
}
return BAKE_ERROR_USER_ABORTED;
}
}
const int max_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/lightmapping/bake_performance/region_size")));
const int x_regions = Math::division_round_up(atlas_size.width, max_region_size);
const int y_regions = Math::division_round_up(atlas_size.height, max_region_size);
// Set ray count to the quality used for direct light and bounces.
switch (p_quality) {
case BAKE_QUALITY_LOW: {
@ -1556,22 +1711,64 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
u.append_id(light_accum_tex);
uniforms.push_back(u);
}
if (p_bake_shadowmask) {
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 5;
u.append_id(shadowmask_tex);
uniforms.push_back(u);
}
}
RID light_uniform_set = rd->uniform_set_create(uniforms, compute_shader_primary, 1);
RD::ComputeListID compute_list = rd->compute_list_begin();
rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_primary_pipeline);
rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
rd->compute_list_bind_uniform_set(compute_list, light_uniform_set, 1);
int count = 0;
for (int s = 0; s < atlas_slices; s++) {
push_constant.atlas_slice = s;
for (int i = 0; i < atlas_slices; i++) {
push_constant.atlas_slice = i;
rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z);
//no barrier, let them run all together
for (int i = 0; i < x_regions; i++) {
for (int j = 0; j < y_regions; j++) {
int x = i * max_region_size;
int y = j * max_region_size;
int w = MIN((i + 1) * max_region_size, atlas_size.width) - x;
int h = MIN((j + 1) * max_region_size, atlas_size.height) - y;
push_constant.region_ofs[0] = x;
push_constant.region_ofs[1] = y;
group_size = Vector3i(Math::division_round_up(w, 8), Math::division_round_up(h, 8), 1);
RD::ComputeListID compute_list = rd->compute_list_begin();
rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_primary_pipeline);
rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
rd->compute_list_bind_uniform_set(compute_list, light_uniform_set, 1);
rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z);
rd->compute_list_end();
rd->submit();
rd->sync();
count++;
if (p_step_function) {
int total = (atlas_slices * x_regions * y_regions);
int percent = count * 100 / total;
float p = float(count) / total * 0.1;
if (p_step_function(0.5 + p, vformat(RTR("Plot direct lighting %d%%"), percent), p_bake_userdata, false)) {
FREE_TEXTURES
FREE_BUFFERS
FREE_RASTER_RESOURCES
FREE_COMPUTE_RESOURCES
memdelete(rd);
if (rcd != nullptr) {
memdelete(rcd);
}
return BAKE_ERROR_USER_ABORTED;
}
}
}
}
}
rd->compute_list_end(); //done
}
#ifdef DEBUG_TEXTURES
@ -1581,12 +1778,17 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
img->save_exr("res://2_light_primary_" + itos(i) + ".exr", false);
}
if (p_bake_sh) {
for (int i = 0; i < atlas_slices * 4; i++) {
Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
img->save_exr("res://2_light_primary_accum_" + itos(i) + ".exr", false);
}
}
#endif
/* SECONDARY (indirect) LIGHT PASS(ES) */
if (p_step_function) {
p_step_function(0.6, RTR("Integrate indirect lighting"), p_bake_userdata, true);
}
if (p_bounces > 0) {
Vector<RD::Uniform> uniforms;
@ -1639,16 +1841,22 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
RID secondary_uniform_set;
secondary_uniform_set = rd->uniform_set_create(uniforms, compute_shader_secondary, 1);
int max_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/lightmapping/bake_performance/region_size")));
int max_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_rays_per_pass");
int x_regions = Math::division_round_up(atlas_size.width, max_region_size);
int y_regions = Math::division_round_up(atlas_size.height, max_region_size);
const int max_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_rays_per_pass");
int ray_iterations = Math::division_round_up((int32_t)push_constant.ray_count, max_rays);
rd->submit();
rd->sync();
if (p_step_function) {
if (p_step_function(0.6, RTR("Integrate indirect lighting"), p_bake_userdata, true)) {
FREE_TEXTURES
FREE_BUFFERS
FREE_RASTER_RESOURCES
FREE_COMPUTE_RESOURCES
memdelete(rd);
if (rcd != nullptr) {
memdelete(rcd);
}
return BAKE_ERROR_USER_ABORTED;
}
}
int count = 0;
for (int s = 0; s < atlas_slices; s++) {
@ -1686,7 +1894,17 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
int total = (atlas_slices * x_regions * y_regions * ray_iterations);
int percent = count * 100 / total;
float p = float(count) / total * 0.1;
p_step_function(0.6 + p, vformat(RTR("Integrate indirect lighting %d%%"), percent), p_bake_userdata, false);
if (p_step_function(0.6 + p, vformat(RTR("Integrate indirect lighting %d%%"), percent), p_bake_userdata, false)) {
FREE_TEXTURES
FREE_BUFFERS
FREE_RASTER_RESOURCES
FREE_COMPUTE_RESOURCES
memdelete(rd);
if (rcd != nullptr) {
memdelete(rcd);
}
return BAKE_ERROR_USER_ABORTED;
}
}
}
}
@ -1702,7 +1920,20 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
light_probe_buffer = rd->storage_buffer_create(sizeof(float) * 4 * 9 * probe_positions.size());
if (p_step_function) {
p_step_function(0.7, RTR("Baking lightprobes"), p_bake_userdata, true);
if (p_step_function(0.7, RTR("Baking light probes"), p_bake_userdata, true)) {
FREE_TEXTURES
FREE_BUFFERS
FREE_RASTER_RESOURCES
FREE_COMPUTE_RESOURCES
if (probe_positions.size() > 0) {
rd->free(light_probe_buffer);
}
memdelete(rd);
if (rcd != nullptr) {
memdelete(rcd);
}
return BAKE_ERROR_USER_ABORTED;
}
}
Vector<RD::Uniform> uniforms;
@ -1770,7 +2001,20 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
if (p_step_function) {
int percent = i * 100 / ray_iterations;
float p = float(i) / ray_iterations * 0.1;
p_step_function(0.7 + p, vformat(RTR("Integrating light probes %d%%"), percent), p_bake_userdata, false);
if (p_step_function(0.7 + p, vformat(RTR("Integrating light probes %d%%"), percent), p_bake_userdata, false)) {
FREE_TEXTURES
FREE_BUFFERS
FREE_RASTER_RESOURCES
FREE_COMPUTE_RESOURCES
if (probe_positions.size() > 0) {
rd->free(light_probe_buffer);
}
memdelete(rd);
if (rcd != nullptr) {
memdelete(rcd);
}
return BAKE_ERROR_USER_ABORTED;
}
}
}
}
@ -1792,18 +2036,46 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
if (p_use_denoiser) {
if (p_step_function) {
p_step_function(0.8, RTR("Denoising"), p_bake_userdata, true);
if (p_step_function(0.8, RTR("Denoising"), p_bake_userdata, true)) {
FREE_TEXTURES
FREE_BUFFERS
FREE_RASTER_RESOURCES
FREE_COMPUTE_RESOURCES
if (probe_positions.size() > 0) {
rd->free(light_probe_buffer);
}
memdelete(rd);
if (rcd != nullptr) {
memdelete(rcd);
}
return BAKE_ERROR_USER_ABORTED;
}
}
{
BakeError error;
if (denoiser == 1) {
// OIDN (external).
error = _denoise_oidn(rd, light_accum_tex, normal_tex, light_accum_tex, atlas_size, atlas_slices, p_bake_sh, oidn_path);
error = _denoise_oidn(rd, light_accum_tex, normal_tex, light_accum_tex, atlas_size, atlas_slices, p_bake_sh, false, oidn_path);
} else {
// JNLM (built-in).
SWAP(light_accum_tex, light_accum_tex2);
error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, normal_tex, light_accum_tex, p_denoiser_strength, p_denoiser_range, atlas_size, atlas_slices, p_bake_sh, p_step_function);
error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, normal_tex, light_accum_tex, p_denoiser_strength, p_denoiser_range, atlas_size, atlas_slices, p_bake_sh, p_step_function, p_bake_userdata);
}
if (unlikely(error != BAKE_OK)) {
return error;
}
}
if (p_bake_shadowmask) {
BakeError error;
if (denoiser == 1) {
// OIDN (external).
error = _denoise_oidn(rd, shadowmask_tex, normal_tex, shadowmask_tex, atlas_size, atlas_slices, false, true, oidn_path);
} else {
// JNLM (built-in).
SWAP(shadowmask_tex, shadowmask_tex2);
error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, shadowmask_tex2, normal_tex, shadowmask_tex, p_denoiser_strength, p_denoiser_range, atlas_size, atlas_slices, false, p_step_function, p_bake_userdata);
}
if (unlikely(error != BAKE_OK)) {
return error;
@ -1811,12 +2083,22 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
}
}
/* DILATE */
{
SWAP(light_accum_tex, light_accum_tex2);
BakeError error = _dilate(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, light_accum_tex, atlas_size, atlas_slices * (p_bake_sh ? 4 : 1));
if (unlikely(error != BAKE_OK)) {
return error;
}
if (p_bake_shadowmask) {
SWAP(shadowmask_tex, shadowmask_tex2);
error = _dilate(rd, compute_shader, compute_base_uniform_set, push_constant, shadowmask_tex2, shadowmask_tex, atlas_size, atlas_slices);
if (unlikely(error != BAKE_OK)) {
return error;
}
}
}
#ifdef DEBUG_TEXTURES
@ -1904,8 +2186,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
uint32_t seam_offset = 0;
uint32_t triangle_offset = 0;
Vector<Color> clear_colors;
clear_colors.push_back(Color(0, 0, 0, 1));
for (int i = 0; i < atlas_slices; i++) {
int subslices = (p_bake_sh ? 4 : 1);
@ -1918,7 +2198,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
seams_push_constant.slice = uint32_t(i * subslices + k);
seams_push_constant.debug = debug;
RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i * subslices + k], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
// Store the current subslice in the breadcrumb.
RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i * subslices + k], RD::DRAW_CLEAR_DEPTH, Vector<Color>(), 1.0f, 0, Rect2(), RDD::BreadcrumbMarker::LIGHTMAPPER_PASS | seams_push_constant.slice);
rd->draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0);
rd->draw_list_bind_uniform_set(draw_list, blendseams_raster_uniform, 1);
@ -1978,6 +2259,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
}
}
if (p_bake_sh) {
SWAP(light_accum_tex, light_accum_tex2);
BakeError error = _pack_l1(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, light_accum_tex, atlas_size, atlas_slices);
if (unlikely(error != BAKE_OK)) {
return error;
}
}
#ifdef DEBUG_TEXTURES
for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
@ -1986,6 +2275,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
img->save_exr("res://5_blendseams" + itos(i) + ".exr", false);
}
#endif
if (p_step_function) {
p_step_function(0.9, RTR("Retrieving textures"), p_bake_userdata, true);
}
@ -1994,7 +2284,16 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
img->convert(Image::FORMAT_RGBH); //remove alpha
bake_textures.push_back(img);
lightmap_textures.push_back(img);
}
if (p_bake_shadowmask) {
for (int i = 0; i < atlas_slices; i++) {
Vector<uint8_t> s = rd->texture_get_data(shadowmask_tex, i);
Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBA8, s);
img->convert(Image::FORMAT_R8);
shadowmask_textures.push_back(img);
}
}
if (probe_positions.size() > 0) {
@ -2027,12 +2326,21 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
}
int LightmapperRD::get_bake_texture_count() const {
return bake_textures.size();
return lightmap_textures.size();
}
Ref<Image> LightmapperRD::get_bake_texture(int p_index) const {
ERR_FAIL_INDEX_V(p_index, bake_textures.size(), Ref<Image>());
return bake_textures[p_index];
ERR_FAIL_INDEX_V(p_index, lightmap_textures.size(), Ref<Image>());
return lightmap_textures[p_index];
}
int LightmapperRD::get_shadowmask_texture_count() const {
return shadowmask_textures.size();
}
Ref<Image> LightmapperRD::get_shadowmask_texture(int p_index) const {
ERR_FAIL_INDEX_V(p_index, shadowmask_textures.size(), Ref<Image>());
return shadowmask_textures[p_index];
}
int LightmapperRD::get_bake_mesh_count() const {
@ -2045,9 +2353,9 @@ Variant LightmapperRD::get_bake_mesh_userdata(int p_index) const {
}
Rect2 LightmapperRD::get_bake_mesh_uv_scale(int p_index) const {
ERR_FAIL_COND_V(bake_textures.is_empty(), Rect2());
ERR_FAIL_COND_V(lightmap_textures.is_empty(), Rect2());
Rect2 uv_ofs;
Vector2 atlas_size = Vector2(bake_textures[0]->get_width(), bake_textures[0]->get_height());
Vector2 atlas_size = Vector2(lightmap_textures[0]->get_width(), lightmap_textures[0]->get_height());
uv_ofs.position = Vector2(mesh_instances[p_index].offset) / atlas_size;
uv_ofs.size = Vector2(mesh_instances[p_index].data.albedo_on_uv2->get_width(), mesh_instances[p_index].data.albedo_on_uv2->get_height()) / atlas_size;
return uv_ofs;