Unity - renderer.material issue

edited in Questions and Answers
Hi Guys. For my comp E entry i've been messing around with materials on objects. My game is having performance issues and I now read that it could be because of renderer.material which is creating clones but not destroying them, so they are left as assets in my game (or something like that). I basically have the following script attached to objects, I do a raycast and then send a message activatePower/deactivatePower to the object which changes the material on the object from 1 material to another, and then back again:

public Material activatedMaterial;
private Material deactivatedMaterial;

void Start()
{
	deactivatedMaterial = renderer.material;
}

void activatePower()
{
	renderer.material = activatedMaterial;
}

void deactivatePower()
{
        renderer.material = deactivatedMaterial;
}


Could this be what is causing the performance issue? Can someone explain to me in "Noob speak" what the issue with this is, and a possible solution.

Comments

  • As far as I understand, It only clones the material if other renderers are using it. So if you only have one object using the material this should be fine. If you have multiple objects sharing the same material then you want to use sharedMaterial:

    deactivatedMaterial = renderer.sharedMaterial;
    renderer.sharedMaterial = activatedMaterial;

    This will just assign the material as a reference, rather than creating a local instance.

    http://docs.unity3d.com/Documentation/ScriptReference/Renderer-material.html
  • I don't think what you've got there would cause it. It could be a whole lot of different problems... perhaps if you post what's in your Stats window (the little toggle on the top right of your Game view) there'd be more clues there.

    As far as I know, it's only when you change properties on a renderer.material (as opposed to renderer.sharedMaterial) that you bleed instances into your scene, and wreak havoc on your draw calls. It doesn't happen if you're assigning existing materials to your renderers.
  • @Merrik - thank you so much. Can't believe the solution is that simple. I will try that out tonight when I get home and see if it improves the performance of my game. The help really is much appreciated.
  • Elyaradine said:
    As far as I know, it's only when you change properties on a renderer.material
    Ah, I think you're right.
  • @Elyaradine - I will post my Stats window when I get home. Was hoping it was a simple solution like renderer.sharedMaterial.
  • @Elyaradine - I've attached a screenshot of my Stats window as requested. Let me just be honest upfront and say that I have absolutely no Idea what any of those stats mean. I am completely new to development, but would really appreciate if you could have a look at it and see if you can see what could be the cause of my performance issue. As stated above I thought it had something to do with the renderer.material calls I was making in script. The game runs fine on my system (mid-range) but some other players have posted that the game is Lagging like crazy on their machines. Any help would be appreciated.
    stat screen.png
    819 x 460 - 145K
  • So... it could be any of a number of other things too (and unfortunately figuring out what exactly it is requires quite a bit of messing around, turning things on and off, etc.), but from that it seems as if the number of draw calls could be your bottleneck.

    This could indeed happen if you're getting instanced materials all over the place, so definitely check for that. (While the game is playing in the editor, go over your objects to see if any of them are using materials with the "(Instance)" suffix to them. You could write a handy script to check for this.) Otherwise, try and get as many of your objects in your scene to use the same materials. If they're using the same shader, you can look at combining several of your smaller textures into one larger texture, and assigning your meshes' UVs to the new texture, so that they can share materials. Even without static batching (which is a Pro feature), dynamic batching could lower your draw calls quite a bit.

    --
    I'll try and explain in "noob speak" what's happening, bearing in mind that I'm no graphics programmer, so my analogy could be kind of wrong. :P

    Imagine that a draw call is like a school bus, taking kids to work (work being your graphics card). The bus can only take kids who're wearing the same school uniform. Whether the bus is full, or whether the bus only has a couple of students, it still takes a lot of time to get to school. So, one of the ways that you could make the overall trip faster, is if you try and group your students so that students who're going to the same school are on the bus at the same time, so that you're getting as many kids to the same place as possible. This is batching. You're effectively combining a whole bunch of smaller meshes that use the same material (the same "school uniform") into one larger mesh, so that they can be transported all at once.

    (And, in reality, there are things in addition to materials that can require more draw calls, like whether you're using real-time lights, or if you're using transparency, or some other things I've probably forgotten.)
  • @FanieG: I highly doubt that your performance problems come from anything graphics related. Not with that framerate... What is the game trying to do when your problems happen?
  • @Elyaradine - thank you so much for the explanation that does make more sense now. I will research batching a bit and try out your suggestion. Thanks again. The help is much appreciated.
  • @dislekcia - It runs fine on my machine, but some players have been complaining about lag, which is so bad that they can't play
  • @dislekcia - Also, my framerate does drop down very low at points too. like 13-17 FPS
  • @FanieG: So take a screenshot of your stats when it drops to those low framerates ;) That'll quickly tell you if it really is a graphics problem, or if it's a computational thing: If it's computation, your main thread will be taking up a lot more time than your renderer. If it's a graphics issue, then your renderer will be taking longer. That's the first line of those stats ;)
  • edited
    @dislekcia - here is the screenshot of the low framerates:

    https://www.dropbox.com/s/ys8lw2tgm99812d/Screenshot 2013-10-24 22.39.15.png

    main thread is 76.3ms and renderer is 0.6ms - so I take it is a computation issue

    But it fluctuates greatly. Did you have a lag issue when you ran my comp E entry? I know you are judging the comp, so if you do not want to check now I would totally understand. Can someone else maybe check too?
  • edited
    I was also getting ridiculously laggy and low performance when I tried out your game @FanieG.

    I dont think this is the cause of your performance issues, but what was the reason for drawing out all the letters on your signposts with cubes, instead of just putting the message in a texture?

    Oh and another thing @FanieG, it doesnt look like your walls are prefabs. Make sure all the often-repeated and non-moving stuff in your game are made into Prefabs and Marked as Static. This will help Unity batch your geometry more efficiently. I'm no Unity expert - so someone please correct me if this advice wrong.
  • @Nitrogen - created the first 1 as a sort of doodle while I was watching tele. Thought it was funny so I made a few more. Here is a build without them:

    https://dl.dropboxusercontent.com/u/187819585/Compe E 5.4/Compe E 5.4.html

    It didn't do much to my main thread and renderer stats, but my draw calls did come down significantly. Would you mind terribly to give this build a try and see if there is any improvement in the lag?
  • edited
    Yeah, it didnt make much of a difference. Still seems to be running at around 10-15fps. Looking at the sky or the floor doesnt seem to help at all either.

  • @Nitrogen - thanks for trying. Really don't know what else to try. Don't really know where to start troubleshooting either.
  • Are you running any hectic code on each frame update? Something like doing a Ray intersection test with every single wall in the game?
  • i am raycasting for the shooting and for the special power. I didn't think that raycasting is a very expensive operation. Is it? I will try deactivating that and see how much of a difference that makes.
  • disabled all of my raycasting scripts - made absolutely no difference to my stats. Wonder if it is my pathfinding? I'm using Dijksta's algorythm with nodes throughout the maze. Will play around with those over the weekend and see what a difference they make. Any other suggestions are more than welcome.
  • edited
    Nitrogen said:
    made into Prefabs and Marked as Static. This will help Unity batch your geometry more efficiently. I'm no Unity expert - so someone please correct me if this advice wrong.
    This is right, except that static batching is a Pro feature.

    Raycasts are pretty cheap, afaik.
  • okay so I disabled the pathfinding script on my AI and Player and my Main thread stat dropped from 70ms down to 20-30ms, and my frame rate went from 13-20 to 30-45 FPS. Now I suppose i am to aim for 60FPS, but what is a good time for the main thread. Also what could be the reason for my pathfinding influencing my script so heavely. Do have too many nodes? Is my scripting just kak? any Ideas or suggestions?
  • @FanieG: Welcome down the rabbit hole...

    Dijkstra's alg is exhaustive, so you end up traversing every single node in order to find a path. That's why it doesn't seem to matter where the things are that you're trying to use pathfinding for - they have to calculate the same amount no matter what. Try reading up on related pathfinding algorithms like A* (which is similar to Dijkstra's alg) except is all about early solutions and avoiding redundant computation. Or do your pathfinding beforehand and just store route data in your nodes "When this looks like this, go here" instead of re-calculating that every frame.

    Also, try distributing your pathfinding out across multiple frames, once you know which node you want to go to, you shouldn't have to keep running the main pathfinding logic until you either get to that node or don't reach it for X seconds or whatever.
  • edited
    I think you could probably get your algorithm to run a lot better with some optimisation.

    It sounds like you're recalculating the pathfinding every single frame - there's a whole bunch of optimisations you can do: only recalulating when you need to, only recalculating the last bit of the solution that has changed, to spreading the pathfinding out over several frames or switching to a new algorithm like @dislekcia suggested.

    Pitty it only improved your framerate marginally though.
  • Okay, so I have 4 areas in my scene. Each has it's own nodes. Currently the AI in those areas only start following the path once I occupy 1 of the nodes in their area, but the script is being called every update. Would it help it I disable the node script (telling them which node they belong to. I belong to, and to follow the path to me) and then activate the script via a trigger in their area. Also disable it again when I leave their area (again with a trigger). That way they are not checking for all of that data every update call, except for the area that I am currently in. Sorry if I am being inarticulate with my explanations. I'm new to scripting (and extremely afrikaans) so I hope my question makes sense.
  • edited
    It's very difficult to solve these sorts of problems without sitting down with your code, so I can only tell you how I would do it:

    Firstly only recalculate which nodes the player and monster belong to when you need to do pathfinding.
    Secondly I would use a state machine for the Ai. When the Ai is moving between two nodes, you dont need to run the pathfinding algo, he just has to get to the next node. Once there he can make a decission as to which node to get to next - find which node the player is nearest, find where it is, and find a path between. Once this has been done once, the Ai should have the next node he needs to move to, and should not pathfind again until reaching it.

    So you have a "moving" state and a "searching" state. 99% of the time, the Ai is just moving slowly to the next node in the "moving" state. When he reaches it, you switch to the "searching" state for 1 frame and do a single pathfinding run. When pathfinding is complete and he has a destination you switch back to "moving" to the next node.

    Thats how I'd approach the problem. That way you only run the pathfinding once when the Ai reaches a node.
  • @Nitrogen - thanks again for the advice. I will try to implement it that way and see what happens to performance. Also will look at sizes of assets and research batching. Just sucks that free unity doesn't have static batching, because from what I read that can make a huge difference to performance. I'm a hobbyist so forking the kind of cash needed for Pro license is not something I can really afford.
  • It's fine to read up about batching for your own learning, but if the gpu isn't your bottleneck, optimizing things there is going to do nothing for your performance. :)
  • Batching is actually a CPU bottleneck :) It's the time that it takes the CPU to kick off rendering requests to the GPU, so batching can be very, VERY important to get your CPU time down. But if you're running on even an average PC, you can probably get away with 500+ draw calls... even more. Mobile and low end CPUs are where you need to start getting concerned about draw calls.
    Thanked by 1Elyaradine
Sign In or Register to comment.