Search Unity

Can I change a SpriteRenderer.sprite in a job?

Discussion in 'Entity Component System' started by Rocky_Unity, Jun 12, 2021.

  1. Rocky_Unity

    Rocky_Unity

    Joined:
    Oct 13, 2017
    Posts:
    96
    Hey guys,
    I wrote an animator in the job system to help myself get acquainted with it. It makes sense to do, until you run into the part where only the main thread (as far as I know) can alter a SpriteRenderer.sprite. This is what I currently do, and it does work fine since I only iterate over a list of animators that need their sprites changed.. But it would be much better if I could let the job thread handle it.

    Is there a possible way to override the safety so that I can set a new sprite from within a job thread? It's one of those things that I know, "on my honor", I will never have two threads trying to write a new sprite to the same reference at the same time. I thought I read somewhere that you could do this sort of "overruling".

    Any suggestions?

    Thanks!
    -Rocky
     
  2. Rocky_Unity

    Rocky_Unity

    Joined:
    Oct 13, 2017
    Posts:
    96
    Bumping, any thoughts on this?
     
  3. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    i'm having the same problem still, 1/25/22
     
  4. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    This is important because I am trying to create a drop shadow on hundreds of players but need it to stay out of the update thread... :(
     
  5. TheSmokingGnu

    TheSmokingGnu

    Joined:
    May 1, 2017
    Posts:
    22
    Hello, affaik you cannot access any managed data from a Burst code - so nothing that is garbage collected basically - and SpriteRenderer is, since it's a Monobehavior, which is a class. Such code is not compileable with burst at all, and jobs have to be bursted, so I think this is impossible at least for this reason.
    It is also a physical restriction, not just a safety one - you can't work with managed data in a unmanaged way straight away - you would need to "pin" the pointer, so that it is not changed (which GC can do randomly). This has to be done on the main thread as well.
     
  6. Rocky_Unity

    Rocky_Unity

    Joined:
    Oct 13, 2017
    Posts:
    96
    You can give this a try Ziron!

    https://coffeebraingames.wordpress.com/2019/03/17/run-managed-code-in-unitys-job-system/


    I decided against my idea of a multi threaded animator. It's not conducive to Unity's GameObject architecture, and would need to be implemented in an ECS style architecture instead.
    If you need something similar, you may need to consider writing your own stuff instead of using SpriteRenderer. But maybe give the link above a try and maybe it'll work for what you need! :)

    As for TheSmokingGnu's comment, they are somewhat correct but not entirely. You aren't operating on pointers in Burst code with unsafe context. The reason the job system tries to stop you is because you are no longer thread safe, and that is not the design of the job system. The job system guarantees thread safety, protecting the coder from making mistakes. It gets clever and uses unmanaged memory for its data collection for extremely fast transactions of data between regular code and the executed job (because it doesn't need to copy data to and from the job). You can however, trick it, and get into managed code if you try hard enough, as seen in the article above. It's just not the design intent of the job system though
     
  7. TheSmokingGnu

    TheSmokingGnu

    Joined:
    May 1, 2017
    Posts:
    22
    @Rocky_Unity
    Thanks for some more details and that post.
    1) I don't think the jobs are bursted - interesting, I thought all the jobs have to be. This eliminates at least one obstacle (however the non-bursted code is slower).

    You said that "You aren't operating on pointers in Burst code with unsafe context." - but you can't compile anything managed in burst, correct? (I think so - here's the docs C#/.NET Language Support | Burst | 1.6.4 (unity3d.com). Which is why I said that it is not impossible to access Sprite object in burst at all. The article does not use burst though.

    2) they do "pin" the pointers in the post, as far as I understood:
    "GCHandle.Alloc() protects a managed object from being garbage collected."

    In the post they pin all the managed data they are working on in the jobs right? The reason it's manageable for them is because they are not really using a lot of managed objects - I think only the "Task" object itself is managed. This is one object per job.

    In the case of an animator, you would need to pin (GCHandle.Alloc) all the references (managed pointers) that you are going to use. So all the SpriteRenderers and all the Sprites at the very least. This I think would cause huge overhead (I am assuming that GCHandle.Alloc is at least non-negligible).

    So yes, you can trick it, but I think it will probably come with a rather big overhead. This is what I meant by "physical restriction, not safety one" - or rather not a safety one that "on my honor" would solve). You just can't control GC, so you have to pin each in every object you access (afaik).


    Also, yes, we had a really cool ECS example of such animator on the forums - I actually have a rewritten fork of it I use in my pet project)
    TarasMartynyuk/SpriteSheetRenderer: A powerful Unity ECS system to render massive numbers of animated sprites. (github.com)
    fabriziospadaro/SpriteSheetRenderer: Spritesheet renderer is a powerful Unity ECS API to render massive numbers of sprites using the new dots stack, taking full advantage of Jobs, DynamicBuffers and ComputeBuffer (github.com)
     
  8. Rocky_Unity

    Rocky_Unity

    Joined:
    Oct 13, 2017
    Posts:
    96
    This is very insightful! Thank you for explaining that. I have much to learn still.

    I have decided to try and release my first game before getting too crazy with intense tech yet :)
     
    GilbertoBitt likes this.