Forráskód Böngészése

Merge pull request #61425 from clayjohn/GLES3-2D

Rémi Verschelde 3 éve
szülő
commit
20a1b85589

+ 127 - 63
drivers/gles3/rasterizer_canvas_gles3.cpp

@@ -445,6 +445,9 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
 }
 
 void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, uint32_t &r_index) {
+	// Used by Polygon and Mesh.
+	static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP };
+
 	RS::CanvasItemTextureFilter current_filter = state.default_filter;
 	RS::CanvasItemTextureRepeat current_repeat = state.default_repeat;
 
@@ -474,7 +477,10 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
 			continue;
 		}
 
-		_update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world);
+		if (c->type != Item::Command::TYPE_MESH) {
+			// For Meshes, this gets updated below.
+			_update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world);
+		}
 
 		for (int i = 0; i < 4; i++) {
 			state.instance_data_array[r_index].modulation[i] = 0.0;
@@ -680,30 +686,9 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
 					state.instance_data_array[r_index].ninepatch_margins[j] = 0;
 				}
 
-				// If the previous operation is not done yet, allocate a new buffer
-				if (state.fences[state.current_buffer] != GLsync()) {
-					GLint syncStatus;
-					glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus);
-					if (syncStatus == GL_UNSIGNALED) {
-						_allocate_instance_data_buffer();
-					} else {
-						glDeleteSync(state.fences[state.current_buffer]);
-					}
-				}
-
-				glBindBufferBase(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer]);
-#ifdef JAVASCRIPT_ENABLED
-				//WebGL 2.0 does not support mapping buffers, so use slow glBufferData instead
-				glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData), &state.instance_data_array[0], GL_DYNAMIC_DRAW);
-#else
-				void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
-				memcpy(ubo, &state.instance_data_array[0], sizeof(InstanceData));
-				glUnmapBuffer(GL_UNIFORM_BUFFER);
-#endif
+				_bind_instance_data_buffer(1);
 				glBindVertexArray(pb->vertex_array);
 
-				static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP };
-
 				if (pb->index_buffer != 0) {
 					glDrawElements(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, nullptr);
 				} else {
@@ -764,12 +749,18 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
 			case Item::Command::TYPE_MESH:
 			case Item::Command::TYPE_MULTIMESH:
 			case Item::Command::TYPE_PARTICLES: {
-				/*
+				GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton();
 				RID mesh;
 				RID mesh_instance;
 				RID texture;
 				Color modulate(1, 1, 1, 1);
-				int instance_count = 1;
+				uint32_t instance_count = 1;
+				GLuint multimesh_buffer = 0;
+				uint32_t multimesh_stride = 0;
+				uint32_t multimesh_color_offset = 0;
+				uint32_t multimesh_custom_data_offset = 0;
+				bool multimesh_uses_color = false;
+				bool multimesh_uses_custom_data = false;
 
 				if (c->type == Item::Command::TYPE_MESH) {
 					const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c);
@@ -781,26 +772,25 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
 				} else if (c->type == Item::Command::TYPE_MULTIMESH) {
 					const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c);
 					RID multimesh = mm->multimesh;
-					mesh = storage->multimesh_get_mesh(multimesh);
+					mesh = mesh_storage->multimesh_get_mesh(multimesh);
 					texture = mm->texture;
 
-					if (storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) {
+					if (mesh_storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) {
 						break;
 					}
 
-					instance_count = storage->multimesh_get_instances_to_draw(multimesh);
+					instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh);
 
 					if (instance_count == 0) {
 						break;
 					}
 
-					state.instance_data_array[r_index].flags |= 1; //multimesh, trails disabled
-					if (storage->multimesh_uses_colors(multimesh)) {
-						state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS;
-					}
-					if (storage->multimesh_uses_custom_data(multimesh)) {
-						state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA;
-					}
+					multimesh_buffer = mesh_storage->multimesh_get_gl_buffer(multimesh);
+					multimesh_stride = mesh_storage->multimesh_get_stride(multimesh);
+					multimesh_color_offset = mesh_storage->multimesh_get_color_offset(multimesh);
+					multimesh_custom_data_offset = mesh_storage->multimesh_get_custom_data_offset(multimesh);
+					multimesh_uses_color = mesh_storage->multimesh_uses_colors(multimesh);
+					multimesh_uses_custom_data = mesh_storage->multimesh_uses_custom_data(multimesh);
 				}
 
 				// TODO: implement particles here
@@ -816,8 +806,15 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
 				}
 
 				_bind_canvas_texture(texture, current_filter, current_repeat, r_index);
+				if (instance_count == 1) {
+					GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_ATTRIBUTES);
+				} else if (instance_count > 1) {
+					GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_INSTANCED);
+				} else {
+					ERR_PRINT("Must have at least one mesh instance to draw mesh");
+				}
 
-				uint32_t surf_count = storage->mesh_get_surface_count(mesh);
+				uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh);
 
 				state.instance_data_array[r_index].modulation[0] = base_color.r * modulate.r;
 				state.instance_data_array[r_index].modulation[1] = base_color.g * modulate.g;
@@ -829,19 +826,79 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
 					state.instance_data_array[r_index].dst_rect[j] = 0;
 					state.instance_data_array[r_index].ninepatch_margins[j] = 0;
 				}
-
+				_bind_instance_data_buffer(1);
 				for (uint32_t j = 0; j < surf_count; j++) {
-					RS::SurfaceData *surface = storage->mesh_get_surface(mesh, j);
+					void *surface = mesh_storage->mesh_get_surface(mesh, j);
 
-					RS::PrimitiveType primitive = storage->mesh_surface_get_primitive(surface);
+					RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface);
 					ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX);
 
-					glBindVertexArray(surface->vertex_array);
-					static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP };
+					GLuint vertex_array_gl = 0;
+					GLuint index_array_gl = 0;
+
+					uint32_t input_mask = 0; // 2D meshes always use the same vertex format
+					if (mesh_instance.is_valid()) {
+						mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, vertex_array_gl);
+					} else {
+						mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, vertex_array_gl);
+					}
+
+					index_array_gl = mesh_storage->mesh_surface_get_index_buffer(surface, 0);
+					bool use_index_buffer = false;
+					glBindVertexArray(vertex_array_gl);
+					if (index_array_gl != 0) {
+						glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_gl);
+						use_index_buffer = true;
+					}
+
+					if (instance_count > 1) {
+						// Bind instance buffers.
+						glBindBuffer(GL_ARRAY_BUFFER, multimesh_buffer);
+						glEnableVertexAttribArray(5);
+						glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0));
+						glVertexAttribDivisor(5, 1);
+						glEnableVertexAttribArray(6);
+						glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(4 * 4));
+						glVertexAttribDivisor(6, 1);
+
+						if (multimesh_uses_color) {
+							glEnableVertexAttribArray(7);
+							glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(multimesh_color_offset * sizeof(float)));
+							glVertexAttribDivisor(7, 1);
+						}
+						if (multimesh_uses_custom_data) {
+							glEnableVertexAttribArray(8);
+							glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(multimesh_custom_data_offset * sizeof(float)));
+							glVertexAttribDivisor(8, 1);
+						}
+					}
+
+					GLenum primitive_gl = prim[int(primitive)];
+					if (instance_count == 1) {
+						if (use_index_buffer) {
+							glDrawElements(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0);
+						} else {
+							glDrawArrays(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface));
+						}
+					} else if (instance_count > 1) {
+						if (use_index_buffer) {
+							glDrawElementsInstanced(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0, instance_count);
+						} else {
+							glDrawArraysInstanced(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), instance_count);
+						}
+					}
+
+					state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
 
-					// Draw directly, no need to batch
+					state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size();
+					glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+					if (instance_count > 1) {
+						glDisableVertexAttribArray(5);
+						glDisableVertexAttribArray(6);
+						glDisableVertexAttribArray(7);
+						glDisableVertexAttribArray(8);
+					}
 				}
-				*/
 			} break;
 			case Item::Command::TYPE_TRANSFORM: {
 				const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c);
@@ -886,26 +943,7 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
 
 void RasterizerCanvasGLES3::_render_batch(uint32_t &r_index) {
 	if (r_index > 0) {
-		// If the previous operation is not done yet, allocate a new buffer
-		if (state.fences[state.current_buffer] != GLsync()) {
-			GLint syncStatus;
-			glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus);
-			if (syncStatus == GL_UNSIGNALED) {
-				_allocate_instance_data_buffer();
-			} else {
-				glDeleteSync(state.fences[state.current_buffer]);
-			}
-		}
-
-		glBindBufferBase(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer]);
-#ifdef JAVASCRIPT_ENABLED
-		//WebGL 2.0 does not support mapping buffers, so use slow glBufferData instead
-		glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * r_index, state.instance_data_array, GL_DYNAMIC_DRAW);
-#else
-		void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData) * r_index, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
-		memcpy(ubo, state.instance_data_array, sizeof(InstanceData) * r_index);
-		glUnmapBuffer(GL_UNIFORM_BUFFER);
-#endif
+		_bind_instance_data_buffer(r_index);
 		glBindVertexArray(data.canvas_quad_array);
 		if (state.current_primitive_points == 0) {
 			glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, r_index);
@@ -939,6 +977,32 @@ void RasterizerCanvasGLES3::_render_batch(uint32_t &r_index) {
 	}
 }
 
+void RasterizerCanvasGLES3::_bind_instance_data_buffer(uint32_t p_max_index) {
+	if (p_max_index == 0) {
+		return;
+	}
+	// If the previous operation is not done yet, allocate a new buffer
+	if (state.fences[state.current_buffer] != GLsync()) {
+		GLint syncStatus;
+		glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus);
+		if (syncStatus == GL_UNSIGNALED) {
+			_allocate_instance_data_buffer();
+		} else {
+			glDeleteSync(state.fences[state.current_buffer]);
+		}
+	}
+
+	glBindBufferBase(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer]);
+#ifdef JAVASCRIPT_ENABLED
+	//WebGL 2.0 does not support mapping buffers, so use slow glBufferData instead
+	glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * p_max_index, state.instance_data_array, GL_DYNAMIC_DRAW);
+#else
+	void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData) * p_max_index, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
+	memcpy(ubo, state.instance_data_array, sizeof(InstanceData) * p_max_index);
+	glUnmapBuffer(GL_UNIFORM_BUFFER);
+#endif
+}
+
 RID RasterizerCanvasGLES3::light_create() {
 	return RID();
 }

+ 1 - 0
drivers/gles3/rasterizer_canvas_gles3.h

@@ -252,6 +252,7 @@ public:
 	void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer = false);
 	void _render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, uint32_t &r_index);
 	void _render_batch(uint32_t &p_max_index);
+	void _bind_instance_data_buffer(uint32_t p_max_index);
 	void _allocate_instance_data_buffer();
 
 	void set_time(double p_time);

+ 22 - 75
drivers/gles3/shaders/canvas.glsl

@@ -5,6 +5,7 @@ mode_quad =
 mode_ninepatch = #define USE_NINEPATCH
 mode_primitive = #define USE_PRIMITIVE
 mode_attributes = #define USE_ATTRIBUTES
+mode_instanced = #define USE_ATTRIBUTES \n#define USE_INSTANCING
 
 #[specializations]
 
@@ -20,6 +21,15 @@ layout(location = 4) in vec2 uv_attrib;
 layout(location = 10) in uvec4 bone_attrib;
 layout(location = 11) in vec4 weight_attrib;
 
+#ifdef USE_INSTANCING
+
+layout(location = 5) in highp vec4 instance_xform0;
+layout(location = 6) in highp vec4 instance_xform1;
+layout(location = 7) in lowp vec4 instance_color;
+layout(location = 8) in highp vec4 instance_custom_data;
+
+#endif
+
 #endif
 
 // This needs to be outside clang-format so the ubo comment is in the right place
@@ -77,13 +87,21 @@ void main() {
 	vec4 bone_weights = vec4(0.0);
 
 #elif defined(USE_ATTRIBUTES)
-
+#ifdef USE_INSTANCING
+	draw_data_instance = 0;
+#endif
 	vec2 vertex = vertex_attrib;
 	vec4 color = color_attrib * draw_data[draw_data_instance].modulation;
 	vec2 uv = uv_attrib;
 
 	uvec4 bones = bone_attrib;
 	vec4 bone_weights = weight_attrib;
+
+#ifdef USE_INSTANCING
+	color *= instance_color;
+	instance_custom = instance_custom_data;
+#endif
+
 #else
 
 	vec2 vertex_base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
@@ -98,81 +116,10 @@ void main() {
 
 	mat4 model_matrix = mat4(vec4(draw_data[draw_data_instance].world_x, 0.0, 0.0), vec4(draw_data[draw_data_instance].world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(draw_data[draw_data_instance].world_ofs, 0.0, 1.0));
 
-	// MultiMeshes don't batch, so always read from draw_data[0]
-	uint instancing = draw_data[0].flags & FLAGS_INSTANCING_MASK;
+#ifdef USE_INSTANCING
+	model_matrix = model_matrix * transpose(mat4(instance_xform0, instance_xform1, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)));
+#endif // USE_INSTANCING
 
-#ifdef USE_ATTRIBUTES
-/*
-	if (instancing > 1) {
-		// trails
-
-		uint stride = 2 + 1 + 1; //particles always uses this format
-
-		uint trail_size = instancing;
-
-		uint offset = trail_size * stride * gl_InstanceID;
-
-		vec4 pcolor;
-		vec2 new_vertex;
-		{
-			uint boffset = offset + bone_attrib.x * stride;
-			new_vertex = (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.x;
-			pcolor = transforms.data[boffset + 2] * weight_attrib.x;
-		}
-		if (weight_attrib.y > 0.001) {
-			uint boffset = offset + bone_attrib.y * stride;
-			new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.y;
-			pcolor += transforms.data[boffset + 2] * weight_attrib.y;
-		}
-		if (weight_attrib.z > 0.001) {
-			uint boffset = offset + bone_attrib.z * stride;
-			new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.z;
-			pcolor += transforms.data[boffset + 2] * weight_attrib.z;
-		}
-		if (weight_attrib.w > 0.001) {
-			uint boffset = offset + bone_attrib.w * stride;
-			new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.w;
-			pcolor += transforms.data[boffset + 2] * weight_attrib.w;
-		}
-
-		instance_custom = transforms.data[offset + 3];
-
-		vertex = new_vertex;
-		color *= pcolor;
-	} else*/
-#endif // USE_ATTRIBUTES
-/*
-	{
-		if (instancing == 1) {
-			uint stride = 2;
-			{
-				if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_COLORS)) {
-					stride += 1;
-				}
-				if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
-					stride += 1;
-				}
-			}
-
-			uint offset = stride * gl_InstanceID;
-
-			mat4 matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
-			offset += 2;
-
-			if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_COLORS)) {
-				color *= transforms.data[offset];
-				offset += 1;
-			}
-
-			if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
-				instance_custom = transforms.data[offset];
-			}
-
-			matrix = transpose(matrix);
-			model_matrix = model_matrix * matrix;
-		}
-	}
-*/
 #if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
 	if (bool(draw_data[draw_data_instance].flags & FLAGS_USING_PARTICLES)) {
 		//scale by texture size

+ 12 - 1
drivers/gles3/storage/mesh_storage.cpp

@@ -722,6 +722,18 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
 
 	for (int i = 0; i < RS::ARRAY_INDEX; i++) {
 		if (!attribs[i].enabled) {
+			glDisableVertexAttribArray(i);
+			if (s->format & RS::ARRAY_FLAG_USE_2D_VERTICES) {
+				if (i == RS::ARRAY_COLOR) {
+					glVertexAttrib4f(i, 1, 1, 1, 1);
+				} else if (i == RS::ARRAY_TEX_UV) {
+					glVertexAttrib2f(i, 1, 1);
+				} else if (i == RS::ARRAY_BONES) {
+					glVertexAttrib4f(i, 1, 1, 1, 1);
+				} else if (i == RS::ARRAY_WEIGHTS) {
+					glVertexAttrib4f(i, 1, 1, 1, 1);
+				}
+			}
 			continue;
 		}
 		if (i <= RS::ARRAY_TANGENT) {
@@ -941,7 +953,6 @@ void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::
 	multimesh->stride_cache = multimesh->custom_data_offset_cache + (p_use_custom_data ? 4 : 0);
 	multimesh->buffer_set = false;
 
-	//print_line("allocate, elements: " + itos(p_instances) + " 2D: " + itos(p_transform_format == RS::MULTIMESH_TRANSFORM_2D) + " colors " + itos(multimesh->uses_colors) + " data " + itos(multimesh->uses_custom_data) + " stride " + itos(multimesh->stride_cache) + " total size " + itos(multimesh->stride_cache * multimesh->instances));
 	multimesh->data_cache = Vector<float>();
 	multimesh->aabb = AABB();
 	multimesh->aabb_dirty = false;

+ 20 - 0
drivers/gles3/storage/mesh_storage.h

@@ -493,6 +493,26 @@ public:
 		return multimesh->instances;
 	}
 
+	_FORCE_INLINE_ GLuint multimesh_get_gl_buffer(RID p_multimesh) const {
+		MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
+		return multimesh->buffer;
+	}
+
+	_FORCE_INLINE_ uint32_t multimesh_get_stride(RID p_multimesh) const {
+		MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
+		return multimesh->stride_cache;
+	}
+
+	_FORCE_INLINE_ uint32_t multimesh_get_color_offset(RID p_multimesh) const {
+		MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
+		return multimesh->color_offset_cache;
+	}
+
+	_FORCE_INLINE_ uint32_t multimesh_get_custom_data_offset(RID p_multimesh) const {
+		MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
+		return multimesh->custom_data_offset_cache;
+	}
+
 	/* SKELETON API */
 
 	Skeleton *get_skeleton(RID p_rid) { return skeleton_owner.get_or_null(p_rid); };

+ 99 - 0
servers/rendering/renderer_canvas_render.cpp

@@ -29,3 +29,102 @@
 /*************************************************************************/
 
 #include "renderer_canvas_render.h"
+#include "servers/rendering/rendering_server_globals.h"
+
+const Rect2 &RendererCanvasRender::Item::get_rect() const {
+	if (custom_rect || (!rect_dirty && !update_when_visible)) {
+		return rect;
+	}
+
+	//must update rect
+
+	if (commands == nullptr) {
+		rect = Rect2();
+		rect_dirty = false;
+		return rect;
+	}
+
+	Transform2D xf;
+	bool found_xform = false;
+	bool first = true;
+
+	const Item::Command *c = commands;
+
+	while (c) {
+		Rect2 r;
+
+		switch (c->type) {
+			case Item::Command::TYPE_RECT: {
+				const Item::CommandRect *crect = static_cast<const Item::CommandRect *>(c);
+				r = crect->rect;
+
+			} break;
+			case Item::Command::TYPE_NINEPATCH: {
+				const Item::CommandNinePatch *style = static_cast<const Item::CommandNinePatch *>(c);
+				r = style->rect;
+			} break;
+
+			case Item::Command::TYPE_POLYGON: {
+				const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c);
+				r = polygon->polygon.rect_cache;
+			} break;
+			case Item::Command::TYPE_PRIMITIVE: {
+				const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c);
+				for (uint32_t j = 0; j < primitive->point_count; j++) {
+					if (j == 0) {
+						r.position = primitive->points[0];
+					} else {
+						r.expand_to(primitive->points[j]);
+					}
+				}
+			} break;
+			case Item::Command::TYPE_MESH: {
+				const Item::CommandMesh *mesh = static_cast<const Item::CommandMesh *>(c);
+				AABB aabb = RSG::mesh_storage->mesh_get_aabb(mesh->mesh, RID());
+
+				r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
+
+			} break;
+			case Item::Command::TYPE_MULTIMESH: {
+				const Item::CommandMultiMesh *multimesh = static_cast<const Item::CommandMultiMesh *>(c);
+				AABB aabb = RSG::mesh_storage->multimesh_get_aabb(multimesh->multimesh);
+
+				r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
+
+			} break;
+			case Item::Command::TYPE_PARTICLES: {
+				const Item::CommandParticles *particles_cmd = static_cast<const Item::CommandParticles *>(c);
+				if (particles_cmd->particles.is_valid()) {
+					AABB aabb = RendererRD::ParticlesStorage::get_singleton()->particles_get_aabb(particles_cmd->particles);
+					r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
+				}
+
+			} break;
+			case Item::Command::TYPE_TRANSFORM: {
+				const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c);
+				xf = transform->xform;
+				found_xform = true;
+				[[fallthrough]];
+			}
+			default: {
+				c = c->next;
+				continue;
+			}
+		}
+
+		if (found_xform) {
+			r = xf.xform(r);
+		}
+
+		if (first) {
+			rect = r;
+			first = false;
+		} else {
+			rect = rect.merge(r);
+		}
+		c = c->next;
+	}
+
+	rect_dirty = false;
+	return rect;
+}

+ 1 - 97
servers/rendering/renderer_canvas_render.h

@@ -356,103 +356,7 @@ public:
 
 		Rect2 global_rect_cache;
 
-		const Rect2 &get_rect() const {
-			if (custom_rect || (!rect_dirty && !update_when_visible)) {
-				return rect;
-			}
-
-			//must update rect
-
-			if (commands == nullptr) {
-				rect = Rect2();
-				rect_dirty = false;
-				return rect;
-			}
-
-			Transform2D xf;
-			bool found_xform = false;
-			bool first = true;
-
-			const Item::Command *c = commands;
-
-			while (c) {
-				Rect2 r;
-
-				switch (c->type) {
-					case Item::Command::TYPE_RECT: {
-						const Item::CommandRect *crect = static_cast<const Item::CommandRect *>(c);
-						r = crect->rect;
-
-					} break;
-					case Item::Command::TYPE_NINEPATCH: {
-						const Item::CommandNinePatch *style = static_cast<const Item::CommandNinePatch *>(c);
-						r = style->rect;
-					} break;
-
-					case Item::Command::TYPE_POLYGON: {
-						const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c);
-						r = polygon->polygon.rect_cache;
-					} break;
-					case Item::Command::TYPE_PRIMITIVE: {
-						const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c);
-						for (uint32_t j = 0; j < primitive->point_count; j++) {
-							if (j == 0) {
-								r.position = primitive->points[0];
-							} else {
-								r.expand_to(primitive->points[j]);
-							}
-						}
-					} break;
-					case Item::Command::TYPE_MESH: {
-						const Item::CommandMesh *mesh = static_cast<const Item::CommandMesh *>(c);
-						AABB aabb = RendererRD::MeshStorage::get_singleton()->mesh_get_aabb(mesh->mesh, RID());
-
-						r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
-
-					} break;
-					case Item::Command::TYPE_MULTIMESH: {
-						const Item::CommandMultiMesh *multimesh = static_cast<const Item::CommandMultiMesh *>(c);
-						AABB aabb = RendererRD::MeshStorage::get_singleton()->multimesh_get_aabb(multimesh->multimesh);
-
-						r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
-
-					} break;
-					case Item::Command::TYPE_PARTICLES: {
-						const Item::CommandParticles *particles_cmd = static_cast<const Item::CommandParticles *>(c);
-						if (particles_cmd->particles.is_valid()) {
-							AABB aabb = RendererRD::ParticlesStorage::get_singleton()->particles_get_aabb(particles_cmd->particles);
-							r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
-						}
-
-					} break;
-					case Item::Command::TYPE_TRANSFORM: {
-						const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c);
-						xf = transform->xform;
-						found_xform = true;
-						[[fallthrough]];
-					}
-					default: {
-						c = c->next;
-						continue;
-					}
-				}
-
-				if (found_xform) {
-					r = xf.xform(r);
-				}
-
-				if (first) {
-					rect = r;
-					first = false;
-				} else {
-					rect = rect.merge(r);
-				}
-				c = c->next;
-			}
-
-			rect_dirty = false;
-			return rect;
-		}
+		const Rect2 &get_rect() const;
 
 		Command *commands = nullptr;
 		Command *last_command = nullptr;