The same texture for input and output of a fragment shader

Hi,


Is it possible to use a texture both as a render target (color attachment) and as a normal texture inside a fragment shader ?


I want to read the texture content inside a fragment shader, and also use the same texture as a color attachment to write on it...


My attempt to set such a configuration was accepted at runtime, but I want to be sure that there is no side effect...

Answered by MikeAlpha in 144692022

if small amount of texture gets covered, then copy out just these texels. For example by coupling same vertex shader you use for running depth peeling with simple fragment shader reading from texture values of which you want to save. Be careful to setup rendering pass so that load operation is "don't care".


If I understand what you're doing then, with many passes per frame, it makes sense to conserve bandwidth.


Alternate solution is to use buffers, with 2D raster organized on your own. You can read and write these in single compute kernel execution. But then you have other problems, for example with operating on a part of buffer only. The only way to make sure is to implement alternative versions and profile.


hope that helps

MIchal

Well, it is possible to setup something like this, but it is asking for trouble. OpenGL specification, for example, calls such setup "texture feedback loop" and states, if I remember well, that "results will be undefined". I have made something like this once (it was a bug, I forget to bind different texture to texture unit when switching rendering target) and got most unpleasant results (from corrupted texture up to snow crashes of GPU).


Now, I couldn't find nothing in Metal documentation explicityly prohibiting it but I do not believe it is safe or advised. The problem (apart from synchronization!) is, texture is not just a simple byte buffer, it contains structured data. And structure varies between GPUs. So even if you can make it work on one particular GPU, it doesn't mean it will work everywhere.


If you still want to use data in texture while rendering into it, just copy it elsewhere, and use that copy while rendering new content. BlitCommandEncoder contains functions for doing just that.


Hope that helps

Michal


PS. I think a read somewhere that some new GPUs/APIs are going to get support for symultaneous read/write to same texture. I don't think that will include read/render, thought. And besides, I think that current GPUs don't allow that still.

PPS (and sorry for double posting)


In fact, there is a way to do something _similar_. You can use blending to operate on both old and new contents of texture. Standard fixed-function pipeline blending will allow only a limited number of operations (because you're constrained by fixed hardware). It is still usefull for things like "write new values for WGB only if previous value of Alpha was 1.0". On iOS there are programmable blending extensions, which you can use to examine previous contents of texture (or buffer) you're rendering to. Of course in both cases you have only current fragment values available, and in fragment shader only.


Regards

Michal

Thanks for the response Michal


"If you still want to use data in texture while rendering into it, just copy it elsewhere, and use that copy while rendering new content. BlitCommandEncoder contains functions for doing just that."


I have supposed this could be slow... Any clue on the performance of such a copy ?

Well, that depends on GPU in question, texture size, copying frequency and so on. GPUs tend to be fast these days. You can also think about optimizing "copy" scenario a bit. For example, instead of copying texture "A" to new texture "B" (so as to use one of them for reading, and other for rendering) you could just render full viewport quad during your second pass and decide in fragment shader whether you want "old" value (and get it from texture "A") or "new" one (and generate it using possibly other data from texture "A"). It all depends on which of the render operations is costly one (if any at all). Perhaps you could describe in detail what you're doing?


Regards

Michal

It is a kind of depth peeling algorithms. I have to accumulate/multiply a colored transmittance for every pass, and to use it in the next pass.


For the pass i :

- In : the previously accumulated transmittance T(0) * T(1) * ... *T(i-1)

- I use it in the shader rendering equation

- I compute the pass transmittance T(i) and multiply it to the accumulated one

- Out : result of the rendering equation

- Out : the new accumated transmittance T(0) * T(1) * ... * T(i)


And so on...

The most often, the new T(i) only covers a small amount of pixels on the screen, So, I have supposed that copying the entire buffer content was a bit too expensive.


Thanks for your help !



Accepted Answer

if small amount of texture gets covered, then copy out just these texels. For example by coupling same vertex shader you use for running depth peeling with simple fragment shader reading from texture values of which you want to save. Be careful to setup rendering pass so that load operation is "don't care".


If I understand what you're doing then, with many passes per frame, it makes sense to conserve bandwidth.


Alternate solution is to use buffers, with 2D raster organized on your own. You can read and write these in single compute kernel execution. But then you have other problems, for example with operating on a part of buffer only. The only way to make sure is to implement alternative versions and profile.


hope that helps

MIchal

I'll give a try to the vertex shader coupled solution. The alternative with compute kernels seems a bit too intricate for such a task.

Thanks again for your help Michal !

Regards,

Cyril.

Last (sorry for the resurrection), is a textureBarrier call gives a chance to making work my in/out first scheme ?


The doc specifies :


"If a same texture is the rendering destination in a render pass and is also read from a shader, the results of the texture reads are undefined unless texture barriers are used."


So, it should avoid the synchronization problem that you talked about in your first post...

Hmm, but how would you invoke textureBarrier from within fragment shader? I believe purpose of textureBarrier is to help to synchronize between separate rendering calls (AFAIK GPU may run these in parallel).


But...lo and behold, even as we are talking, Apple is adding new functionality to the Metal. Have a look at the Session 605 - What's new in Metal, part 2 of current WWDC. They're describing Read-Write textures, which is exactly what you want. I am not sure where and when this feature will be available. I'm guessing OS X Sierra this fall...

You're right...

But it restrains a bit more the required platform, as it needs a new feature set and macOS Sierra (the last Metal documentation updates the feature sets, including the one required by texture read/write).

Thanks again !

Cyril.

It's been 8 years but I still can't find a definitive answer to the OP :(

so... does Metal allow using the same color texture both as the render target and an input to the frag shader?

The same texture for input and output of a fragment shader
 
 
Q