Browse Source

Several fixes to GIProbes

Juan Linietsky 5 years ago
parent
commit
da0457fa29

+ 13 - 0
core/image.cpp

@@ -87,6 +87,8 @@ const char *Image::format_names[Image::FORMAT_MAX] = {
 SavePNGFunc Image::save_png_func = NULL;
 SaveEXRFunc Image::save_exr_func = NULL;
 
+SavePNGBufferFunc Image::save_png_buffer_func = NULL;
+
 void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, const uint8_t *p_pixel) {
 
 	uint32_t ofs = (p_y * width + p_x) * p_pixelsize;
@@ -901,6 +903,7 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
 	ERR_FAIL_COND_MSG(p_height <= 0, "Image height must be greater than 0.");
 	ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + ".");
 	ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + ".");
+	ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS));
 
 	if (p_width == width && p_height == height)
 		return;
@@ -1593,6 +1596,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
 
 	ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH);
 	ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT);
+	ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS));
 
 	int mm = 0;
 	int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
@@ -1612,6 +1616,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
 
 	ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH);
 	ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT);
+	ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS));
 
 	int mm;
 	int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
@@ -1910,6 +1915,14 @@ Error Image::save_png(const String &p_path) const {
 	return save_png_func(p_path, Ref<Image>((Image *)this));
 }
 
+PoolVector<uint8_t> Image::save_png_to_buffer() const {
+	if (save_png_buffer_func == NULL) {
+		return PoolVector<uint8_t>();
+	}
+
+	return save_png_buffer_func(Ref<Image>((Image *)this));
+}
+
 Error Image::save_exr(const String &p_path, bool p_grayscale) const {
 
 	if (save_exr_func == NULL)

+ 6 - 2
core/image.h

@@ -47,6 +47,7 @@
 class Image;
 
 typedef Error (*SavePNGFunc)(const String &p_path, const Ref<Image> &p_img);
+typedef PoolVector<uint8_t> (*SavePNGBufferFunc)(const Ref<Image> &p_img);
 typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size);
 
 typedef Error (*SaveEXRFunc)(const String &p_path, const Ref<Image> &p_img, bool p_grayscale);
@@ -57,10 +58,12 @@ class Image : public Resource {
 public:
 	static SavePNGFunc save_png_func;
 	static SaveEXRFunc save_exr_func;
+	static SavePNGBufferFunc save_png_buffer_func;
 
 	enum {
-		MAX_WIDTH = 16384, // force a limit somehow
-		MAX_HEIGHT = 16384 // force a limit somehow
+		MAX_WIDTH = (1 << 24), // force a limit somehow
+		MAX_HEIGHT = (1 << 24), // force a limit somehow
+		MAX_PIXELS = 268435456
 	};
 
 	enum Format {
@@ -265,6 +268,7 @@ public:
 
 	Error load(const String &p_path);
 	Error save_png(const String &p_path) const;
+	PoolVector<uint8_t> save_png_to_buffer() const;
 	Error save_exr(const String &p_path, bool p_grayscale) const;
 
 	/**

+ 1 - 1
core/math/vector3i.h

@@ -183,7 +183,7 @@ bool Vector3i::operator==(const Vector3i &p_v) const {
 
 bool Vector3i::operator!=(const Vector3i &p_v) const {
 
-	return (x != p_v.x || y == p_v.y || z == p_v.z);
+	return (x != p_v.x || y != p_v.y || z != p_v.z);
 }
 
 bool Vector3i::operator<(const Vector3i &p_v) const {

+ 1 - 0
drivers/png/png_driver_common.cpp

@@ -114,6 +114,7 @@ Error png_to_image(const uint8_t *p_source, size_t p_size, Ref<Image> p_image) {
 	ERR_FAIL_COND_V_MSG(check_error(png_img), ERR_FILE_CORRUPT, png_img.message);
 	ERR_FAIL_COND_V(!success, ERR_FILE_CORRUPT);
 
+	//print_line("png width: "+itos(png_img.width)+" height: "+itos(png_img.height));
 	p_image->create(png_img.width, png_img.height, 0, dest_format, buffer);
 
 	return OK;

+ 9 - 0
drivers/png/resource_saver_png.cpp

@@ -71,6 +71,14 @@ Error ResourceSaverPNG::save_image(const String &p_path, const Ref<Image> &p_img
 	return OK;
 }
 
+PoolVector<uint8_t> ResourceSaverPNG::save_image_to_buffer(const Ref<Image> &p_img) {
+
+	PoolVector<uint8_t> buffer;
+	Error err = PNGDriverCommon::image_to_png(p_img, buffer);
+	ERR_FAIL_COND_V_MSG(err, PoolVector<uint8_t>(), "Can't convert image to PNG.");
+	return buffer;
+}
+
 bool ResourceSaverPNG::recognize(const RES &p_resource) const {
 
 	return (p_resource.is_valid() && p_resource->is_class("ImageTexture"));
@@ -86,4 +94,5 @@ void ResourceSaverPNG::get_recognized_extensions(const RES &p_resource, List<Str
 ResourceSaverPNG::ResourceSaverPNG() {
 
 	Image::save_png_func = &save_image;
+	Image::save_png_buffer_func = &save_image_to_buffer;
 };

+ 1 - 0
drivers/png/resource_saver_png.h

@@ -37,6 +37,7 @@
 class ResourceSaverPNG : public ResourceFormatSaver {
 public:
 	static Error save_image(const String &p_path, const Ref<Image> &p_img);
+	static PoolVector<uint8_t> save_image_to_buffer(const Ref<Image> &p_img);
 
 	virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
 	virtual bool recognize(const RES &p_resource) const;

+ 56 - 2
drivers/vulkan/rendering_device_vulkan.cpp

@@ -36,6 +36,8 @@
 #include "drivers/vulkan/vulkan_context.h"
 #include "thirdparty/spirv-reflect/spirv_reflect.h"
 
+//#define FORCE_FULL_BARRIER
+
 void RenderingDeviceVulkan::_add_dependency(RID p_id, RID p_depends_on) {
 
 	if (!dependency_map.has(p_depends_on)) {
@@ -1499,6 +1501,42 @@ void RenderingDeviceVulkan::_memory_barrier(VkPipelineStageFlags p_src_stage_mas
 	vkCmdPipelineBarrier(p_sync_with_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer, p_src_stage_mask, p_dst_stage_mask, 0, 1, &mem_barrier, 0, NULL, 0, NULL);
 }
 
+void RenderingDeviceVulkan::_full_barrier(bool p_sync_with_draw) {
+	//used for debug
+	_memory_barrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+			VK_ACCESS_INDIRECT_COMMAND_READ_BIT |
+					VK_ACCESS_INDEX_READ_BIT |
+					VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT |
+					VK_ACCESS_UNIFORM_READ_BIT |
+					VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
+					VK_ACCESS_SHADER_READ_BIT |
+					VK_ACCESS_SHADER_WRITE_BIT |
+					VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+					VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
+					VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
+					VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
+					VK_ACCESS_TRANSFER_READ_BIT |
+					VK_ACCESS_TRANSFER_WRITE_BIT |
+					VK_ACCESS_HOST_READ_BIT |
+					VK_ACCESS_HOST_WRITE_BIT,
+			VK_ACCESS_INDIRECT_COMMAND_READ_BIT |
+					VK_ACCESS_INDEX_READ_BIT |
+					VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT |
+					VK_ACCESS_UNIFORM_READ_BIT |
+					VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
+					VK_ACCESS_SHADER_READ_BIT |
+					VK_ACCESS_SHADER_WRITE_BIT |
+					VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+					VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
+					VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
+					VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
+					VK_ACCESS_TRANSFER_READ_BIT |
+					VK_ACCESS_TRANSFER_WRITE_BIT |
+					VK_ACCESS_HOST_READ_BIT |
+					VK_ACCESS_HOST_WRITE_BIT,
+			p_sync_with_draw);
+}
+
 void RenderingDeviceVulkan::_buffer_memory_barrier(VkBuffer buffer, uint64_t p_from, uint64_t p_size, VkPipelineStageFlags p_src_stage_mask, VkPipelineStageFlags p_dst_stage_mask, VkAccessFlags p_src_access, VkAccessFlags p_dst_sccess, bool p_sync_with_draw) {
 
 	VkBufferMemoryBarrier buffer_mem_barrier;
@@ -4643,7 +4681,11 @@ Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint
 	}
 
 	_buffer_memory_barrier(buffer->buffer, p_offset, p_size, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage_mask, VK_ACCESS_TRANSFER_WRITE_BIT, dst_access, p_sync_with_draw);
-
+#ifdef FORCE_FULL_BARRIER
+	_full_barrier(p_sync_with_draw);
+#else
+	_buffer_memory_barrier(buffer->buffer, p_offset, p_size, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage_mask, VK_ACCESS_TRANSFER_WRITE_BIT, dst_access, p_sync_with_draw);
+#endif
 	return err;
 }
 
@@ -6038,7 +6080,12 @@ void RenderingDeviceVulkan::draw_list_end() {
 	// To ensure proper synchronization, we must make sure rendering is done before:
 	//  * Some buffer is copied
 	//  * Another render pass happens (since we may be done
+
+#ifdef FORCE_FULL_BARRIER
+	_full_barrier(true);
+#else
 	_memory_barrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, true);
+#endif
 }
 
 /***********************/
@@ -6298,7 +6345,11 @@ void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t
 }
 
 void RenderingDeviceVulkan::compute_list_add_barrier(ComputeListID p_list) {
+#ifdef FORCE_FULL_BARRIER
+	_full_barrier(true);
+#else
 	_memory_barrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, true);
+#endif
 }
 
 void RenderingDeviceVulkan::compute_list_end() {
@@ -6330,8 +6381,11 @@ void RenderingDeviceVulkan::compute_list_end() {
 
 	memdelete(compute_list);
 	compute_list = NULL;
-
+#ifdef FORCE_FULL_BARRIER
+	_full_barrier(true);
+#else
 	_memory_barrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT, true);
+#endif
 }
 
 #if 0

+ 1 - 0
drivers/vulkan/rendering_device_vulkan.h

@@ -216,6 +216,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
 	Error _buffer_free(Buffer *p_buffer);
 	Error _buffer_update(Buffer *p_buffer, size_t p_offset, const uint8_t *p_data, size_t p_data_size, bool p_use_draw_command_buffer = false, uint32_t p_required_align = 32);
 
+	void _full_barrier(bool p_sync_with_draw);
 	void _memory_barrier(VkPipelineStageFlags p_src_stage_mask, VkPipelineStageFlags p_dst_stage_mask, VkAccessFlags p_src_access, VkAccessFlags p_dst_sccess, bool p_sync_with_draw);
 	void _buffer_memory_barrier(VkBuffer buffer, uint64_t p_from, uint64_t p_size, VkPipelineStageFlags p_src_stage_mask, VkPipelineStageFlags p_dst_stage_mask, VkAccessFlags p_src_access, VkAccessFlags p_dst_sccess, bool p_sync_with_draw);
 

+ 70 - 4
editor/plugins/gi_probe_editor_plugin.cpp

@@ -33,6 +33,18 @@
 void GIProbeEditorPlugin::_bake() {
 
 	if (gi_probe) {
+		if (gi_probe->get_probe_data().is_null()) {
+			String path = get_tree()->get_edited_scene_root()->get_filename();
+			if (path==String()) {
+				path="res://"+gi_probe->get_name()+"_data.res";
+			} else {
+				String ext = path.get_extension();
+				path = path.get_basename()+"."+gi_probe->get_name()+"_data.res";
+			}
+			probe_file->set_current_path(path);
+			probe_file->popup_centered_ratio();
+			return;
+		}
 		gi_probe->bake();
 	}
 }
@@ -51,13 +63,42 @@ bool GIProbeEditorPlugin::handles(Object *p_object) const {
 	return p_object->is_class("GIProbe");
 }
 
+void GIProbeEditorPlugin::_notification(int p_what) {
+
+	if (p_what==NOTIFICATION_PROCESS) {
+		if (!gi_probe) {
+			return;
+		}
+
+		String text;
+
+		Vector3i size = gi_probe->get_estimated_cell_size();
+		text = itos(size.x)+", "+itos(size.y)+", "+itos(size.z);
+		int data_size = 4;
+		if (GLOBAL_GET("rendering/quality/gi_probes/anisotropic")) {
+			data_size+=4;
+		}
+		text += " - VRAM Size: " + String::num(size.x*size.y*size.z*data_size/(1024.0*1024.0),2)+" Mb.";
+
+		if (bake_info->get_text()==text) {
+			return;
+		}
+
+		bake_info->add_color_override("font_color", bake_info->get_color("success_color", "Editor"));
+
+		bake_info->set_text(text);
+	}
+}
+
 void GIProbeEditorPlugin::make_visible(bool p_visible) {
 
 	if (p_visible) {
-		bake->show();
+		bake_hb->show();
+		set_process(true);
 	} else {
 
-		bake->hide();
+		bake_hb->hide();
+		set_process(false);
 	}
 }
 
@@ -82,21 +123,46 @@ void GIProbeEditorPlugin::bake_func_end() {
 	tmp_progress = NULL;
 }
 
+void GIProbeEditorPlugin::_giprobe_save_path_and_bake(const String& p_path) {
+	probe_file->hide();
+	if (gi_probe) {
+		gi_probe->bake();
+		ERR_FAIL_COND( gi_probe->get_probe_data().is_null() );
+		ResourceSaver::save(p_path,gi_probe->get_probe_data(),ResourceSaver::FLAG_CHANGE_PATH);
+	}
+}
+
 void GIProbeEditorPlugin::_bind_methods() {
 
 	ClassDB::bind_method("_bake", &GIProbeEditorPlugin::_bake);
+	ClassDB::bind_method("_giprobe_save_path_and_bake", &GIProbeEditorPlugin::_giprobe_save_path_and_bake);
+
 }
 
 GIProbeEditorPlugin::GIProbeEditorPlugin(EditorNode *p_node) {
 
 	editor = p_node;
+	bake_hb = memnew( HBoxContainer );
+	bake_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+	bake_hb->hide();
 	bake = memnew(ToolButton);
 	bake->set_icon(editor->get_gui_base()->get_icon("Bake", "EditorIcons"));
 	bake->set_text(TTR("Bake GI Probe"));
-	bake->hide();
 	bake->connect("pressed", this, "_bake");
-	add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake);
+	bake_hb->add_child(bake);
+	bake_info = memnew( Label );
+	bake_info->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+	bake_info->set_clip_text(true);
+	bake_hb->add_child(bake_info);
+
+	add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake_hb);
 	gi_probe = NULL;
+	probe_file = memnew( EditorFileDialog );
+	probe_file->set_mode(EditorFileDialog::MODE_SAVE_FILE);
+	probe_file->add_filter("*.res");
+	probe_file->connect("file_selected",this,"_giprobe_save_path_and_bake");
+	get_editor_interface()->get_base_control()->add_child(probe_file);
+	probe_file->set_title(TTR("Select path for GIProbe Data File"));
 
 	GIProbe::bake_begin_function = bake_func_begin;
 	GIProbe::bake_step_function = bake_func_step;

+ 7 - 0
editor/plugins/gi_probe_editor_plugin.h

@@ -42,18 +42,25 @@ class GIProbeEditorPlugin : public EditorPlugin {
 
 	GIProbe *gi_probe;
 
+	HBoxContainer *bake_hb;
+	Label *bake_info;
 	ToolButton *bake;
 	EditorNode *editor;
 
+	EditorFileDialog *probe_file;
+
 	static EditorProgress *tmp_progress;
 	static void bake_func_begin(int p_steps);
 	static void bake_func_step(int p_step, const String &p_description);
 	static void bake_func_end();
 
 	void _bake();
+	void _giprobe_save_path_and_bake(const String& p_path);
+
 
 protected:
 	static void _bind_methods();
+	void _notification(int p_what);
 
 public:
 	virtual String get_name() const { return "GIProbe"; }

+ 55 - 4
scene/3d/gi_probe.cpp

@@ -41,7 +41,7 @@ void GIProbeData::_set_data(const Dictionary &p_data) {
 	ERR_FAIL_COND(!p_data.has("octree_size"));
 	ERR_FAIL_COND(!p_data.has("octree_cells"));
 	ERR_FAIL_COND(!p_data.has("octree_data"));
-	ERR_FAIL_COND(!p_data.has("octree_df"));
+	ERR_FAIL_COND(!p_data.has("octree_df") && !p_data.has("octree_df_png"));
 	ERR_FAIL_COND(!p_data.has("level_counts"));
 	ERR_FAIL_COND(!p_data.has("to_cell_xform"));
 
@@ -49,7 +49,19 @@ void GIProbeData::_set_data(const Dictionary &p_data) {
 	Vector3 octree_size = p_data["octree_size"];
 	PoolVector<uint8_t> octree_cells = p_data["octree_cells"];
 	PoolVector<uint8_t> octree_data = p_data["octree_data"];
-	PoolVector<uint8_t> octree_df = p_data["octree_df"];
+
+	PoolVector<uint8_t> octree_df;
+	if (p_data.has("octree_df")) {
+		octree_df = p_data["octree_df"];
+	} else if (p_data.has("octree_df_png")) {
+		PoolVector<uint8_t> octree_df_png = p_data["octree_df_png"];
+		Ref<Image> img;
+		img.instance();
+		Error err = img->load_png_from_buffer(octree_df_png);
+		ERR_FAIL_COND(err != OK);
+		ERR_FAIL_COND(img->get_format() != Image::FORMAT_L8);
+		octree_df = img->get_data();
+	}
 	PoolVector<int> octree_levels = p_data["level_counts"];
 	Transform to_cell_xform = p_data["to_cell_xform"];
 
@@ -59,10 +71,21 @@ void GIProbeData::_set_data(const Dictionary &p_data) {
 Dictionary GIProbeData::_get_data() const {
 	Dictionary d;
 	d["bounds"] = get_bounds();
-	d["octree_size"] = get_octree_size();
+	Vector3i otsize = get_octree_size();
+	d["octree_size"] = Vector3(otsize);
 	d["octree_cells"] = get_octree_cells();
 	d["octree_data"] = get_data_cells();
-	d["octree_df"] = get_distance_field();
+	if (otsize != Vector3i()) {
+		Ref<Image> img;
+		img.instance();
+		img->create(otsize.x * otsize.y, otsize.z, false, Image::FORMAT_L8, get_distance_field());
+		PoolVector<uint8_t> df_png = img->save_png_to_buffer();
+		ERR_FAIL_COND_V(df_png.size() == 0, Dictionary());
+		d["octree_df_png"] = df_png;
+	} else {
+		d["octree_df"] = PoolVector<uint8_t>();
+	}
+
 	d["level_counts"] = get_level_counts();
 	d["to_cell_xform"] = get_to_cell_xform();
 	return d;
@@ -384,6 +407,32 @@ GIProbe::BakeBeginFunc GIProbe::bake_begin_function = NULL;
 GIProbe::BakeStepFunc GIProbe::bake_step_function = NULL;
 GIProbe::BakeEndFunc GIProbe::bake_end_function = NULL;
 
+Vector3i GIProbe::get_estimated_cell_size() const {
+	static const int subdiv_value[SUBDIV_MAX] = { 6, 7, 8, 9 };
+	int cell_subdiv = subdiv_value[subdiv];
+	int axis_cell_size[3];
+	AABB bounds = AABB(-extents, extents * 2.0);
+	int longest_axis = bounds.get_longest_axis_index();
+	axis_cell_size[longest_axis] = 1 << cell_subdiv;
+
+
+	for (int i = 0; i < 3; i++) {
+
+		if (i == longest_axis)
+			continue;
+
+		axis_cell_size[i] = axis_cell_size[longest_axis];
+		float axis_size = bounds.size[longest_axis];
+
+		//shrink until fit subdiv
+		while (axis_size / 2.0 >= bounds.size[i]) {
+			axis_size /= 2.0;
+			axis_cell_size[i] >>= 1;
+		}
+	}
+
+	return Vector3i(axis_cell_size[0],axis_cell_size[1],axis_cell_size[2]);
+}
 void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) {
 
 	static const int subdiv_value[SUBDIV_MAX] = { 6, 7, 8, 9 };
@@ -458,6 +507,8 @@ void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) {
 	if (bake_end_function) {
 		bake_end_function();
 	}
+
+	_change_notify(); //bake property may have changed
 }
 
 void GIProbe::_debug_bake() {

+ 1 - 0
scene/3d/gi_probe.h

@@ -159,6 +159,7 @@ public:
 
 	void set_extents(const Vector3 &p_extents);
 	Vector3 get_extents() const;
+	Vector3i get_estimated_cell_size() const;
 
 	void bake(Node *p_from_node = NULL, bool p_create_visual_debug = false);
 

+ 1 - 0
scene/3d/mesh_instance.cpp

@@ -409,6 +409,7 @@ void MeshInstance::_bind_methods() {
 	ClassDB::set_method_flags("MeshInstance", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
 
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
+	ADD_GROUP("Skeleton", "");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "skin", PROPERTY_HINT_RESOURCE_TYPE, "Skin"), "set_skin", "get_skin");
 	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton"), "set_skeleton_path", "get_skeleton_path");
 }

+ 6 - 3
servers/visual/rasterizer_rd/rasterizer_scene_forward_rd.cpp

@@ -2363,11 +2363,14 @@ void RasterizerSceneForwardRD::_setup_render_pass_uniform_set(RID p_depth_buffer
 		RD::Uniform u;
 		u.binding = 6;
 		u.type = RD::UNIFORM_TYPE_TEXTURE;
+		RID texture;
 		if (p_shadow_atlas.is_valid()) {
-			u.ids.push_back(shadow_atlas_get_texture(p_shadow_atlas));
-		} else {
-			u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE));
+			texture = shadow_atlas_get_texture(p_shadow_atlas);
 		}
+		if (!texture.is_valid()) {
+			texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE);
+		}
+		u.ids.push_back(texture);
 		uniforms.push_back(u);
 	}
 

+ 1 - 0
servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp

@@ -2334,6 +2334,7 @@ void RasterizerSceneRD::render_shadow(RID p_light, RID p_shadow_atlas, int p_pas
 		}
 	} else {
 		//render shadow
+
 		_render_shadow(render_fb, p_cull_result, p_cull_count, light_projection, light_transform, zfar, bias, normal_bias, using_dual_paraboloid, using_dual_paraboloid_flip);
 
 		//copy to atlas

+ 1 - 1
servers/visual/rasterizer_rd/shaders/scene_forward.glsl

@@ -266,7 +266,7 @@ VERTEX_SHADER_CODE
 #else
 
 	float z_ofs = scene_data.z_offset;
-	z_ofs += (1.0 - abs(normal_interp.z)) * scene_data.z_slope_scale;
+	z_ofs += max(0.0,1.0 - abs(normalize(normal_interp).z)) * scene_data.z_slope_scale;
 	vertex_interp.z -= z_ofs;
 
 #endif

+ 4 - 1
servers/visual/visual_server_scene.cpp

@@ -851,7 +851,7 @@ void VisualServerScene::instance_geometry_set_flag(RID p_instance, VS::InstanceF
 	Instance *instance = instance_owner.getornull(p_instance);
 	ERR_FAIL_COND(!instance);
 
-	ERR_FAIL_COND(((1 << instance->base_type) & VS::INSTANCE_GEOMETRY_MASK));
+	//ERR_FAIL_COND(((1 << instance->base_type) & VS::INSTANCE_GEOMETRY_MASK));
 
 	switch (p_flags) {
 
@@ -2526,6 +2526,9 @@ void VisualServerScene::render_probes() {
 		for (List<InstanceGIProbeData::PairInfo>::Element *E = probe->dynamic_geometries.front(); E; E = E->next()) {
 			if (instance_cull_count < MAX_INSTANCE_CULL) {
 				Instance *ins = E->get().geometry;
+				if (!ins->visible) {
+					continue;
+				}
 				InstanceGeometryData *geom = (InstanceGeometryData *)ins->base_data;
 
 				if (geom->gi_probes_dirty) {