Search Unity

Problem while reading & writing render texture via compute shader

Discussion in 'General Graphics' started by somi94, Aug 3, 2020.

  1. somi94

    somi94

    Joined:
    Mar 20, 2015
    Posts:
    3
    edit: found a workaround for my problem, but the question persists

    Hey all,

    i have some trouble getting a quite simple idea to work.
    Is it possible to write to a RenderTexture via ComputeShader and use the produced result in the next Dispatch of the same Shader?

    I have two render textures "input" and "output". Both are created in the assets folder, share the same size (1024x1024) and contain a single channel (color format is R8_UNORM). "input" is rendered to by a camera. The compute shaders task is to compare the pixels of "input" and "output" and to write the brighter one back to "output".

    Reading from "input" and writing to "output" works, but the compute shader seems to "forget" about the changes made to "output". Meaning every read from "output" produces a black pixel, although displaying the "output" through a material shader (or inspecting it in the editor) confirms that it received the pixels from "input".

    I debugged this quite a bit and the issue really seems to be that the compute shader is unable to receive the previously drawn pixels (which definitely get drawn in time, because i can see the copied pixels from "input" before the next Dispatch kicks in). Reading the textures pixels to CPU is never happening (maybe that's a part of the problem?). Camera renders, ComputeShader compares & "copies", Material displays the results. Is there a way to tell the compute shader to reload the output texture before the dispatch? Although I'm unsure why this should be necessary, because the compute shader is the only one writing to the "output" Texture.

    This is my code:

    Code (CSharp):
    1.  
    2. public class FoWCam : MonoBehaviour {
    3.         public RenderTexture input;
    4.         public RenderTexture output;
    5.         public ComputeShader compute;
    6.  
    7.         protected int kernel;
    8.  
    9.         void Awake() {
    10.             this.kernel = this.compute.FindKernel("WriteFoW");
    11.  
    12.             this.output.enableRandomWrite = true;
    13.  
    14.             this.compute.SetTexture(this.kernel, "textureIn", this.input);
    15.             this.compute.SetTexture(this.kernel, "textureOut", this.output);
    16.         }
    17.  
    18.         void OnEnable() {
    19.             StartCoroutine(DrawRoutine());
    20.         }
    21.  
    22.         void OnDisable() {
    23.             StopCoroutine(DrawRoutine());
    24.         }
    25.  
    26.         IEnumerator DrawRoutine() {
    27.             while(true) {
    28.                 yield return 0;  
    29.                 this.compute.Dispatch(this.kernel, 64, 64, 1);
    30.                 yield return new WaitForSeconds(1f);
    31.             }
    32.         }
    33.     }
    34.  
    Code (csharp):
    1.  
    2. Texture2D<float> textureIn;
    3. RWTexture2D<float> textureOut;
    4. #pragma kernel WriteFoW
    5. [numthreads(16,16,1)]    
    6. void WriteFoW(uint2 id : SV_DispatchThreadID){
    7.     textureOut[id.xy] = max(textureOut[id.xy].r, textureIn[id.xy].r);
    8. }
    9.  
    edit
    I was able to solve my problem by copying the output to a third RT, which i then use as the second input texture for the CS:


    Code (CSharp):
    1.  
    2.         [...]
    3.              this.compute.SetTexture(this.kernel, "textureIn", this.input);
    4.              this.compute.SetTexture(this.kernel, "textureIn2", this.store);
    5.              this.compute.SetTexture(this.kernel, "textureOut", this.output);  
    6.         [...]
    7.         IEnumerator DrawRoutine() {
    8.            while(true) {
    9.                yield return 0;      
    10.                this.compute.Dispatch(this.kernel, 64, 64, 1);
    11.                Graphics.CopyTexture(this.output, this.store);
    12.            }
    13.        }
    14.        [...]
    15.  
    Code (CSharp):
    1.  
    2. #pragma kernel WriteFoW
    3.  
    4. Texture2D<float> textureIn;
    5. Texture2D<float> textureIn2;
    6. RWTexture2D<float> textureOut;
    7. [numthreads(16,16,1)]      
    8. void WriteFoW(uint2 id : SV_DispatchThreadID){
    9.     textureOut[id.xy] = max(textureIn[id.xy].r, textureIn2[id.xy].r);
    10. }
    11.  
    But I'm still curious if this isn't solvable using only those initial 2 textures...
     
    Last edited: Aug 3, 2020