Search Unity

Do shaders update exactly the same amount as game logic?

Discussion in 'Shaders' started by cosmologosaurusrex, Jun 15, 2020.

  1. cosmologosaurusrex

    cosmologosaurusrex

    Joined:
    Jul 1, 2015
    Posts:
    50
    Hey, so i know that shaders update on the gpu and game logic on the cpu but do they wait for each other? If the game runs 120 updates each sec. Do the shader run the same amount of updates? What happens first? shader or late update?

    I change a material through game logic in late update but then i also change the material through the shader.
    Is this a bad idea in some way?

    Thx in advance :)
     
  2. Steven-1

    Steven-1

    Joined:
    Sep 11, 2010
    Posts:
    471
    you change the material through the shader?
    what exactly do you mean by that?

    but anyway, shader code is run at the very end of a frame, it has to, cause it's what makes your game visible
     
    cosmologosaurusrex likes this.
  3. cosmologosaurusrex

    cosmologosaurusrex

    Joined:
    Jul 1, 2015
    Posts:
    50
    "but anyway, shader code is run at the very end of a frame, it has to, cause it's what makes your game visible"
    Oh, yea. That makes sense :p. But also every frame then, thx.

    "you change the material through the shader?"
    Just the colors of vertecies. I dont know if it would be called changing the material or not.But the colors change each frame based on the cameras dot product with vertex normals. So i do that in the shader. But the color also changes before that in a C# script based on how close my character is to a wall.

    So lets say I could change the pixels of a small texture of a material in either a shader or in C#. What would be more performant? If I do that each frame.
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    Depends on how you're changing them. Most of the time the GPU doing is going to be faster. The main question to ask is "how much data does the GPU need to have to do this work". Because in terms of computation cost the GPU is going to win every time, but it might take longer if a lot of data needs to be uploaded every frame, especially if the cost of collecting all of that information and translating it into a form the GPU can use takes just as long or longer to do than just doing the work on the CPU and uploading that.

    For something as simple as changing the vertex colors based on a dot product with the camera, doing this in the shader is going to be literally thousands of times faster. The GPU already has all the information needed to do this calculation.

    Understand, you're not changing the "material". A material in the context of Unity is a container that has a list of values, numerical data and texture references, a shader reference, and a few other bits of information for Unity to know how and when to render something. The GPU knows nothing of the material, it just knows it's being told to render some mesh data with a specific set of shader passes and this set of values & textures.

    What you do in a shader can only modify the color value(s) output by the fragment shader. Each vertex shader only sees a single vertex at a time, and only passes on data to the fragment shader, and the fragment shader only sees the interpolated values the GPU calculates per pixel and outputs a color. Everything else is temporary and is thrown away after each vertex / fragment gets calculated for that frame and does not affect any other vertex or fragment. And it won't change the existing mesh / material data the GPU or CPU has.
     
    Last edited: Jun 15, 2020
    cosmologosaurusrex likes this.
  5. cosmologosaurusrex

    cosmologosaurusrex

    Joined:
    Jul 1, 2015
    Posts:
    50
    [QUOTE="bgolus, post: 5984399, member: 163285" QUOTE][/QUOTE] Thank you epic snail! Ok, right now I have a C# script that updates a 4x4 pixel texture each frame in C#. I guess its possible to change the texture like that in a shader right?and do it before fragment and vertex shader? Just do it once for the model each frame, not for each vert or pixel? This example does not use the dot pruduct i talked about. It just lerps between colors over time on each pixel of the texture.

    Code (CSharp):
    1. float timer0 =  0.5f*Mathf.Sin(timer*2*Mathf.PI)+0.5f;
    2. float timer1 =  0.5f*Mathf.Sin((timer-0.125f)*2*Mathf.PI)+0.5f;
    3. float timer2 =  0.5f*Mathf.Sin((timer-0.25f)*2*Mathf.PI)+0.5f;
    4. float timer3 =  0.5f*Mathf.Sin((timer-0.375f)*2*Mathf.PI)+0.5f;
    5.  
    6. float timer4 = 0.5f*Mathf.Sin((timer-0.5f)*2*Mathf.PI)+0.5f;
    7. float timer5 =  0.5f*Mathf.Sin((timer-0.625f)*2*Mathf.PI)+0.5f;
    8. float timer6 =  0.5f*Mathf.Sin((timer-0.75f)*2*Mathf.PI)+0.5f;
    9. float timer7 =  0.5f*Mathf.Sin((timer-0.875f)*2*Mathf.PI)+0.5f;
    10.  
    11. Color c01=c1;
    12. Color c02=c2;
    13.  
    14. c0L=Color.Lerp(c01,c02 ,timer0);
    15. c1L=Color.Lerp(c01,c02 ,timer1);
    16. c2L=Color.Lerp(c01,c02 ,timer2);
    17. c3L=Color.Lerp(c01,c02 ,timer3);
    18. c4L=Color.Lerp(c01,c02 ,timer4);
    19. c5L=Color.Lerp(c01,c02 ,timer5);
    20. c6L=Color.Lerp(c01,c02 ,timer6);
    21. c7L=Color.Lerp(c01,c02 ,timer7);
    22.  
    23.  
    24.        
    25. texture.SetPixel(0, 0, c0L);
    26. texture.SetPixel(1, 0, c1L);
    27. texture.SetPixel(2, 0, c2L);
    28. texture.SetPixel(3, 0, c3L);
    29.  
    30. texture.SetPixel(0, 1, c4L);
    31. texture.SetPixel(1, 1, c5L);
    32. texture.SetPixel(2, 1, c6L);
    33.  texture.SetPixel(3, 1, c7L);
    34.  
    35. texture.SetPixel(0, 2, c0L);
    36. texture.SetPixel(1, 2, c1L);
    37. texture.SetPixel(2, 2, c2L);
    38.  texture.SetPixel(3, 2, c3L);
    39.  
    40. texture.SetPixel(0, 3, c4L);
    41. texture.SetPixel(1, 3, c5L);
    42. texture.SetPixel(2, 3, c6L);
    43. texture.SetPixel(3, 3, c7L);
    Is this much code allowed in a shader? as long as its not on per vertex and per fragment+
    And it would still be a lot faster?
     
    Last edited: Jun 15, 2020
  6. cosmologosaurusrex

    cosmologosaurusrex

    Joined:
    Jul 1, 2015
    Posts:
    50
    Just quoting correctly so you have easier time seing.
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    That's not really a lot of code. You could be doing all of that code every frame, in every fragment, plus more, and probably not really notice any impact. There's some additional code that'd be required to translate the above into the values you would get when sampling a texture with these values, specifically having to recreate bilinear filtering in the shader, if that's something that's needed.

    However my next question would be "how is that texture being used"? Or more specifically, what are you using that texture to achieve. Most likely there's no reason to do most of that code, especially since you're only ever lerping between two color values. Really the code is get a time value, offset it by some amount, get the sine of that, and lerp between two colors. That looks like this:
    Code (csharp):
    1. // derive timer offsets by something like the original texture's UVs
    2. float timerOffset = saturate(uv.x * 4.0 - 1.5) * 0.5 + saturate(uv.y - 0.125) * 0.5;
    3. float timer = _Time.y - timerOffset;
    4. float factor = sin(timer * 2 * UNITY_PI) * 0.5 + 0.5;
    5. half4 color = lerp(_Color01, _Color02, factor);
    That's very little code. It doesn't exactly produce the same output as the above texture based setup. An interpolated time offset isn't the same as an interpolation between fixed time offsets. And if you're using linear color space rendering interpolating between the color values wont produce the same results as the c# code which is working in sRGB space. But depending on your use case it might be good enough.
     
    cosmologosaurusrex likes this.
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    As a point of comparison, the above bit of code I posted is roughly about 12 instructions on a GPU (off the hip guesstimate count). Something like Unity's Standard shader does 5-20 times that in both the fragment and vertex shader stages individually depending on the rendering path and options you're using.
     
    cosmologosaurusrex likes this.
  9. cosmologosaurusrex

    cosmologosaurusrex

    Joined:
    Jul 1, 2015
    Posts:
    50
    Oh thats really good to know. I made two shaders in the past that where really non performant that moved verticies (vertex animation) so im really carefull or cynical about what shaders can handle. But thank you so much. You always go beyond what i expect <3 . Is there something special about moving the vertecies that makes it so much more costly then changing their colors? Or maybe i just made the shader poorly. You dont have to answer. You have been really helpfull already
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    It really depends on how you did the vertex animation. If you used a lot of if statements, that's not going to work like you think it will. That's usually the first mistake I see people make when starting in shaders is they'll put if statements in everywhere in an attempt to "optimize" the code, but really they're just making it way slower.

    GPUs are really good at doing a lot of math. They're less good at branching. Most of the time if you can do something with a bunch of math instead of an if statement, the math will be faster. And also most of the time an if between two options doesn't mean the GPU only does one or the other, it always does both and throws away the results of the other. You still pay the cost of both.
     
    cosmologosaurusrex likes this.
  11. cosmologosaurusrex

    cosmologosaurusrex

    Joined:
    Jul 1, 2015
    Posts:
    50
    Yea that part i have gotten an understanding of at least. I'm a slow learner with shaders though. I'm mostly doing all the other game dev stuff.