2
0
Эх сурвалжийг харах

More GIProbe work and fixes

Juan Linietsky 5 жил өмнө
parent
commit
6ee2f5e6b6

+ 4 - 0
core/engine.cpp

@@ -214,6 +214,9 @@ Engine *Engine::get_singleton() {
 	return singleton;
 }
 
+bool Engine::is_abort_on_gpu_errors_enabled() const {
+	return abort_on_gpu_errors;
+}
 Engine::Engine() {
 
 	singleton = this;
@@ -232,4 +235,5 @@ Engine::Engine() {
 	_frame_ticks = 0;
 	_frame_step = 0;
 	editor_hint = false;
+	abort_on_gpu_errors = false;
 }

+ 3 - 0
core/engine.h

@@ -64,6 +64,7 @@ private:
 	bool _pixel_snap;
 	uint64_t _physics_frames;
 	float _physics_interpolation_fraction;
+	bool abort_on_gpu_errors;
 
 	uint64_t _idle_frames;
 	bool _in_physics;
@@ -126,6 +127,8 @@ public:
 	Dictionary get_license_info() const;
 	String get_license_text() const;
 
+	bool is_abort_on_gpu_errors_enabled() const;
+
 	Engine();
 	virtual ~Engine() {}
 };

+ 106 - 69
drivers/vulkan/rendering_device_vulkan.cpp

@@ -1250,7 +1250,7 @@ Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size,
 	allocInfo.pUserData = NULL;
 
 	VkResult err = vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &p_buffer->buffer, &p_buffer->allocation, NULL);
-	ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
+	ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Can't create buffer of size: " + itos(p_size));
 	p_buffer->size = p_size;
 	p_buffer->buffer_info.buffer = p_buffer->buffer;
 	p_buffer->buffer_info.offset = 0;
@@ -1857,6 +1857,8 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T
 
 RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID p_with_texture) {
 
+	_THREAD_SAFE_METHOD_
+
 	Texture *src_texture = texture_owner.getornull(p_with_texture);
 	ERR_FAIL_COND_V(!src_texture, RID());
 
@@ -1938,6 +1940,8 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID
 
 RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, TextureSliceType p_slice_type) {
 
+	_THREAD_SAFE_METHOD_
+
 	Texture *src_texture = texture_owner.getornull(p_with_texture);
 	ERR_FAIL_COND_V(!src_texture, RID());
 
@@ -2314,6 +2318,9 @@ PoolVector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture
 }
 
 PoolVector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t p_layer) {
+
+	_THREAD_SAFE_METHOD_
+
 	Texture *tex = texture_owner.getornull(p_texture);
 	ERR_FAIL_COND_V(!tex, PoolVector<uint8_t>());
 
@@ -2416,6 +2423,10 @@ PoolVector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint3
 
 		{
 
+			//despite textures being in block sizes, spec requiers they are in pixel sizes (?)
+			uint32_t computed_w = tex->width;
+			uint32_t computed_h = tex->height;
+
 			for (uint32_t i = 0; i < tex->mipmaps; i++) {
 
 				uint32_t mm_width, mm_height, mm_depth;
@@ -2438,11 +2449,14 @@ PoolVector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint3
 				image_copy_region.dstOffset.y = 0;
 				image_copy_region.dstOffset.z = 0;
 
-				image_copy_region.extent.width = mm_width;
-				image_copy_region.extent.height = mm_height;
-				image_copy_region.extent.depth = mm_depth;
+				image_copy_region.extent.width = computed_w;
+				image_copy_region.extent.height = computed_h;
+				image_copy_region.extent.depth = mm_depth; //block is only x,y so this is fine anyway
 
 				vkCmdCopyImage(command_buffer, tex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy_region);
+
+				computed_w = MAX(1, computed_w >> 1);
+				computed_h = MAX(1, computed_h >> 1);
 			}
 		}
 
@@ -2500,6 +2514,8 @@ PoolVector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint3
 }
 
 bool RenderingDeviceVulkan::texture_is_shared(RID p_texture) {
+	_THREAD_SAFE_METHOD_
+
 	Texture *tex = texture_owner.getornull(p_texture);
 	ERR_FAIL_COND_V(!tex, false);
 	return tex->owner.is_valid();
@@ -2511,6 +2527,8 @@ bool RenderingDeviceVulkan::texture_is_valid(RID p_texture) {
 
 Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, bool p_sync_with_draw) {
 
+	_THREAD_SAFE_METHOD_
+
 	Texture *src_tex = texture_owner.getornull(p_from_texture);
 	ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER);
 
@@ -2681,6 +2699,8 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture,
 
 Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, bool p_sync_with_draw) {
 
+	_THREAD_SAFE_METHOD_
+
 	Texture *src_tex = texture_owner.getornull(p_texture);
 	ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER);
 
@@ -4010,6 +4030,8 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages
 }
 
 uint32_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) {
+	_THREAD_SAFE_METHOD_
+
 	const Shader *shader = shader_owner.getornull(p_shader);
 	ERR_FAIL_COND_V(!shader, 0);
 	return shader->vertex_input_mask;
@@ -4690,6 +4712,8 @@ Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint
 
 PoolVector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) {
 
+	_THREAD_SAFE_METHOD_
+
 	Buffer *buffer = NULL;
 	if (vertex_buffer_owner.owns(p_buffer)) {
 		buffer = vertex_buffer_owner.getornull(p_buffer);
@@ -6077,7 +6101,7 @@ 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
-	_memory_barrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT, true);
+	_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);
 }
 
 /***********************/
@@ -6284,9 +6308,12 @@ void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t
 	ComputeList *cl = compute_list;
 
 #ifdef DEBUG_ENABLED
-	ERR_FAIL_COND(p_x_groups > limits.maxComputeWorkGroupCount[0]);
-	ERR_FAIL_COND(p_y_groups > limits.maxComputeWorkGroupCount[1]);
-	ERR_FAIL_COND(p_z_groups > limits.maxComputeWorkGroupCount[2]);
+	ERR_FAIL_COND_MSG(p_x_groups > limits.maxComputeWorkGroupCount[0],
+			"Dispatch amount of X compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[0]) + ")");
+	ERR_FAIL_COND_MSG(p_y_groups > limits.maxComputeWorkGroupCount[1],
+			"Dispatch amount of Y compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[0]) + ")");
+	ERR_FAIL_COND_MSG(p_z_groups > limits.maxComputeWorkGroupCount[2],
+			"Dispatch amount of Z compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[0]) + ")");
 
 	ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified.");
 #endif
@@ -6500,24 +6527,75 @@ void RenderingDeviceVulkan::free(RID p_id) {
 	_free_dependencies(p_id); //recursively erase dependencies first, to avoid potential API problems
 	_free_internal(p_id);
 }
-
-void RenderingDeviceVulkan::finalize_frame() {
+void RenderingDeviceVulkan::swap_buffers() {
 
 	_THREAD_SAFE_METHOD_
 
-	if (draw_list) {
-		ERR_PRINT("Found open draw list at the end of the frame, this should never happen (further drawing will likely not work).");
-	}
+	{ //finalize frame
 
-	if (compute_list) {
-		ERR_PRINT("Found open compute list at the end of the frame, this should never happen (further compute will likely not work).");
+		if (draw_list) {
+			ERR_PRINT("Found open draw list at the end of the frame, this should never happen (further drawing will likely not work).");
+		}
+
+		if (compute_list) {
+			ERR_PRINT("Found open compute list at the end of the frame, this should never happen (further compute will likely not work).");
+		}
+
+		{ //complete the setup buffer (that needs to be processed before anything else)
+			vkEndCommandBuffer(frames[frame].setup_command_buffer);
+			vkEndCommandBuffer(frames[frame].draw_command_buffer);
+		}
+		screen_prepared = false;
 	}
 
-	{ //complete the setup buffer (that needs to be processed before anything else)
-		vkEndCommandBuffer(frames[frame].setup_command_buffer);
-		vkEndCommandBuffer(frames[frame].draw_command_buffer);
+	//swap buffers
+	context->swap_buffers();
+
+	{ //advance frame
+
+		frame = (frame + 1) % frame_count;
+
+		//erase pending resources
+		_free_pending_resources(frame);
+
+		//create setup command buffer and set as the setup buffer
+
+		{
+			VkCommandBufferBeginInfo cmdbuf_begin;
+			cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+			cmdbuf_begin.pNext = NULL;
+			cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+			cmdbuf_begin.pInheritanceInfo = NULL;
+
+			VkResult err = vkResetCommandBuffer(frames[frame].setup_command_buffer, 0);
+			ERR_FAIL_COND(err);
+
+			err = vkBeginCommandBuffer(frames[frame].setup_command_buffer, &cmdbuf_begin);
+			ERR_FAIL_COND(err);
+			context->set_setup_buffer(frames[frame].setup_command_buffer); //append now so it's added before everything else
+			err = vkBeginCommandBuffer(frames[frame].draw_command_buffer, &cmdbuf_begin);
+			ERR_FAIL_COND(err);
+			context->append_command_buffer(frames[frame].draw_command_buffer);
+		}
+
+		//advance current frame
+		frames_drawn++;
+		//advance staging buffer if used
+		if (staging_buffer_used) {
+			staging_buffer_current = (staging_buffer_current + 1) % staging_buffer_blocks.size();
+			staging_buffer_used = false;
+		}
+
+		if (frames[frame].timestamp_count) {
+			vkGetQueryPoolResults(device, frames[frame].timestamp_pool, 0, frames[frame].timestamp_count, sizeof(uint64_t) * max_timestamp_query_elements, frames[frame].timestamp_result_values, sizeof(uint64_t), VK_QUERY_RESULT_64_BIT);
+			SWAP(frames[frame].timestamp_names, frames[frame].timestamp_result_names);
+			SWAP(frames[frame].timestamp_cpu_values, frames[frame].timestamp_cpu_result_values);
+		}
+
+		frames[frame].timestamp_result_count = frames[frame].timestamp_count;
+		frames[frame].timestamp_count = 0;
+		frames[frame].index = Engine::get_singleton()->get_frames_drawn();
 	}
-	screen_prepared = false;
 }
 
 void RenderingDeviceVulkan::_free_pending_resources(int p_frame) {
@@ -6630,55 +6708,6 @@ void RenderingDeviceVulkan::prepare_screen_for_drawing() {
 	screen_prepared = true;
 }
 
-void RenderingDeviceVulkan::advance_frame() {
-
-	_THREAD_SAFE_METHOD_
-
-	//advance the frame
-	frame = (frame + 1) % frame_count;
-
-	//erase pending resources
-	_free_pending_resources(frame);
-
-	//create setup command buffer and set as the setup buffer
-
-	{
-		VkCommandBufferBeginInfo cmdbuf_begin;
-		cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
-		cmdbuf_begin.pNext = NULL;
-		cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
-		cmdbuf_begin.pInheritanceInfo = NULL;
-
-		VkResult err = vkResetCommandBuffer(frames[frame].setup_command_buffer, 0);
-		ERR_FAIL_COND(err);
-
-		err = vkBeginCommandBuffer(frames[frame].setup_command_buffer, &cmdbuf_begin);
-		ERR_FAIL_COND(err);
-		context->set_setup_buffer(frames[frame].setup_command_buffer); //append now so it's added before everything else
-		err = vkBeginCommandBuffer(frames[frame].draw_command_buffer, &cmdbuf_begin);
-		ERR_FAIL_COND(err);
-		context->append_command_buffer(frames[frame].draw_command_buffer);
-	}
-
-	//advance current frame
-	frames_drawn++;
-	//advance staging buffer if used
-	if (staging_buffer_used) {
-		staging_buffer_current = (staging_buffer_current + 1) % staging_buffer_blocks.size();
-		staging_buffer_used = false;
-	}
-
-	if (frames[frame].timestamp_count) {
-		vkGetQueryPoolResults(device, frames[frame].timestamp_pool, 0, frames[frame].timestamp_count, sizeof(uint64_t) * max_timestamp_query_elements, frames[frame].timestamp_result_values, sizeof(uint64_t), VK_QUERY_RESULT_64_BIT);
-		SWAP(frames[frame].timestamp_names, frames[frame].timestamp_result_names);
-		SWAP(frames[frame].timestamp_cpu_values, frames[frame].timestamp_cpu_result_values);
-	}
-
-	frames[frame].timestamp_result_count = frames[frame].timestamp_count;
-	frames[frame].timestamp_count = 0;
-	frames[frame].index = Engine::get_singleton()->get_frames_drawn();
-}
-
 uint32_t RenderingDeviceVulkan::get_frame_delay() const {
 	return frame_count;
 }
@@ -6793,7 +6822,7 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context) {
 
 	{
 		//begin the first command buffer for the first frame, so
-		//setting up things can be done in the meantime until finalize_frame(), which is called before advance.
+		//setting up things can be done in the meantime until swap_buffers(), which is called before advance.
 		VkCommandBufferBeginInfo cmdbuf_begin;
 		cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
 		cmdbuf_begin.pNext = NULL;
@@ -6957,6 +6986,14 @@ int RenderingDeviceVulkan::limit_get(Limit p_limit) {
 		case LIMIT_MAX_VERTEX_INPUT_BINDINGS: return limits.maxVertexInputBindings;
 		case LIMIT_MAX_VERTEX_INPUT_BINDING_STRIDE: return limits.maxVertexInputBindingStride;
 		case LIMIT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT: return limits.minUniformBufferOffsetAlignment;
+		case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X: return limits.maxComputeWorkGroupCount[0];
+		case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Y: return limits.maxComputeWorkGroupCount[1];
+		case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Z: return limits.maxComputeWorkGroupCount[2];
+		case LIMIT_MAX_COMPUTE_WORKGROUP_INVOCATIONS: return limits.maxComputeWorkGroupInvocations;
+		case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_X: return limits.maxComputeWorkGroupSize[0];
+		case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Y: return limits.maxComputeWorkGroupSize[1];
+		case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z: return limits.maxComputeWorkGroupSize[2];
+
 		default: ERR_FAIL_V(0);
 	}
 

+ 1 - 2
drivers/vulkan/rendering_device_vulkan.h

@@ -1116,8 +1116,7 @@ public:
 	void initialize(VulkanContext *p_context);
 	void finalize();
 
-	virtual void finalize_frame();
-	virtual void advance_frame();
+	virtual void swap_buffers();
 
 	virtual uint32_t get_frame_delay() const;
 

+ 5 - 1
drivers/vulkan/vulkan_context.cpp

@@ -29,6 +29,7 @@
 /*************************************************************************/
 
 #include "vulkan_context.h"
+#include "core/engine.h"
 #include "core/print_string.h"
 #include "core/project_settings.h"
 #include "core/version.h"
@@ -36,6 +37,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
 #define VULKAN_DEBUG(m_text) print_line(m_text)
 #define APP_SHORT_NAME "GodotEngine"
@@ -121,7 +123,9 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback(VkDebugU
 
 	free(message);
 
-	//	abort();
+	if (Engine::get_singleton()->is_abort_on_gpu_errors_enabled()) {
+		abort();
+	}
 	// Don't bail out, but keep going.
 	return false;
 }

+ 6 - 2
editor/plugins/spatial_editor_plugin.cpp

@@ -2767,7 +2767,8 @@ void SpatialEditorViewport::_menu_option(int p_option) {
 		case VIEW_DISPLAY_DEBUG_SHADOW_ATLAS:
 		case VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS:
 		case VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO:
-		case VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING: {
+		case VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING:
+		case VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION: {
 
 			static const int display_options[] = {
 				VIEW_DISPLAY_NORMAL,
@@ -2780,6 +2781,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
 				VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS,
 				VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO,
 				VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING,
+				VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION,
 				VIEW_MAX
 			};
 			static const Viewport::DebugDraw debug_draw_modes[] = {
@@ -2792,7 +2794,8 @@ void SpatialEditorViewport::_menu_option(int p_option) {
 				Viewport::DEBUG_DRAW_SHADOW_ATLAS,
 				Viewport::DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS,
 				Viewport::DEBUG_DRAW_GI_PROBE_ALBEDO,
-				Viewport::DEBUG_DRAW_GI_PROBE_LIGHTING
+				Viewport::DEBUG_DRAW_GI_PROBE_LIGHTING,
+				Viewport::DEBUG_DRAW_GI_PROBE_EMISSION
 			};
 
 			int idx = 0;
@@ -3635,6 +3638,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
 	display_submenu->add_separator();
 	display_submenu->add_radio_check_item(TTR("GIProbe Lighting"), VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING);
 	display_submenu->add_radio_check_item(TTR("GIProbe Albedo"), VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO);
+	display_submenu->add_radio_check_item(TTR("GIProbe Emission"), VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION);
 	display_submenu->set_name("display_advanced");
 	view_menu->get_popup()->add_submenu_item(TTR("Display Advanced..."), "display_advanced");
 	view_menu->get_popup()->add_separator();

+ 1 - 0
editor/plugins/spatial_editor_plugin.h

@@ -172,6 +172,7 @@ class SpatialEditorViewport : public Control {
 		VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS,
 		VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO,
 		VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING,
+		VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION,
 		VIEW_LOCK_ROTATION,
 		VIEW_CINEMATIC_PREVIEW,
 		VIEW_MAX

+ 4 - 0
main/main.cpp

@@ -286,6 +286,7 @@ void Main::print_help(const char *p_binary) {
 	OS::get_singleton()->print("  -d, --debug                      Debug (local stdout debugger).\n");
 	OS::get_singleton()->print("  -b, --breakpoints                Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n");
 	OS::get_singleton()->print("  --profiling                      Enable profiling in the script debugger.\n");
+	OS::get_singleton()->print("  --gpu-abort                      Abort on GPU errors (usually validation layer errors), may help see the problem if your system freezes.\n");
 	OS::get_singleton()->print("  --remote-debug <address>         Remote debug (<host/IP>:<port> address).\n");
 #if defined(DEBUG_ENABLED) && !defined(SERVER_ENABLED)
 	OS::get_singleton()->print("  --debug-collisions               Show collision shapes when running the scene.\n");
@@ -548,6 +549,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 		} else if (I->get() == "-w" || I->get() == "--windowed") { // force windowed window
 
 			init_windowed = true;
+		} else if (I->get() == "--gpu-abort") { // force windowed window
+
+			Engine::singleton->abort_on_gpu_errors = true;
 		} else if (I->get() == "-t" || I->get() == "--always-on-top") { // force always-on-top window
 
 			init_always_on_top = true;

+ 2 - 2
platform/x11/os_x11.cpp

@@ -872,7 +872,6 @@ void OS_X11::finalize() {
 
 		if (context_gles2)
 			memdelete(context_gles2);
-
 	}
 #endif
 #if defined(VULKAN_ENABLED)
@@ -3146,11 +3145,12 @@ void OS_X11::swap_buffers() {
 		context_gles2->swap_buffers();
 	}
 #endif
+	/* not needed for now
 #if defined(VULKAN_ENABLED)
 	if (video_driver_index == VIDEO_DRIVER_VULKAN) {
 		context_vulkan->swap_buffers();
 	}
-#endif
+#endif*/
 }
 
 void OS_X11::alert(const String &p_alert, const String &p_title) {

+ 1 - 1
scene/3d/cpu_particles.cpp

@@ -1144,7 +1144,7 @@ void CPUParticles::_update_render_thread() {
 	update_mutex->lock();
 #endif
 	if (can_update) {
-		VS::get_singleton()->multimesh_set_buffer(multimesh, particle_data);
+		//VS::get_singleton()->multimesh_set_buffer(multimesh, particle_data);
 		can_update = false; //wait for next time
 	}
 

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

@@ -409,6 +409,7 @@ void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) {
 		probe_data->allocate(baker.get_to_cell_space_xform(), AABB(-extents, extents * 2.0), baker.get_giprobe_octree_size(), baker.get_giprobe_octree_cells(), baker.get_giprobe_data_cells(), baker.get_giprobe_level_cell_count());
 
 		set_probe_data(probe_data);
+		probe_data->set_edited(true); //so it gets saved
 	}
 
 	if (bake_end_function) {

+ 1 - 0
scene/main/viewport.h

@@ -133,6 +133,7 @@ public:
 		DEBUG_DRAW_WIREFRAME,
 		DEBUG_DRAW_GI_PROBE_ALBEDO,
 		DEBUG_DRAW_GI_PROBE_LIGHTING,
+		DEBUG_DRAW_GI_PROBE_EMISSION,
 		DEBUG_DRAW_SHADOW_ATLAS,
 		DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS,
 

+ 2 - 4
servers/visual/rasterizer_rd/rasterizer_rd.cpp

@@ -84,12 +84,10 @@ void RasterizerRD::begin_frame(double frame_step) {
 
 void RasterizerRD::end_frame(bool p_swap_buffers) {
 
-	RD::get_singleton()->finalize_frame();
 #ifndef _MSC_VER
-#warning not swapping buffers likely not an option for now, find another way
+#warning TODO: likely passa bool to swap buffers to avoid display?
 #endif
-	OS::get_singleton()->swap_buffers(); //probably should pass some bool to avoid display?
-	RD::get_singleton()->advance_frame(); //advance frame here, so any new call happens on new frame
+	RD::get_singleton()->swap_buffers(); //probably should pass some bool to avoid display?
 }
 
 void RasterizerRD::initialize() {

+ 7 - 2
servers/visual/rasterizer_rd/rasterizer_scene_forward_rd.cpp

@@ -1899,7 +1899,7 @@ void RasterizerSceneForwardRD::_render_scene(RenderBufferData *p_buffer_data, co
 	_fill_instances(render_list.elements, render_list.element_count);
 
 	bool can_continue = true; //unless the middle buffers are needed
-	bool debug_giprobes = debug_draw == VS::VIEWPORT_DEBUG_DRAW_GI_PROBE_ALBEDO || debug_draw == VS::VIEWPORT_DEBUG_DRAW_GI_PROBE_LIGHTING;
+	bool debug_giprobes = debug_draw == VS::VIEWPORT_DEBUG_DRAW_GI_PROBE_ALBEDO || debug_draw == VS::VIEWPORT_DEBUG_DRAW_GI_PROBE_LIGHTING || debug_draw == VS::VIEWPORT_DEBUG_DRAW_GI_PROBE_EMISSION;
 	bool using_separate_specular = false;
 
 	bool depth_pre_pass = depth_framebuffer.is_valid();
@@ -1930,7 +1930,7 @@ void RasterizerSceneForwardRD::_render_scene(RenderBufferData *p_buffer_data, co
 		CameraMatrix cm = (dc * p_cam_projection) * CameraMatrix(p_cam_transform.affine_inverse());
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(opaque_framebuffer, RD::INITIAL_ACTION_CONTINUE, will_continue ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, will_continue ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ);
 		for (int i = 0; i < p_gi_probe_cull_count; i++) {
-			_debug_giprobe(p_gi_probe_cull_result[i], draw_list, opaque_framebuffer, cm, debug_draw == VS::VIEWPORT_DEBUG_DRAW_GI_PROBE_LIGHTING, 1.0);
+			_debug_giprobe(p_gi_probe_cull_result[i], draw_list, opaque_framebuffer, cm, debug_draw == VS::VIEWPORT_DEBUG_DRAW_GI_PROBE_LIGHTING, debug_draw == VS::VIEWPORT_DEBUG_DRAW_GI_PROBE_EMISSION, 1.0);
 		}
 		RD::get_singleton()->draw_list_end();
 	}
@@ -2204,10 +2204,15 @@ void RasterizerSceneForwardRD::_update_render_base_uniform_set() {
 			} else {
 				u.ids.resize(slot_count);
 			}
+
+			print_line("updating slots, probe count: " + itos(slot_count));
 			for (int i = 0; i < slot_count; i++) {
 
 				RID probe = gi_probe_get_slots()[i];
 
+				if (probe.is_valid()) {
+					print_line("probe valid: " + itos(i));
+				}
 				if (gi_probe_is_anisotropic()) {
 					if (probe.is_null()) {
 						RID empty_tex = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE);

+ 22 - 8
servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp

@@ -1439,6 +1439,7 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, const Vector<RID> &p_light_
 		}
 
 		gi_probe->last_probe_data_version = data_version;
+		gi_probe_slots_dirty = true;
 	}
 
 	// UDPDATE TIME
@@ -1510,6 +1511,8 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, const Vector<RID> &p_light_
 		RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
 
 		int passes = storage->gi_probe_is_using_two_bounces(gi_probe->probe) ? 2 : 1;
+		int wg_size = 64;
+		int wg_limit_x = RD::get_singleton()->limit_get(RD::LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X);
 
 		for (int pass = 0; pass < passes; pass++) {
 
@@ -1532,9 +1535,14 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, const Vector<RID> &p_light_
 				push_constant.cell_offset = gi_probe->mipmaps[i].cell_offset;
 				push_constant.cell_count = gi_probe->mipmaps[i].cell_count;
 
-				RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbePushConstant));
-
-				RD::get_singleton()->compute_list_dispatch(compute_list, (gi_probe->mipmaps[i].cell_count - 1) / 64 + 1, 1, 1);
+				int wg_todo = (gi_probe->mipmaps[i].cell_count - 1) / wg_size + 1;
+				while (wg_todo) {
+					int wg_count = MIN(wg_todo, wg_limit_x);
+					RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbePushConstant));
+					RD::get_singleton()->compute_list_dispatch(compute_list, wg_count, 1, 1);
+					wg_todo -= wg_count;
+					push_constant.cell_offset += wg_count * wg_size;
+				}
 			}
 
 			RD::get_singleton()->compute_list_add_barrier(compute_list); //wait til previous step is done
@@ -1548,9 +1556,14 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, const Vector<RID> &p_light_
 				push_constant.cell_offset = gi_probe->mipmaps[i].cell_offset;
 				push_constant.cell_count = gi_probe->mipmaps[i].cell_count;
 
-				RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbePushConstant));
-
-				RD::get_singleton()->compute_list_dispatch(compute_list, (gi_probe->mipmaps[i].cell_count - 1) / 64 + 1, 1, 1);
+				int wg_todo = (gi_probe->mipmaps[i].cell_count - 1) / wg_size + 1;
+				while (wg_todo) {
+					int wg_count = MIN(wg_todo, wg_limit_x);
+					RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbePushConstant));
+					RD::get_singleton()->compute_list_dispatch(compute_list, wg_count, 1, 1);
+					wg_todo -= wg_count;
+					push_constant.cell_offset += wg_count * wg_size;
+				}
 			}
 		}
 
@@ -1560,7 +1573,7 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, const Vector<RID> &p_light_
 	gi_probe->last_probe_version = storage->gi_probe_get_version(gi_probe->probe);
 }
 
-void RasterizerSceneRD::_debug_giprobe(RID p_gi_probe, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, float p_alpha) {
+void RasterizerSceneRD::_debug_giprobe(RID p_gi_probe, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha) {
 	GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_gi_probe);
 	ERR_FAIL_COND(!gi_probe);
 
@@ -1631,7 +1644,7 @@ void RasterizerSceneRD::_debug_giprobe(RID p_gi_probe, RD::DrawListID p_draw_lis
 	}
 
 	giprobe_debug_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_debug_shader_version_shaders[0], 0);
-	RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, giprobe_debug_shader_version_pipelines[p_lighting ? GI_PROBE_DEBUG_LIGHT : GI_PROBE_DEBUG_COLOR].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));
+	RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, giprobe_debug_shader_version_pipelines[p_emission ? GI_PROBE_DEBUG_EMISSION : p_lighting ? GI_PROBE_DEBUG_LIGHT : GI_PROBE_DEBUG_COLOR].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));
 	RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, giprobe_debug_uniform_set, 0);
 	RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(GIProbeDebugPushConstant));
 	RD::get_singleton()->draw_list_draw(p_draw_list, false, cell_count, 36);
@@ -2007,6 +2020,7 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) {
 		Vector<String> versions;
 		versions.push_back("\n#define MODE_DEBUG_COLOR\n");
 		versions.push_back("\n#define MODE_DEBUG_LIGHT\n");
+		versions.push_back("\n#define MODE_DEBUG_EMISSION\n");
 
 		giprobe_debug_shader.initialize(versions, defines);
 		giprobe_debug_shader_version = giprobe_debug_shader.version_create();

+ 2 - 1
servers/visual/rasterizer_rd/rasterizer_scene_rd.h

@@ -27,7 +27,7 @@ protected:
 	virtual void _render_scene(RenderBufferData *p_buffer_data, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) = 0;
 	virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool use_dp_flip) = 0;
 
-	virtual void _debug_giprobe(RID p_gi_probe, RenderingDevice::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, float p_alpha);
+	virtual void _debug_giprobe(RID p_gi_probe, RenderingDevice::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha);
 
 private:
 	int roughness_layers;
@@ -210,6 +210,7 @@ private:
 	enum {
 		GI_PROBE_DEBUG_COLOR,
 		GI_PROBE_DEBUG_LIGHT,
+		GI_PROBE_DEBUG_EMISSION,
 		GI_PROBE_DEBUG_MAX
 	};
 

+ 13 - 13
servers/visual/rasterizer_rd/shaders/giprobe.glsl

@@ -248,7 +248,7 @@ void main() {
 
 	vec3 pos = vec3(posu) + vec3(0.5);
 
-	vec3 emission = vec3(ivec3(cell_data.data[cell_index].emission&0x3FF,(cell_data.data[cell_index].emission>>10)&0x7FF,cell_data.data[cell_index].emission>>21)) * params.emission_scale;
+	vec3 emission = vec3(uvec3(cell_data.data[cell_index].emission & 0x1ff,(cell_data.data[cell_index].emission >> 9) & 0x1ff,(cell_data.data[cell_index].emission >> 18) & 0x1ff)) * pow(2.0, float(cell_data.data[cell_index].emission >> 27) - 15.0 - 9.0);
 	vec4 normal = unpackSnorm4x8(cell_data.data[cell_index].normal);
 
 #ifdef MODE_ANISOTROPIC
@@ -271,8 +271,8 @@ void main() {
 		float distance = length(light_dir);
 		light_dir=normalize(light_dir);
 
-		if (length(normal.xyz) > 0.2 && dot(normal.xyz,light_dir)>=0) {
-			continue; //not facing the light
+		if (attenuation < 0.01 || (length(normal.xyz) > 0.2 && dot(normal.xyz,light_dir)>=0)) {
+			continue; //not facing the light, or attenuation is near zero
 		}
 
 		if (lights.data[i].has_shadow) {
@@ -299,11 +299,11 @@ void main() {
 #ifdef MODE_ANISOTROPIC
 		for(uint j=0;j<6;j++) {
 
-			accum[j]+=max(0.0,dot(accum_dirs[j],-light_dir))*light+emission;
+			accum[j]+=max(0.0,dot(accum_dirs[j],-light_dir))*light;
 		}
 #else
 		if (length(normal.xyz) > 0.2) {
-			accum+=max(0.0,dot(normal.xyz,-light_dir))*light+emission;
+			accum+=max(0.0,dot(normal.xyz,-light_dir))*light;
 		} else {
 			//all directions
 			accum+=light+emission;
@@ -314,14 +314,14 @@ void main() {
 
 #ifdef MODE_ANISOTROPIC
 
-	outputs.data[cell_index*6+0]=vec4(accum[0],0.0);
-	outputs.data[cell_index*6+1]=vec4(accum[1],0.0);
-	outputs.data[cell_index*6+2]=vec4(accum[2],0.0);
-	outputs.data[cell_index*6+3]=vec4(accum[3],0.0);
-	outputs.data[cell_index*6+4]=vec4(accum[4],0.0);
-	outputs.data[cell_index*6+5]=vec4(accum[5],0.0);
+	outputs.data[cell_index*6+0]=vec4(accum[0] + emission,0.0);
+	outputs.data[cell_index*6+1]=vec4(accum[1] + emission,0.0);
+	outputs.data[cell_index*6+2]=vec4(accum[2] + emission,0.0);
+	outputs.data[cell_index*6+3]=vec4(accum[3] + emission,0.0);
+	outputs.data[cell_index*6+4]=vec4(accum[4] + emission,0.0);
+	outputs.data[cell_index*6+5]=vec4(accum[5] + emission,0.0);
 #else
-	outputs.data[cell_index]=vec4(accum,0.0);
+	outputs.data[cell_index]=vec4(accum + emission,0.0);
 
 #endif
 
@@ -420,7 +420,7 @@ void main() {
 				}
 
 			}
-			color *= cone_weights[i] * params.dynamic_range; //restore range
+			color *= cone_weights[i] * vec4(albedo.rgb,1.0) * params.dynamic_range; //restore range
 #ifdef MODE_ANISOTROPIC
 			for(uint j=0;j<6;j++) {
 

+ 5 - 0
servers/visual/rasterizer_rd/shaders/giprobe_debug.glsl

@@ -85,9 +85,14 @@ void main() {
 
 	uvec3 posu = uvec3(cell_data.data[cell_index].position&0x7FF,(cell_data.data[cell_index].position>>11)&0x3FF,cell_data.data[cell_index].position>>21);
 
+#ifdef MODE_DEBUG_EMISSION
+	color_interp.xyz = vec3(uvec3(cell_data.data[cell_index].emission & 0x1ff,(cell_data.data[cell_index].emission >> 9) & 0x1ff,(cell_data.data[cell_index].emission >> 18) & 0x1ff)) * pow(2.0, float(cell_data.data[cell_index].emission >> 27) - 15.0 - 9.0);
+#endif
+
 #ifdef MODE_DEBUG_COLOR
 	color_interp.xyz = unpackUnorm4x8(cell_data.data[cell_index].albedo).xyz;
 #endif
+
 #ifdef MODE_DEBUG_LIGHT
 
 #ifdef USE_ANISOTROPY

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

@@ -326,9 +326,9 @@ layout(location =8) in float dp_clip;
 
 //defines to keep compatibility with vertex
 
-#define world_matrix instances.data[instance_index].transform;
-#define world_normal_matrix instances.data[instance_index].normal_transform;
-#define projection_matrix scene_data.projection_matrix;
+#define world_matrix instances.data[instance_index].transform
+#define world_normal_matrix instances.data[instance_index].normal_transform
+#define projection_matrix scene_data.projection_matrix
 
 #ifdef USE_MATERIAL_UNIFORMS
 layout(set = 3, binding = 0, std140) uniform MaterialUniforms {

+ 11 - 2
servers/visual/rendering_device.h

@@ -1002,14 +1002,23 @@ public:
 		LIMIT_MAX_VERTEX_INPUT_BINDINGS,
 		LIMIT_MAX_VERTEX_INPUT_BINDING_STRIDE,
 		LIMIT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT,
+		LIMIT_MAX_COMPUTE_SHARED_MEMORY_SIZE,
+		LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X,
+		LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Y,
+		LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Z,
+		LIMIT_MAX_COMPUTE_WORKGROUP_INVOCATIONS,
+		LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_X,
+		LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Y,
+		LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z,
 	};
 
 	virtual int limit_get(Limit p_limit) = 0;
 
 	//methods below not exposed, used by RenderingDeviceRD
 	virtual void prepare_screen_for_drawing() = 0;
-	virtual void finalize_frame() = 0;
-	virtual void advance_frame() = 0;
+
+	virtual void swap_buffers() = 0;
+
 	virtual uint32_t get_frame_delay() const = 0;
 
 	static RenderingDevice *get_singleton();

+ 32 - 3
servers/visual/visual_server_scene.cpp

@@ -345,7 +345,11 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) {
 			case VS::INSTANCE_LIGHT: {
 
 				InstanceLightData *light = static_cast<InstanceLightData *>(instance->base_data);
-
+#ifdef DEBUG_ENABLED
+				if (light->geometries.size()) {
+					ERR_PRINT("BUG, indexing did not unpair geometries from light.");
+				}
+#endif
 				if (instance->scenario && light->D) {
 					instance->scenario->directional_lights.erase(light->D);
 					light->D = NULL;
@@ -371,7 +375,16 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) {
 			case VS::INSTANCE_GI_PROBE: {
 
 				InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(instance->base_data);
-
+#ifdef DEBUG_ENABLED
+				if (gi_probe->geometries.size()) {
+					ERR_PRINT("BUG, indexing did not unpair geometries from GIProbe.");
+				}
+#endif
+#ifdef DEBUG_ENABLED
+				if (gi_probe->lights.size()) {
+					ERR_PRINT("BUG, indexing did not unpair lights from GIProbe.");
+				}
+#endif
 				if (gi_probe->update_element.in_list()) {
 					gi_probe_update_list.remove(&gi_probe->update_element);
 				}
@@ -490,7 +503,11 @@ void VisualServerScene::instance_set_scenario(RID p_instance, RID p_scenario) {
 			case VS::INSTANCE_LIGHT: {
 
 				InstanceLightData *light = static_cast<InstanceLightData *>(instance->base_data);
-
+#ifdef DEBUG_ENABLED
+				if (light->geometries.size()) {
+					ERR_PRINT("BUG, indexing did not unpair geometries from light.");
+				}
+#endif
 				if (light->D) {
 					instance->scenario->directional_lights.erase(light->D);
 					light->D = NULL;
@@ -504,6 +521,18 @@ void VisualServerScene::instance_set_scenario(RID p_instance, RID p_scenario) {
 			case VS::INSTANCE_GI_PROBE: {
 
 				InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(instance->base_data);
+
+#ifdef DEBUG_ENABLED
+				if (gi_probe->geometries.size()) {
+					ERR_PRINT("BUG, indexing did not unpair geometries from GIProbe.");
+				}
+#endif
+#ifdef DEBUG_ENABLED
+				if (gi_probe->lights.size()) {
+					ERR_PRINT("BUG, indexing did not unpair lights from GIProbe.");
+				}
+#endif
+
 				if (gi_probe->update_element.in_list()) {
 					gi_probe_update_list.remove(&gi_probe->update_element);
 				}

+ 1 - 0
servers/visual_server.h

@@ -652,6 +652,7 @@ public:
 		VIEWPORT_DEBUG_DRAW_WIREFRAME,
 		VIEWPORT_DEBUG_DRAW_GI_PROBE_ALBEDO,
 		VIEWPORT_DEBUG_DRAW_GI_PROBE_LIGHTING,
+		VIEWPORT_DEBUG_DRAW_GI_PROBE_EMISSION,
 		VIEWPORT_DEBUG_DRAW_SHADOW_ATLAS,
 		VIEWPORT_DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS,