Bad performance with Viewport textures as material

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By ksubox

Hello,

I try to check power of Godot and how well is it for my purposes.
I need “render to texture” many 2D objects and then draw that textures in 3D space.
So I made test script to create 4 materials based on 4 Viewport textures. In every viewport I put AnimatedSprite. Then I use that 4 materials (yes, just 4) to draw thousands of meshes.
But I can draw well only 2-3 thousands of meshes without dropping fps. For me it’s very strange. Engine should support at least 2-3 hundreds of materials and more than 10000 objects I guess.

Could somebody comment this ?
Actually I need about 500 dynamic materials in scene and 3000-4000 objects, using that materials.

Here is the script:

extends Spatial

var title = "Game: "
var meshes = []

# Called when the node enters the scene tree for the first time.
func _ready():
	var materials : Array = []
	# create several materials
	for i in range(1,5): # 1 - 4
		var newVPort : Viewport = createVieport()
		add_child(newVPort)
		var asprite : AnimatedSprite
		match i:
			1:
				asprite = createAnimatedSprite(["res://anim/anim_1_1.png", "res://anim/anim_1_2.png", "res://anim/anim_1_3.png"])
			2:
				asprite = createAnimatedSprite(["res://anim/anim_2_1.png", "res://anim/anim_2_2.png", "res://anim/anim_2_3.png", "res://anim/anim_2_4.png", "res://anim/anim_2_5.png", "res://anim/anim_2_6.png", "res://anim/anim_2_7.png", "res://anim/anim_2_8.png", "res://anim/anim_2_9.png", "res://anim/anim_2_10.png", "res://anim/anim_2_11.png", "res://anim/anim_2_12.png", "res://anim/anim_2_13.png", "res://anim/anim_2_14.png"])
			3:
				asprite = createAnimatedSprite(["res://anim/anim_3_1.png", "res://anim/anim_3_2.png", "res://anim/anim_3_3.png", "res://anim/anim_3_4.png"])
			4:
				asprite = createAnimatedSprite(["res://anim/anim_4_1.png", "res://anim/anim_4_2.png", "res://anim/anim_4_3.png", "res://anim/anim_4_4.png"])
		asprite.centered = false;
		newVPort.add_child(asprite)
		var tex = newVPort.get_texture()
		var mat = createMaterial(tex)
		materials.append(mat)

	for i in range(1, 2000):
		var mesh : MeshInstance = createMesh()
		mesh.material_override = materials[randi() % materials.size()]
		add_child(mesh)
		var newPos = Vector3(-2.0 + randf()*4, -randf(), -2.0 + randf()*4)
		mesh.translate(newPos)

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	OS.set_window_title(title + " | fps: " + str(Engine.get_frames_per_second()))
	#print(Performance.get_monitor(Performance.TIME_FPS))
	#print(Performance.get_monitor(Performance.TIME_PROCESS))
	pass

func createVieport():
	var vport:Viewport = Viewport.new()
	vport.size.x = 64
	vport.size.y = 64
	vport.usage = Viewport.USAGE_2D
	vport.disable_3d = true
	vport.hdr = false
	vport.render_target_v_flip = true
	return vport

func createMaterial(texParam:Texture):
	var mat:SpatialMaterial = SpatialMaterial.new()
	mat.albedo_color = Color.white
	mat.albedo_texture = texParam
	return mat

func createMesh():
	var pmesh = PlaneMesh.new()
	pmesh.size = Vector2(0.5, 0.5)
	var mesh:MeshInstance = MeshInstance.new()
	mesh.mesh = pmesh
	mesh.rotate(Vector3(1, 0, 0), PI/2)
	return mesh

func createAnimatedSprite(imgArr:Array):
	var frames:SpriteFrames = SpriteFrames.new()
	frames.add_animation("default")
	frames.set_animation_loop("default", true)
	var w = 0
	var h = 0
	for fname in imgArr:
		var res:Texture = load(fname) #StreamTexture
		if res != null:
			frames.add_frame("default", res, -1)
			if w == 0:
				w = res.get_width()
			if h == 0:
				h = res.get_height()
	var sprite:AnimatedSprite = AnimatedSprite.new()
	var scale = min(64.0/w, 64.0/h)
	sprite.scale = Vector2(scale, scale)
	sprite.frames = frames
	sprite.animation = "default"
	sprite.play("default")
	return sprite

Did you try out using a Multimesh instead? It seems to be the right thing for your use case:
MultiMesh — Godot Engine (3.1) documentation in English

Also:
I encountered some performance issues (while loading adding/removing nodes) in Godot 2.15 when many nodes where flat as children under one node. At that time I cicumvented that by grouping the nodes under multiple sub-nodes. But I have no idea it that problem still exists in Godot 3.1

wombatstampede | 2019-03-19 11:22

Thank you for reply.
Actually Multimesh is not what I need, because I should have almost every mesh with unique material. I would say more clear - every mesh should have multiple surfaces and every surface should have unique material.

So, today tested grouping with subnodes - nothing changed.
Also made 6 surface cubes (with SurfaceTool) and assign different dynamic materials to different surfaces. Result is even worse :frowning:

I wonder is it possible to find bottleneck in Godot pipeline ?

ksubox | 2019-03-20 06:31

Were you able to get around this issue? I am currently trying to create multiple viewports as textures, and when I use add_child in my main scene, it freezes for 1-2 seconds…

zecaribeiro | 2021-04-04 17:34