Bläddra i källkod

Restored multiple viewport function, as well as view modes.

Juan Linietsky 8 år sedan
förälder
incheckning
4d50c7ad8c

+ 109 - 134
drivers/gles3/rasterizer_scene_gles3.cpp

@@ -1290,8 +1290,11 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo
 				storage->mesh_render_blend_shapes(s, e->instance->blend_values.ptr());
 				//rebind shader
 				state.scene_shader.bind();
+#ifdef DEBUG_ENABLED
+			} else if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_WIREFRAME && s->array_wireframe_id) {
+				glBindVertexArray(s->array_wireframe_id); // everything is so easy nowadays
+#endif
 			} else {
-
 				glBindVertexArray(s->array_id); // everything is so easy nowadays
 			}
 
@@ -1301,7 +1304,16 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo
 
 			RasterizerStorageGLES3::MultiMesh *multi_mesh = static_cast<RasterizerStorageGLES3::MultiMesh *>(e->owner);
 			RasterizerStorageGLES3::Surface *s = static_cast<RasterizerStorageGLES3::Surface *>(e->geometry);
-			glBindVertexArray(s->instancing_array_id); // use the instancing array ID
+#ifdef DEBUG_ENABLED
+			if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_WIREFRAME && s->instancing_array_wireframe_id) {
+
+				glBindVertexArray(s->instancing_array_wireframe_id); // use the instancing array ID
+			} else
+#endif
+			{
+				glBindVertexArray(s->instancing_array_id); // use the instancing array ID
+			}
+
 			glBindBuffer(GL_ARRAY_BUFFER, multi_mesh->buffer); //modify the buffer
 
 			int stride = (multi_mesh->xform_floats + multi_mesh->color_floats) * 4;
@@ -1366,13 +1378,26 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo
 				sorter.sort(particle_array, particles->amount);
 
 				glUnmapBuffer(GL_ARRAY_BUFFER);
+#ifdef DEBUG_ENABLED
+				if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_WIREFRAME && s->instancing_array_wireframe_id) {
+					glBindVertexArray(s->instancing_array_wireframe_id); // use the wireframe instancing array ID
+				} else
+#endif
+				{
 
-				glBindVertexArray(s->instancing_array_id); // use the instancing array ID
+					glBindVertexArray(s->instancing_array_id); // use the instancing array ID
+				}
 				glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffer_histories[1]); //modify the buffer
 
 			} else {
-
-				glBindVertexArray(s->instancing_array_id); // use the instancing array ID
+#ifdef DEBUG_ENABLED
+				if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_WIREFRAME && s->instancing_array_wireframe_id) {
+					glBindVertexArray(s->instancing_array_wireframe_id); // use the wireframe instancing array ID
+				} else
+#endif
+				{
+					glBindVertexArray(s->instancing_array_id); // use the instancing array ID
+				}
 				glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffers[0]); //modify the buffer
 			}
 
@@ -1421,7 +1446,15 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) {
 
 			RasterizerStorageGLES3::Surface *s = static_cast<RasterizerStorageGLES3::Surface *>(e->geometry);
 
-			if (s->index_array_len > 0) {
+#ifdef DEBUG_ENABLED
+
+			if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_WIREFRAME && s->array_wireframe_id) {
+
+				glDrawElements(GL_LINES, s->index_wireframe_len, GL_UNSIGNED_INT, 0);
+				storage->info.render_vertices_count += s->index_array_len;
+			} else
+#endif
+					if (s->index_array_len > 0) {
 
 				glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0);
 
@@ -1442,7 +1475,15 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) {
 
 			int amount = MAX(multi_mesh->size, multi_mesh->visible_instances);
 
-			if (s->index_array_len > 0) {
+#ifdef DEBUG_ENABLED
+
+			if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_WIREFRAME && s->array_wireframe_id) {
+
+				glDrawElementsInstanced(GL_LINES, s->index_wireframe_len, GL_UNSIGNED_INT, 0, amount);
+				storage->info.render_vertices_count += s->index_array_len * amount;
+			} else
+#endif
+					if (s->index_array_len > 0) {
 
 				glDrawElementsInstanced(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0, amount);
 
@@ -1600,8 +1641,15 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) {
 					glEnableVertexAttribArray(12); //custom
 					glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + sizeof(float) * 4 * 2);
 					glVertexAttribDivisor(12, 1);
+#ifdef DEBUG_ENABLED
 
-					if (s->index_array_len > 0) {
+					if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_WIREFRAME && s->array_wireframe_id) {
+
+						glDrawElementsInstanced(GL_LINES, s->index_wireframe_len, GL_UNSIGNED_INT, 0, amount - split);
+						storage->info.render_vertices_count += s->index_array_len * (amount - split);
+					} else
+#endif
+							if (s->index_array_len > 0) {
 
 						glDrawElementsInstanced(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0, amount - split);
 
@@ -1631,8 +1679,15 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) {
 					glEnableVertexAttribArray(12); //custom
 					glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 2);
 					glVertexAttribDivisor(12, 1);
+#ifdef DEBUG_ENABLED
 
-					if (s->index_array_len > 0) {
+					if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_WIREFRAME && s->array_wireframe_id) {
+
+						glDrawElementsInstanced(GL_LINES, s->index_wireframe_len, GL_UNSIGNED_INT, 0, split);
+						storage->info.render_vertices_count += s->index_array_len * split;
+					} else
+#endif
+							if (s->index_array_len > 0) {
 
 						glDrawElementsInstanced(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0, split);
 
@@ -1648,7 +1703,15 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) {
 
 			} else {
 
-				if (s->index_array_len > 0) {
+#ifdef DEBUG_ENABLED
+
+				if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_WIREFRAME && s->array_wireframe_id) {
+
+					glDrawElementsInstanced(GL_LINES, s->index_wireframe_len, GL_UNSIGNED_INT, 0, amount);
+					storage->info.render_vertices_count += s->index_array_len * amount;
+				} else
+#endif
+						if (s->index_array_len > 0) {
 
 					glDrawElementsInstanced(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0, amount);
 
@@ -2034,6 +2097,10 @@ void RasterizerSceneGLES3::_add_geometry(RasterizerStorageGLES3::Geometry *p_geo
 	RasterizerStorageGLES3::Material *m = NULL;
 	RID m_src = p_instance->material_override.is_valid() ? p_instance->material_override : (p_material >= 0 ? p_instance->materials[p_material] : p_geometry->material);
 
+	if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_OVERDRAW) {
+		m_src = default_overdraw_material;
+	}
+
 	/*
 #ifdef DEBUG_ENABLED
 	if (current_debug==VS::SCENARIO_DEBUG_OVERDRAW) {
@@ -2152,7 +2219,7 @@ void RasterizerSceneGLES3::_add_geometry(RasterizerStorageGLES3::Geometry *p_geo
 
 	//e->light_type=0xFF; // no lights!
 
-	if (shadow || m->shader->spatial.unshaded /*|| current_debug==VS::SCENARIO_DEBUG_SHADELESS*/) {
+	if (shadow || m->shader->spatial.unshaded || state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_UNSHADED) {
 
 		e->sort_key |= RenderList::SORT_KEY_UNSHADED_FLAG;
 	}
@@ -3789,7 +3856,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
 
 	state.used_contact_shadows = true;
 
-	if (storage->frame.current_rt && true) { //detect with state.used_contact_shadows too
+	if (storage->frame.current_rt && state.debug_draw != VS::VIEWPORT_DEBUG_DRAW_OVERDRAW) { //detect with state.used_contact_shadows too
 		//pre z pass
 
 		glDisable(GL_BLEND);
@@ -3880,6 +3947,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
 		//effects disabled and transparency also prevent using MRTs
 		use_mrt = use_mrt && !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT];
 		use_mrt = use_mrt && !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_NO_3D_EFFECTS];
+		use_mrt = use_mrt && state.debug_draw != VS::VIEWPORT_DEBUG_DRAW_OVERDRAW;
 
 		glViewport(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height);
 
@@ -3928,7 +3996,10 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
 	RasterizerStorageGLES3::Sky *sky = NULL;
 	GLuint env_radiance_tex = 0;
 
-	if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
+	if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_OVERDRAW) {
+		clear_color = Color(0, 0, 0, 0);
+		storage->frame.clear_request = false;
+	} else if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
 		clear_color = Color(0, 0, 0, 0);
 	} else if (!env || env->bg_mode == VS::ENV_BG_CLEAR_COLOR) {
 
@@ -3992,7 +4063,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
 		glDrawBuffers(1, &gldb);
 	}
 
-	if (env && env->bg_mode == VS::ENV_BG_SKY && !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
+	if (env && env->bg_mode == VS::ENV_BG_SKY && !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT] && state.debug_draw != VS::VIEWPORT_DEBUG_DRAW_OVERDRAW) {
 
 		/*
 		if (use_mrt) {
@@ -4554,136 +4625,39 @@ bool RasterizerSceneGLES3::free(RID p_rid) {
 	return true;
 }
 
-// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
-static _FORCE_INLINE_ float radicalInverse_VdC(uint32_t bits) {
-	bits = (bits << 16u) | (bits >> 16u);
-	bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
-	bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
-	bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
-	bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
-	return float(bits) * 2.3283064365386963e-10f; // / 0x100000000
-}
-
-static _FORCE_INLINE_ Vector2 Hammersley(uint32_t i, uint32_t N) {
-	return Vector2(float(i) / float(N), radicalInverse_VdC(i));
-}
-
-static _FORCE_INLINE_ Vector3 ImportanceSampleGGX(Vector2 Xi, float Roughness, Vector3 N) {
-	float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph]
-
-	// Compute distribution direction
-	float Phi = 2.0f * Math_PI * Xi.x;
-	float CosTheta = Math::sqrt((float)(1.0f - Xi.y) / (1.0f + (a * a - 1.0f) * Xi.y));
-	float SinTheta = Math::sqrt((float)Math::abs(1.0f - CosTheta * CosTheta));
-
-	// Convert to spherical direction
-	Vector3 H;
-	H.x = SinTheta * Math::cos(Phi);
-	H.y = SinTheta * Math::sin(Phi);
-	H.z = CosTheta;
-
-	Vector3 UpVector = Math::abs(N.z) < 0.999 ? Vector3(0.0, 0.0, 1.0) : Vector3(1.0, 0.0, 0.0);
-	Vector3 TangentX = UpVector.cross(N);
-	TangentX.normalize();
-	Vector3 TangentY = N.cross(TangentX);
-
-	// Tangent to world space
-	return TangentX * H.x + TangentY * H.y + N * H.z;
-}
+void RasterizerSceneGLES3::set_debug_draw_mode(VS::ViewportDebugDraw p_debug_draw) {
 
-static _FORCE_INLINE_ float GGX(float NdotV, float a) {
-	float k = a / 2.0;
-	return NdotV / (NdotV * (1.0 - k) + k);
+	state.debug_draw = p_debug_draw;
 }
 
-// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html
-float _FORCE_INLINE_ G_Smith(float a, float nDotV, float nDotL) {
-	return GGX(nDotL, a * a) * GGX(nDotV, a * a);
-}
-
-void RasterizerSceneGLES3::_generate_brdf() {
-
-	int brdf_size = GLOBAL_DEF("rendering/gles3/brdf_texture_size", 64);
-
-	PoolVector<uint8_t> brdf;
-	brdf.resize(brdf_size * brdf_size * 2);
-
-	PoolVector<uint8_t>::Write w = brdf.write();
-
-	for (int i = 0; i < brdf_size; i++) {
-		for (int j = 0; j < brdf_size; j++) {
-
-			float Roughness = float(j) / (brdf_size - 1);
-			float NoV = float(i + 1) / (brdf_size); //avoid storing nov0
-
-			Vector3 V;
-			V.x = Math::sqrt(1.0f - NoV * NoV);
-			V.y = 0.0;
-			V.z = NoV;
-
-			Vector3 N = Vector3(0.0, 0.0, 1.0);
-
-			float A = 0;
-			float B = 0;
-
-			for (int s = 0; s < 512; s++) {
-
-				Vector2 xi = Hammersley(s, 512);
-				Vector3 H = ImportanceSampleGGX(xi, Roughness, N);
-				Vector3 L = 2.0 * V.dot(H) * H - V;
+void RasterizerSceneGLES3::initialize() {
 
-				float NoL = CLAMP(L.z, 0.0, 1.0);
-				float NoH = CLAMP(H.z, 0.0, 1.0);
-				float VoH = CLAMP(V.dot(H), 0.0, 1.0);
+	render_pass = 0;
 
-				if (NoL > 0.0) {
-					float G = G_Smith(Roughness, NoV, NoL);
-					float G_Vis = G * VoH / (NoH * NoV);
-					float Fc = pow(1.0 - VoH, 5.0);
+	state.scene_shader.init();
 
-					A += (1.0 - Fc) * G_Vis;
-					B += Fc * G_Vis;
-				}
-			}
+	{
+		//default material and shader
 
-			A /= 512.0;
-			B /= 512.0;
+		default_shader = storage->shader_create();
+		storage->shader_set_code(default_shader, "shader_type spatial;\n");
+		default_material = storage->material_create();
+		storage->material_set_shader(default_material, default_shader);
 
-			int tofs = ((brdf_size - j - 1) * brdf_size + i) * 2;
-			w[tofs + 0] = CLAMP(A * 255, 0, 255);
-			w[tofs + 1] = CLAMP(B * 255, 0, 255);
-		}
+		default_shader_twosided = storage->shader_create();
+		default_material_twosided = storage->material_create();
+		storage->shader_set_code(default_shader_twosided, "shader_type spatial; render_mode cull_disabled;\n");
+		storage->material_set_shader(default_material_twosided, default_shader_twosided);
 	}
 
-	//set up brdf texture
-
-	glGenTextures(1, &state.brdf_texture);
-
-	glActiveTexture(GL_TEXTURE0);
-	glBindTexture(GL_TEXTURE_2D, state.brdf_texture);
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, brdf_size, brdf_size, 0, GL_RG, GL_UNSIGNED_BYTE, w.ptr());
-	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-	glBindTexture(GL_TEXTURE_2D, 0);
-}
-
-void RasterizerSceneGLES3::initialize() {
-
-	render_pass = 0;
-
-	state.scene_shader.init();
-
-	default_shader = storage->shader_create();
-	storage->shader_set_code(default_shader, "shader_type spatial;\n");
-	default_material = storage->material_create();
-	storage->material_set_shader(default_material, default_shader);
+	{
+		//default material and shader
 
-	default_shader_twosided = storage->shader_create();
-	default_material_twosided = storage->material_create();
-	storage->shader_set_code(default_shader_twosided, "shader_type spatial; render_mode cull_disabled;\n");
-	storage->material_set_shader(default_material_twosided, default_shader_twosided);
+		default_overdraw_shader = storage->shader_create();
+		storage->shader_set_code(default_overdraw_shader, "shader_type spatial;\nrender_mode blend_add,unshaded;\n void fragment() { ALBEDO=vec3(0.4,0.8,0.8); ALPHA=0.2; }");
+		default_overdraw_material = storage->material_create();
+		storage->material_set_shader(default_overdraw_material, default_overdraw_shader);
+	}
 
 	glGenBuffers(1, &state.scene_ubo);
 	glBindBuffer(GL_UNIFORM_BUFFER, state.scene_ubo);
@@ -4721,7 +4695,6 @@ void RasterizerSceneGLES3::initialize() {
 	}
 	render_list.init();
 	state.cube_to_dp_shader.init();
-	_generate_brdf();
 
 	shadow_atlas_realloc_tolerance_msec = 500;
 
@@ -4968,6 +4941,8 @@ void RasterizerSceneGLES3::initialize() {
 		GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
 		ERR_CONTINUE(status != GL_FRAMEBUFFER_COMPLETE);
 	}
+
+	state.debug_draw = VS::VIEWPORT_DEBUG_DRAW_DISABLED;
 }
 
 void RasterizerSceneGLES3::iteration() {

+ 5 - 4
drivers/gles3/rasterizer_scene_gles3.h

@@ -77,6 +77,9 @@ public:
 	RID default_shader;
 	RID default_shader_twosided;
 
+	RID default_overdraw_material;
+	RID default_overdraw_shader;
+
 	RasterizerStorageGLES3 *storage;
 
 	Vector<RasterizerStorageGLES3::RenderTarget::Exposure> exposure_shrink;
@@ -152,8 +155,6 @@ public:
 
 		GLuint env_radiance_ubo;
 
-		GLuint brdf_texture;
-
 		GLuint sky_verts;
 		GLuint sky_array;
 
@@ -187,6 +188,7 @@ public:
 		bool used_sss;
 		bool used_screen_texture;
 
+		VS::ViewportDebugDraw debug_draw;
 	} state;
 
 	/* SHADOW ATLAS API */
@@ -783,9 +785,8 @@ public:
 	virtual void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count);
 	virtual bool free(RID p_rid);
 
-	void _generate_brdf();
-
 	virtual void set_scene_pass(uint64_t p_pass);
+	virtual void set_debug_draw_mode(VS::ViewportDebugDraw p_debug_draw);
 
 	void iteration();
 	void initialize();

+ 118 - 0
drivers/gles3/rasterizer_storage_gles3.cpp

@@ -2727,6 +2727,112 @@ void RasterizerStorageGLES3::mesh_add_surface(RID p_mesh, uint32_t p_format, VS:
 			glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
 			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 		}
+
+#ifdef DEBUG_ENABLED
+
+		if (config.generate_wireframes && p_primitive == VS::PRIMITIVE_TRIANGLES) {
+			//generate wireframes, this is used mostly by editor
+			PoolVector<uint32_t> wf_indices;
+			int index_count;
+
+			if (p_format & VS::ARRAY_FORMAT_INDEX) {
+
+				index_count = p_index_count * 2;
+				wf_indices.resize(index_count);
+
+				PoolVector<uint8_t>::Read ir = p_index_array.read();
+				PoolVector<uint32_t>::Write wr = wf_indices.write();
+
+				if (p_vertex_count < (1 << 16)) {
+					//read 16 bit indices
+					const uint16_t *src_idx = (const uint16_t *)ir.ptr();
+					for (int i = 0; i < index_count; i += 6) {
+
+						wr[i + 0] = src_idx[i / 2];
+						wr[i + 1] = src_idx[i / 2 + 1];
+						wr[i + 2] = src_idx[i / 2 + 1];
+						wr[i + 3] = src_idx[i / 2 + 2];
+						wr[i + 4] = src_idx[i / 2 + 2];
+						wr[i + 5] = src_idx[i / 2];
+					}
+
+				} else {
+
+					//read 16 bit indices
+					const uint32_t *src_idx = (const uint32_t *)ir.ptr();
+					for (int i = 0; i < index_count; i += 6) {
+
+						wr[i + 0] = src_idx[i / 2];
+						wr[i + 1] = src_idx[i / 2 + 1];
+						wr[i + 2] = src_idx[i / 2 + 1];
+						wr[i + 3] = src_idx[i / 2 + 2];
+						wr[i + 4] = src_idx[i / 2 + 2];
+						wr[i + 5] = src_idx[i / 2];
+					}
+				}
+
+			} else {
+
+				index_count = p_vertex_count * 2;
+				wf_indices.resize(index_count);
+				PoolVector<uint32_t>::Write wr = wf_indices.write();
+				for (int i = 0; i < index_count; i += 6) {
+
+					wr[i + 0] = i / 2;
+					wr[i + 1] = i / 2 + 1;
+					wr[i + 2] = i / 2 + 1;
+					wr[i + 3] = i / 2 + 2;
+					wr[i + 4] = i / 2 + 2;
+					wr[i + 5] = i / 2;
+				}
+			}
+			{
+				PoolVector<uint32_t>::Read ir = wf_indices.read();
+
+				glGenBuffers(1, &surface->index_wireframe_id);
+				glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surface->index_wireframe_id);
+				glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_count * sizeof(uint32_t), ir.ptr(), GL_STATIC_DRAW);
+				glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //unbind
+
+				surface->index_wireframe_len = index_count;
+			}
+
+			for (int ai = 0; ai < 2; ai++) {
+
+				if (ai == 0) {
+					//for normal draw
+					glGenVertexArrays(1, &surface->array_wireframe_id);
+					glBindVertexArray(surface->array_wireframe_id);
+					glBindBuffer(GL_ARRAY_BUFFER, surface->vertex_id);
+				} else if (ai == 1) {
+					//for instancing draw (can be changed and no one cares)
+					glGenVertexArrays(1, &surface->instancing_array_wireframe_id);
+					glBindVertexArray(surface->instancing_array_wireframe_id);
+					glBindBuffer(GL_ARRAY_BUFFER, surface->vertex_id);
+				}
+
+				for (int i = 0; i < VS::ARRAY_MAX - 1; i++) {
+
+					if (!attribs[i].enabled)
+						continue;
+
+					if (attribs[i].integer) {
+						glVertexAttribIPointer(attribs[i].index, attribs[i].size, attribs[i].type, attribs[i].stride, ((uint8_t *)0) + attribs[i].offset);
+					} else {
+						glVertexAttribPointer(attribs[i].index, attribs[i].size, attribs[i].type, attribs[i].normalized, attribs[i].stride, ((uint8_t *)0) + attribs[i].offset);
+					}
+					glEnableVertexAttribArray(attribs[i].index);
+				}
+
+				glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surface->index_wireframe_id);
+
+				glBindVertexArray(0);
+				glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+				glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+			}
+		}
+
+#endif
 	}
 
 	{
@@ -2998,6 +3104,12 @@ void RasterizerStorageGLES3::mesh_remove_surface(RID p_mesh, int p_surface) {
 		glDeleteVertexArrays(1, &surface->blend_shapes[i].array_id);
 	}
 
+	if (surface->index_wireframe_id) {
+		glDeleteBuffers(1, &surface->index_wireframe_id);
+		glDeleteVertexArrays(1, &surface->array_wireframe_id);
+		glDeleteVertexArrays(1, &surface->instancing_array_wireframe_id);
+	}
+
 	mesh->instance_material_change_notify();
 
 	memdelete(surface);
@@ -6345,6 +6457,11 @@ bool RasterizerStorageGLES3::has_os_feature(const String &p_feature) const {
 
 ////////////////////////////////////////////
 
+void RasterizerStorageGLES3::set_debug_generate_wireframes(bool p_generate) {
+
+	config.generate_wireframes = p_generate;
+}
+
 void RasterizerStorageGLES3::initialize() {
 
 	RasterizerStorageGLES3::system_fbo = 0;
@@ -6526,6 +6643,7 @@ void RasterizerStorageGLES3::initialize() {
 	frame.delta = 0;
 	frame.current_rt = NULL;
 	config.keep_original_textures = false;
+	config.generate_wireframes = false;
 }
 
 void RasterizerStorageGLES3::finalize() {

+ 14 - 0
drivers/gles3/rasterizer_storage_gles3.h

@@ -84,6 +84,8 @@ public:
 		int max_texture_image_units;
 		int max_texture_size;
 
+		bool generate_wireframes;
+
 		Set<String> extensions;
 
 		bool keep_original_textures;
@@ -537,6 +539,11 @@ public:
 		GLuint vertex_id;
 		GLuint index_id;
 
+		GLuint index_wireframe_id;
+		GLuint array_wireframe_id;
+		GLuint instancing_array_wireframe_id;
+		int index_wireframe_len;
+
 		Vector<Rect3> skeleton_bone_aabb;
 		Vector<bool> skeleton_bone_used;
 
@@ -581,6 +588,11 @@ public:
 			primitive = VS::PRIMITIVE_POINTS;
 			index_array_len = 0;
 			active = false;
+
+			index_wireframe_id = 0;
+			array_wireframe_id = 0;
+			instancing_array_wireframe_id = 0;
+			index_wireframe_len = 0;
 		}
 
 		~Surface() {
@@ -1283,6 +1295,8 @@ public:
 
 	virtual void update_dirty_resources();
 
+	virtual void set_debug_generate_wireframes(bool p_generate);
+
 	RasterizerStorageGLES3();
 };
 

+ 1 - 0
editor/editor_node.cpp

@@ -4878,6 +4878,7 @@ EditorNode::EditorNode() {
 	Resource::_get_local_scene_func = _resource_get_edited_scene;
 
 	VisualServer::get_singleton()->textures_keep_original(true);
+	VisualServer::get_singleton()->set_debug_generate_wireframes(true);
 
 	EditorHelp::generate_doc(); //before any editor classes are crated
 	SceneState::set_disable_placeholders(true);

+ 48 - 60
editor/plugins/spatial_editor_plugin.cpp

@@ -1997,6 +1997,45 @@ void SpatialEditorViewport::_menu_option(int p_option) {
 			view_menu->get_popup()->set_item_checked(idx, current);
 
 		} break;
+		case VIEW_DISPLAY_NORMAL: {
+
+			viewport->set_debug_draw(Viewport::DEBUG_DRAW_DISABLED);
+
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), true);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME), false);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW), false);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS), false);
+
+		} break;
+		case VIEW_DISPLAY_WIREFRAME: {
+
+			viewport->set_debug_draw(Viewport::DEBUG_DRAW_WIREFRAME);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), false);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME), true);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW), false);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS), false);
+
+		} break;
+		case VIEW_DISPLAY_OVERDRAW: {
+
+			viewport->set_debug_draw(Viewport::DEBUG_DRAW_OVERDRAW);
+			VisualServer::get_singleton()->scenario_set_debug(get_tree()->get_root()->get_world()->get_scenario(), VisualServer::SCENARIO_DEBUG_OVERDRAW);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), false);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME), false);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW), true);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS), false);
+
+		} break;
+		case VIEW_DISPLAY_SHADELESS: {
+
+			viewport->set_debug_draw(Viewport::DEBUG_DRAW_UNSHADED);
+			VisualServer::get_singleton()->scenario_set_debug(get_tree()->get_root()->get_world()->get_scenario(), VisualServer::SCENARIO_DEBUG_SHADELESS);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), false);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME), false);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW), false);
+			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS), true);
+
+		} break;
 	}
 }
 
@@ -2296,12 +2335,18 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
 	view_menu->get_popup()->add_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL);
 	view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true);
 	view_menu->get_popup()->add_separator();
-	view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_environment", TTR("Environment")), VIEW_ENVIRONMENT);
+	view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL);
+	view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME);
+	view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW);
+	view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS);
+	view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), true);
+	view_menu->get_popup()->add_separator();
+	view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_environment", TTR("View Environment")), VIEW_ENVIRONMENT);
+	view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_gizmos", TTR("View Gizmos")), VIEW_GIZMOS);
+	view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_information", TTR("View Information")), VIEW_INFORMATION);
 	view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ENVIRONMENT), true);
 	view_menu->get_popup()->add_separator();
 	view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_audio_listener", TTR("Audio Listener")), VIEW_AUDIO_LISTENER);
-	view_menu->get_popup()->add_separator();
-	view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_gizmos", TTR("Gizmos")), VIEW_GIZMOS);
 	view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS), true);
 
 	view_menu->get_popup()->add_separator();
@@ -3087,43 +3132,6 @@ void SpatialEditor::_menu_item_pressed(int p_option) {
 			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false);
 			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false);
 
-		} break;
-		case MENU_VIEW_DISPLAY_NORMAL: {
-
-			VisualServer::get_singleton()->scenario_set_debug(get_tree()->get_root()->get_world()->get_scenario(), VisualServer::SCENARIO_DEBUG_DISABLED);
-
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_NORMAL), true);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_WIREFRAME), false);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_OVERDRAW), false);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_SHADELESS), false);
-
-		} break;
-		case MENU_VIEW_DISPLAY_WIREFRAME: {
-
-			VisualServer::get_singleton()->scenario_set_debug(get_tree()->get_root()->get_world()->get_scenario(), VisualServer::SCENARIO_DEBUG_WIREFRAME);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_NORMAL), false);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_WIREFRAME), true);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_OVERDRAW), false);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_SHADELESS), false);
-
-		} break;
-		case MENU_VIEW_DISPLAY_OVERDRAW: {
-
-			VisualServer::get_singleton()->scenario_set_debug(get_tree()->get_root()->get_world()->get_scenario(), VisualServer::SCENARIO_DEBUG_OVERDRAW);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_NORMAL), false);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_WIREFRAME), false);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_OVERDRAW), true);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_SHADELESS), false);
-
-		} break;
-		case MENU_VIEW_DISPLAY_SHADELESS: {
-
-			VisualServer::get_singleton()->scenario_set_debug(get_tree()->get_root()->get_world()->get_scenario(), VisualServer::SCENARIO_DEBUG_SHADELESS);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_NORMAL), false);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_WIREFRAME), false);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_OVERDRAW), false);
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_SHADELESS), true);
-
 		} break;
 		case MENU_VIEW_ORIGIN: {
 
@@ -3473,17 +3481,6 @@ void SpatialEditor::_unhandled_key_input(Ref<InputEvent> p_event) {
 
 			else if (ED_IS_SHORTCUT("spatial_editor/tool_scale", p_event))
 				_menu_item_pressed(MENU_TOOL_SCALE);
-
-			else if (ED_IS_SHORTCUT("spatial_editor/display_wireframe", p_event)) {
-				if (k->get_shift() || k->get_control() || k->get_command())
-					return;
-
-				if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_WIREFRAME))) {
-					_menu_item_pressed(MENU_VIEW_DISPLAY_NORMAL);
-				} else {
-					_menu_item_pressed(MENU_VIEW_DISPLAY_WIREFRAME);
-				}
-			}
 		}
 	}
 }
@@ -3650,9 +3647,6 @@ void SpatialEditor::clear() {
 		viewports[i]->reset();
 	}
 
-	_menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT);
-	_menu_item_pressed(MENU_VIEW_DISPLAY_NORMAL);
-
 	VisualServer::get_singleton()->instance_set_visible(origin_instance, true);
 	view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN), true);
 	for (int i = 0; i < 3; ++i) {
@@ -3797,17 +3791,11 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
 	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS);
 	p->add_separator();
 
-	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/display_normal", TTR("Display Normal")), MENU_VIEW_DISPLAY_NORMAL);
-	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/display_wireframe", TTR("Display Wireframe")), MENU_VIEW_DISPLAY_WIREFRAME);
-	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/display_overdraw", TTR("Display Overdraw")), MENU_VIEW_DISPLAY_OVERDRAW);
-	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/display_shadeless", TTR("Display Shadeless")), MENU_VIEW_DISPLAY_SHADELESS);
-	p->add_separator();
 	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN);
 	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid")), MENU_VIEW_GRID);
 	p->add_separator();
 	p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings")), MENU_VIEW_CAMERA_SETTINGS);
 
-	p->set_item_checked(p->get_item_index(MENU_VIEW_DISPLAY_NORMAL), true);
 	p->set_item_checked(p->get_item_index(MENU_VIEW_ORIGIN), true);
 	p->set_item_checked(p->get_item_index(MENU_VIEW_GRID), true);
 

+ 5 - 4
editor/plugins/spatial_editor_plugin.h

@@ -84,6 +84,11 @@ class SpatialEditorViewport : public Control {
 		VIEW_ORTHOGONAL,
 		VIEW_AUDIO_LISTENER,
 		VIEW_GIZMOS,
+		VIEW_INFORMATION,
+		VIEW_DISPLAY_NORMAL,
+		VIEW_DISPLAY_WIREFRAME,
+		VIEW_DISPLAY_OVERDRAW,
+		VIEW_DISPLAY_SHADELESS,
 	};
 
 public:
@@ -416,10 +421,6 @@ private:
 		MENU_VIEW_USE_3_VIEWPORTS,
 		MENU_VIEW_USE_3_VIEWPORTS_ALT,
 		MENU_VIEW_USE_4_VIEWPORTS,
-		MENU_VIEW_DISPLAY_NORMAL,
-		MENU_VIEW_DISPLAY_WIREFRAME,
-		MENU_VIEW_DISPLAY_OVERDRAW,
-		MENU_VIEW_DISPLAY_SHADELESS,
 		MENU_VIEW_ORIGIN,
 		MENU_VIEW_GRID,
 		MENU_VIEW_CAMERA_SETTINGS,

+ 36 - 0
scene/main/viewport.cpp

@@ -2530,6 +2530,22 @@ Viewport::Usage Viewport::get_usage() const {
 	return usage;
 }
 
+void Viewport::set_debug_draw(DebugDraw p_debug_draw) {
+
+	debug_draw = p_debug_draw;
+	VS::get_singleton()->viewport_set_debug_draw(viewport, VS::ViewportDebugDraw(p_debug_draw));
+}
+
+Viewport::DebugDraw Viewport::get_debug_draw() const {
+
+	return debug_draw;
+}
+
+int Viewport::get_render_info(RenderInfo p_info) {
+
+	return VS::get_singleton()->viewport_get_render_info(viewport, VS::ViewportRenderInfo(p_info));
+}
+
 void Viewport::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("set_size", "size"), &Viewport::set_size);
@@ -2586,6 +2602,11 @@ void Viewport::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_usage", "usage"), &Viewport::set_usage);
 	ClassDB::bind_method(D_METHOD("get_usage"), &Viewport::get_usage);
 
+	ClassDB::bind_method(D_METHOD("set_debug_draw", "debug_draw"), &Viewport::set_debug_draw);
+	ClassDB::bind_method(D_METHOD("get_debug_draw"), &Viewport::get_debug_draw);
+
+	ClassDB::bind_method(D_METHOD("get_render_info", "info"), &Viewport::get_render_info);
+
 	ClassDB::bind_method(D_METHOD("get_texture:ViewportTexture"), &Viewport::get_texture);
 
 	ClassDB::bind_method(D_METHOD("set_physics_object_picking", "enable"), &Viewport::set_physics_object_picking);
@@ -2640,6 +2661,7 @@ void Viewport::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hdr"), "set_hdr", "get_hdr");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "usage", PROPERTY_HINT_ENUM, "2D,2D No-Sampling,3D,3D No-Effects"), "set_usage", "get_usage");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw");
 	ADD_GROUP("Render Target", "render_target_");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "render_target_v_flip"), "set_vflip", "get_vflip");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "render_target_clear_on_new_frame"), "set_clear_on_new_frame", "get_clear_on_new_frame");
@@ -2674,6 +2696,19 @@ void Viewport::_bind_methods() {
 	BIND_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_1024);
 	BIND_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_MAX);
 
+	BIND_CONSTANT(RENDER_INFO_OBJECTS_IN_FRAME);
+	BIND_CONSTANT(RENDER_INFO_VERTICES_IN_FRAME);
+	BIND_CONSTANT(RENDER_INFO_MATERIAL_CHANGES_IN_FRAME);
+	BIND_CONSTANT(RENDER_INFO_SHADER_CHANGES_IN_FRAME);
+	BIND_CONSTANT(RENDER_INFO_SURFACE_CHANGES_IN_FRAME);
+	BIND_CONSTANT(RENDER_INFO_DRAW_CALLS_IN_FRAME);
+	BIND_CONSTANT(RENDER_INFO_MAX);
+
+	BIND_CONSTANT(DEBUG_DRAW_DISABLED);
+	BIND_CONSTANT(DEBUG_DRAW_UNSHADED);
+	BIND_CONSTANT(DEBUG_DRAW_OVERDRAW);
+	BIND_CONSTANT(DEBUG_DRAW_WIREFRAME);
+
 	BIND_CONSTANT(MSAA_DISABLED);
 	BIND_CONSTANT(MSAA_2X);
 	BIND_CONSTANT(MSAA_4X);
@@ -2750,6 +2785,7 @@ Viewport::Viewport() {
 	hdr = false;
 
 	usage = USAGE_3D;
+	debug_draw = DEBUG_DRAW_DISABLED;
 }
 
 Viewport::~Viewport() {

+ 27 - 0
scene/main/viewport.h

@@ -122,6 +122,24 @@ public:
 		USAGE_3D_NO_EFFECTS,
 	};
 
+	enum RenderInfo {
+
+		RENDER_INFO_OBJECTS_IN_FRAME,
+		RENDER_INFO_VERTICES_IN_FRAME,
+		RENDER_INFO_MATERIAL_CHANGES_IN_FRAME,
+		RENDER_INFO_SHADER_CHANGES_IN_FRAME,
+		RENDER_INFO_SURFACE_CHANGES_IN_FRAME,
+		RENDER_INFO_DRAW_CALLS_IN_FRAME,
+		RENDER_INFO_MAX
+	};
+
+	enum DebugDraw {
+		DEBUG_DRAW_DISABLED,
+		DEBUG_DRAW_UNSHADED,
+		DEBUG_DRAW_OVERDRAW,
+		DEBUG_DRAW_WIREFRAME,
+	};
+
 private:
 	friend class ViewportTexture;
 
@@ -204,6 +222,8 @@ private:
 	RID texture_rid;
 	uint32_t texture_flags;
 
+	DebugDraw debug_draw;
+
 	Usage usage;
 
 	int shadow_atlas_size;
@@ -430,6 +450,11 @@ public:
 	void set_usage(Usage p_usage);
 	Usage get_usage() const;
 
+	void set_debug_draw(DebugDraw p_debug_draw);
+	DebugDraw get_debug_draw() const;
+
+	int get_render_info(RenderInfo p_info);
+
 	Viewport();
 	~Viewport();
 };
@@ -438,5 +463,7 @@ VARIANT_ENUM_CAST(Viewport::UpdateMode);
 VARIANT_ENUM_CAST(Viewport::ShadowAtlasQuadrantSubdiv);
 VARIANT_ENUM_CAST(Viewport::MSAA);
 VARIANT_ENUM_CAST(Viewport::Usage);
+VARIANT_ENUM_CAST(Viewport::DebugDraw);
+VARIANT_ENUM_CAST(Viewport::RenderInfo);
 
 #endif

+ 3 - 0
servers/visual/rasterizer.h

@@ -155,6 +155,7 @@ public:
 	virtual void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) = 0;
 
 	virtual void set_scene_pass(uint64_t p_pass) = 0;
+	virtual void set_debug_draw_mode(VS::ViewportDebugDraw p_debug_draw) = 0;
 
 	virtual bool free(RID p_rid) = 0;
 
@@ -498,6 +499,8 @@ public:
 
 	virtual void update_dirty_resources() = 0;
 
+	virtual void set_debug_generate_wireframes(bool p_generate) = 0;
+
 	static RasterizerStorage *base_singleton;
 	RasterizerStorage();
 	virtual ~RasterizerStorage() {}

+ 5 - 0
servers/visual/visual_server_raster.cpp

@@ -175,6 +175,11 @@ bool VisualServerRaster::has_os_feature(const String &p_feature) const {
 	return VSG::storage->has_os_feature(p_feature);
 }
 
+void VisualServerRaster::set_debug_generate_wireframes(bool p_generate) {
+
+	VSG::storage->set_debug_generate_wireframes(p_generate);
+}
+
 VisualServerRaster::VisualServerRaster() {
 
 	VSG::canvas = memnew(VisualServerCanvas);

+ 6 - 0
servers/visual/visual_server_raster.h

@@ -595,6 +595,8 @@ public:
 	m_r m_name(m_type1 arg1) { return BINDBASE->m_name(arg1); }
 #define BIND1RC(m_r, m_name, m_type1) \
 	m_r m_name(m_type1 arg1) const { return BINDBASE->m_name(arg1); }
+#define BIND2R(m_r, m_name, m_type1, m_type2) \
+	m_r m_name(m_type1 arg1, m_type2 arg2) { return BINDBASE->m_name(arg1, arg2); }
 #define BIND2RC(m_r, m_name, m_type1, m_type2) \
 	m_r m_name(m_type1 arg1, m_type2 arg2) const { return BINDBASE->m_name(arg1, arg2); }
 #define BIND3RC(m_r, m_name, m_type1, m_type2, m_type3) \
@@ -932,6 +934,9 @@ public:
 	BIND2(viewport_set_hdr, RID, bool)
 	BIND2(viewport_set_usage, RID, ViewportUsage)
 
+	BIND2R(int, viewport_get_render_info, RID, ViewportRenderInfo)
+	BIND2(viewport_set_debug_draw, RID, ViewportDebugDraw)
+
 /* ENVIRONMENT API */
 
 #undef BINDBASE
@@ -1130,6 +1135,7 @@ public:
 	virtual bool has_feature(Features p_feature) const;
 
 	virtual bool has_os_feature(const String &p_feature) const;
+	virtual void set_debug_generate_wireframes(bool p_generate);
 
 	VisualServerRaster();
 	~VisualServerRaster();

+ 21 - 0
servers/visual/visual_server_viewport.cpp

@@ -272,6 +272,9 @@ void VisualServerViewport::draw_viewports() {
 			continue;
 
 		VSG::rasterizer->set_current_render_target(vp->render_target);
+
+		VSG::scene_render->set_debug_draw_mode(vp->debug_draw);
+
 		_draw_viewport(vp);
 
 		if (vp->viewport_to_screen_rect != Rect2()) {
@@ -283,6 +286,7 @@ void VisualServerViewport::draw_viewports() {
 		if (vp->update_mode == VS::VIEWPORT_UPDATE_ONCE) {
 			vp->update_mode = VS::VIEWPORT_UPDATE_DISABLED;
 		}
+		VSG::scene_render->set_debug_draw_mode(VS::VIEWPORT_DEBUG_DRAW_DISABLED);
 	}
 }
 
@@ -557,6 +561,23 @@ void VisualServerViewport::viewport_set_usage(RID p_viewport, VS::ViewportUsage
 	}
 }
 
+int VisualServerViewport::viewport_get_render_info(RID p_viewport, VS::ViewportRenderInfo p_info) {
+
+	ERR_FAIL_INDEX_V(p_info, VS::VIEWPORT_RENDER_INFO_MAX, -1);
+	Viewport *viewport = viewport_owner.getornull(p_viewport);
+	ERR_FAIL_COND_V(!viewport, -1);
+
+	return viewport->render_info[p_info];
+}
+
+void VisualServerViewport::viewport_set_debug_draw(RID p_viewport, VS::ViewportDebugDraw p_draw) {
+
+	Viewport *viewport = viewport_owner.getornull(p_viewport);
+	ERR_FAIL_COND(!viewport);
+
+	viewport->debug_draw = p_draw;
+}
+
 bool VisualServerViewport::free(RID p_rid) {
 
 	if (viewport_owner.owns(p_rid)) {

+ 10 - 0
servers/visual/visual_server_viewport.h

@@ -64,6 +64,9 @@ public:
 		RID shadow_atlas;
 		int shadow_atlas_size;
 
+		int render_info[VS::VIEWPORT_RENDER_INFO_MAX];
+		VS::ViewportDebugDraw debug_draw;
+
 		VS::ViewportClearMode clear_mode;
 
 		bool rendered_in_prev_frame;
@@ -103,6 +106,10 @@ public:
 			shadow_atlas_size = 0;
 			disable_3d = false;
 			disable_3d_by_usage = false;
+			debug_draw = VS::VIEWPORT_DEBUG_DRAW_DISABLED;
+			for (int i = 0; i < VS::VIEWPORT_RENDER_INFO_MAX; i++) {
+				render_info[i] = 0;
+			}
 		}
 	};
 
@@ -168,6 +175,9 @@ public:
 	void viewport_set_hdr(RID p_viewport, bool p_enabled);
 	void viewport_set_usage(RID p_viewport, VS::ViewportUsage p_usage);
 
+	virtual int viewport_get_render_info(RID p_viewport, VS::ViewportRenderInfo p_info);
+	virtual void viewport_set_debug_draw(RID p_viewport, VS::ViewportDebugDraw p_draw);
+
 	void draw_viewports();
 
 	bool free(RID p_rid);

+ 5 - 0
servers/visual/visual_server_wrap_mt.h

@@ -372,6 +372,9 @@ public:
 	FUNC2(viewport_set_hdr, RID, bool)
 	FUNC2(viewport_set_usage, RID, ViewportUsage)
 
+	FUNC2R(int, viewport_get_render_info, RID, ViewportRenderInfo)
+	FUNC2(viewport_set_debug_draw, RID, ViewportDebugDraw)
+
 	/* ENVIRONMENT API */
 
 	FUNC0R(RID, environment_create)
@@ -552,6 +555,8 @@ public:
 
 	FUNC0R(RID, get_test_cube)
 
+	FUNC1(set_debug_generate_wireframes, bool)
+
 	virtual bool has_feature(Features p_feature) const { return visual_server->has_feature(p_feature); }
 	virtual bool has_os_feature(const String &p_feature) const { return visual_server->has_os_feature(p_feature); }
 

+ 24 - 0
servers/visual_server.h

@@ -590,6 +590,28 @@ public:
 	virtual void viewport_set_hdr(RID p_viewport, bool p_enabled) = 0;
 	virtual void viewport_set_usage(RID p_viewport, ViewportUsage p_usage) = 0;
 
+	enum ViewportRenderInfo {
+
+		VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME,
+		VIEWPORT_RENDER_INFO_VERTICES_IN_FRAME,
+		VIEWPORT_RENDER_INFO_MATERIAL_CHANGES_IN_FRAME,
+		VIEWPORT_RENDER_INFO_SHADER_CHANGES_IN_FRAME,
+		VIEWPORT_RENDER_INFO_SURFACE_CHANGES_IN_FRAME,
+		VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME,
+		VIEWPORT_RENDER_INFO_MAX
+	};
+
+	virtual int viewport_get_render_info(RID p_viewport, ViewportRenderInfo p_info) = 0;
+
+	enum ViewportDebugDraw {
+		VIEWPORT_DEBUG_DRAW_DISABLED,
+		VIEWPORT_DEBUG_DRAW_UNSHADED,
+		VIEWPORT_DEBUG_DRAW_OVERDRAW,
+		VIEWPORT_DEBUG_DRAW_WIREFRAME,
+	};
+
+	virtual void viewport_set_debug_draw(RID p_viewport, ViewportDebugDraw p_draw) = 0;
+
 	/* ENVIRONMENT API */
 
 	virtual RID environment_create() = 0;
@@ -912,6 +934,8 @@ public:
 
 	virtual bool has_os_feature(const String &p_feature) const = 0;
 
+	virtual void set_debug_generate_wireframes(bool p_generate) = 0;
+
 	VisualServer();
 	virtual ~VisualServer();
 };