Performance question when rendering models

Hi guys, I faced an issue with profiling my game. I’m creating a simple mobile game with a lot of objects, each object has about 20 vertices and I have about 600 objects in the scene on my Mac it works fine, but on the phone, the game takes about 20-30ms to draw a frame and it looks freezing.

In the built-in profiler, I can see that these objects take about 4ms for an update, but the update method for these objects is empty, it really strange for me.

Script.update@/main/entities/obj.script	3.36	600
RenderScript.UpdateRSI	5.51

Also, I run the game with profile UI enabled and I see that top of slow methods are:

clear screen - up to 10ms (I do it twice per frame)
draw render list - up to 15-20ms
setvertexbufferdata - 10ms
renderbatch - 5ms
renderbatchworld - 5ms

resources:

ModelVertexBuffer - 13217280
DrawCalls	  - 43

The question is why it takes so much time to draw 600 simple objects, they are in the same collection proxy, the same model, the same material and I do not pass any shader constants per object, only on init I use the same tint for each object. And why I have so much draw calls?

Can you explain this?

If they are always the same you can pass the tint as a render constant when drawing the predicate tag which should not break batching.

There may be other issues with your game. If you can upload a stripped down sample where you still see the frame time issues that can help diagnose it and enable us to suggest improvements.

1 Like

We have several caveats with our model rendering.

One is batching. If the material uses World Space, then we batch all the models into a single vertex buffer. This takes time on the CPU, but saves draw calls, but is costly on lower end devices.

Second issue is that we don’t support instancing. If you instead choose Local Space vertices in the material, each model will produce a separate draw call.

I hope we can work on instancing this year, as we very recently updated our engine to support OpenGLES 3. But we have no ETA for this feature.

3 Likes

Until the instancing is implemented you can try separating your models and scripts, have a single script that manages all instances of your model.
If the model is simple, you can win a few ms by using a custom material and removing unused lines from the shader.
Also it’s interesting if you would get different results with the mesh component instead of the model component. You’d have to try both local space and world space coordinates.

I just run set_constant in the init method, like this:

model.set_constant("#model", "tint", vmath.vector4(...))

I will try your suggestions and post the results here. Thank you

Using mesh instead of model even worse, now it takes hundreds of draw calls.

1 Like

Even doing this on init will break drawcalls. If the tint is consistent you need to set it in your render script as a render constant when drawing the model predicate tag.

I tried to remove this line, but nothing has changed.

You can send your project to @britzl or @Mathias_Westerdahl so they can see the main cause of breaking batching. If you are okay with posting it publicly you can upload as zip here. Or make stripped down version which still has too many drawcalls and post here as zip.

1 Like

sure, will prepare the code. thank you

perf_test.zip (179.2 KB)

So this is a minimal sample of what I do in real code. You can see that it takes 4ms to update 1000 objects
Found strange issue, now it takes only 3 draw calls, will investigate further.
FPS drops to 45 on my phone

One more thing, can I create more than 2048 objects? When I increse limits it crush with this error:

ERROR:CRASH: CALL STACK:

# 0 pc      0x91d7d libc++abi.dylib _sigtramp+29
# 1 pc     0x1beb85 dmengine _ZN5dmRigL9DoAnimateEPNS_10RigContextEPNS_11RigInstanceEf+21
# 2 pc     0x1bbda1 dmengine _ZN5dmRig6UpdateEPNS_10RigContextEf+241
# 3 pc      0x586fd dmengine _ZN12dmGameSystem15CompModelUpdateERKN12dmGameObject22ComponentsUpdateParamsERNS0_22ComponentsUpdateResultE+45
# 4 pc      0x215b8 dmengine _ZN12dmGameObject6UpdateEPNS_16CollectionHandleEPKNS_13UpdateContextE+584
# 5 pc      0x454eb dmengine _ZN12dmGameSystem25CompCollectionProxyUpdateERKN12dmGameObject22ComponentsUpdateParamsERNS0_22ComponentsUpdateResultE+507
# 6 pc      0x215b8 dmengine _ZN12dmGameObject6UpdateEPNS_16CollectionHandleEPKNS_13UpdateContextE+584
# 7 pc       0x681f dmengine _ZN8dmEngine4StepEPNS_6EngineE+991
# 8 pc       0x7aba dmengine _Z14dmEngineUpdatePN8dmEngine6EngineE+26
# 9 pc       0x7ced dmengine _ZN8dmEngine7RunLoopEPKNS_13RunLoopParamsE+109
#10 pc       0x7c52 dmengine _Z11engine_mainiPPc+98

Which limit have you increased in game.project? The model max count?

Did the crash happen with the test project you shared?

I created a ticket to investigate: https://github.com/defold/defold/issues/5794

The limit is 2048. Yes, it can be reproduced using the test project