Renderstep + Part resize (+ CFrame?) = Performance is kill

Im updating 5 parts CFrame + Size in a renderstep, and its taking the script load from a few percent to 80+, and the game starts running at ½ FPS.

And they are not even very interesting parts. Anchored, cancollide=false, regular brick. Something like 1x1xX in size where X is like a HUNDRED studs. No welds. They do have a blockmesh.

The worst part is, I already throttled the size changes to 4 Hz.

What fixes the issue:

  1. Not resizing at all (just commenting it out)
  2. Doing resize in regular loop - this is still much slower than I would expect (up to 20-30% script activity just for cframe/resize a few parts once a frame - and resize is limited to 4 Hz here as well)

Script activity is only a few percent when there is no resizing going on.

NOTE: This is either somewhat recent, or related to overall (physics?) load of the place. It was not as bad before (but there was still a reason for the 4 Hz size change rate limit - but that was enough to hide any issues when I added it)

EDIT:

Oh and its just resizing INSIDE renderstep that causes issues, it seems to be ok if I resize it from a regular loop while the renderstepped is dedicated to updating the cframe.

EDIT2:

I kinda forgot that theres actually 4 additional parts, not just 1. Those ones are 100 studs long and go through terrain water the whole way. So I guess that could make the issue more noticeable.

1 Like

The only things I can think of which would cause this is possibly something with GetRenderCFrame being very expensive, or that the script is somehow running multiple times.

I know some others who are changing the cframe of many parts on Renderstepped and they’re running perfectly fine.

Its not the CFrame change, its the size change.

If I only use GetRenderCFrame and change the CFrame, that works perfectly. But as soon as I also change size, it starts crawling.

And If I only do size+cframe (not getrendercframe), thats also better (still slow though)

have a repro place or something?

The script I did to test couldn’t pull anything near the numbers you were mentioning. Best I got was 3% with a really really big part.

It seems GetRenderCFrame() isnt related after all, it just randomly decided to be fast when I tried switching it. (I noticed I use it on different parts than the one I resize, anyways, so…)

That is what it looks like for a single part (60% main activity, Render is at 80%+, seems to settle over 90 if I wait a bit). Regular activity is few percent for main and render at 15% or so.

As for reproducing it, this is the renderstep event:

local attachmentPoint1=part1:GetRenderCFrame():pointToWorldSpace(a1)
local attachmentPoint2=part2:GetRenderCFrame():pointToWorldSpace(a2)
--midpoint=(attachmentPoint1+attachmentPoint2)/2
local relpos=attachmentPoint1-attachmentPoint2
local distance=relpos.magnitude
if self.lastVisualUpdate>0.25 then
		--local meshScale=Vector3.new(0.5,0.5,1)--distance+1)
		--mesh.Scale=meshScale
		local size=Vector3.new(1,1,distance+1)
		rope.Size=size
		self.lastVisualUpdate=0
	else
		self.lastVisualUpdate=self.lastVisualUpdate+t
	end
	local cframe=CFrame.new((attachmentPoint1+attachmentPoint2)/2, attachmentPoint2)
	rope.CFrame=cframe

(commenting out the rope.Size bit would make it not lag)

And the other relevant factors:

  1. Im also updating the CFrame and Size on the server, so those arent only getting changed by the renderstep event, but also by the replication of the properties from server. (on server its just regular loop, and same 4 Hz size change rate limit)
  2. Theres 10k+ parts in the place, which will probably affect performane of size changes in some way

Ill reduce the part count to see if thats actually related.
EDIT:
part count doesnt seem to have a significant effect (at least not a linear one). I still get 30-50 main activity and similar levels of render activity, even with most parts in the place removed. (keep in mind there is higher base load with a ton of parts, which should explain some of the reduction)
EDIT2:
Ill solve this for myself by doing the resizing in a regular loop, and only cframing in renderstep

Tested with this code:

Result:

Size is 6x more expensive to set than CFrame when the part is far away from other parts.
Size is 10x more expensive to set than CFrame when the part is nearby other parts.

RunService wasn’t running, so most of the overhead is apparently coming from that ridiculous “clip-above-intersecting-parts-even-though-we-didn’t-ask-for-that-to-happen” behavior. Why is that still a thing?

Although this is annoying on its own, resizing itself isnt problematic for me (its fast enough when I do it outside renderstep). Of course if resizing didnt do weird useless physics related checks, it might not cause slowdown for me anymore.

But for some strange reason having it in renderstep is slow.

RenderStepped blocks; other RunService events and wait() run parallel with rendering. You can generally boost framerate by putting expensive operations in Stepped or Heartbeat.

1 Like

This might be related

While that may be the root cause (size being expensive to change), I was more worried about the fact that doing it in renderstep specifically seemed to be ridiculously expensive. Not sure if its just the way renderstep works, based on what fractality posted. Its just unexpected…