read from and write to same surface in fragment shader

Started by
4 comments, last by Evil Steve 14 years, 5 months ago
Hi, i tryed to bind surface as render target and in fragment shader use same surface as texture, so i can read value from surface, modify it and write it back to it. But after that i have black horizontal lines across surface... So my question is : is it ok to render to surface that is used as texture ?
Advertisement
No, it's not, at least in D3D9 (but the same should apply to D3D10/D3D11).
The API should signal you there's a problem.

The problem is even if there's no scattering in the render target (scattering = ability to write to different texels), any gathering (gathering = reading from different texels) operation could be impossible to be determined.

Think about the filtering problem when using dynamic branching, you can't use a standard sampling because the neighbors texels couldn't be available.

The problem is the same, but worse. If you could write on the RT, then the only texel you could safely read is the "current" texel, let's call it (x,y). You don't know if the texel (x,y+1) has the "old" value or has already been modified, so the results of such an operation are unpredictable.
I used if for refraction post effect (read refracted value from surface, write it back), so basically its not 1:1 matching of pixels.
It's not 1:1 if the coordinates of the texels you sample are different than the coordinates of the output texel, which I assume is the case of your refraction shader.

I'll try to explain better, sorry but english isn't my native language. :)

Let's say we have a pixel shader reading from (x-1,y-1) and outputting this value.

Let's see what happens with pixel (50,50).
You read the value of another pixel, (49,49), and output it to 50,50. But hey, pixel (49,49) reads from (48,48)! Which is the current value of (49,49)? Its original value or the one in (48,48)?

There's no way to know if the shader for pixel (49,49) has already been executed. So you don't know if you are sampling (49,49) or (48,48).

The results are impossible to be determined a priori.

From an hardware point of view the only possible solution is to execute one pixel after another in strict order, which would make rendering really slow.

So the solution is to use a new buffer as a RT. It's fast and reliable.
It is "ok", but it is undefined. In other words, although it isn't strictly forbidden, you should not do it.

The black lines you see come from pixel values not written out before you read them back.
For some shaders (ones that only read the same pixel that is written back), you can get away with it, especially if you put some operations in between, so the GPU has time to write out all data. This is still "gambling" and not something 100% defined, but it usually works. On nVidia hardware under OpenGL, you can use the vendor extension NV_texture_barrier for this very thing, which makes it a defined operation.

However, for a refraction shader, it cannot work, since you sample from more or less arbitrary locations. GPUs process groups of pixels in parallel, so if you read from some more or less random nearby location while writing to the same texture, you will occasionally be reading data that is being modified, cleared, or uninitialised.
Therefore you must use 2 textures, one you read from and one you render into.
ok, so now my current implementation is :

render scene (deffered).

process lights to light map

combine color map (from deffered pass) with light map (output value to backbuffer and normal_surface (from deffered pass ("memory performance")))

I'm reusing normal_surface because i don't want to create new render target.

(it's true i have NX260 card,but still i don,t like "wasting" of memory)
also i don't like to create new shader only to put together results of refracting and final scene.

process refract (back buffer as target, normal_surface as texture)

its okay to use back buffer as regular surface ?

during implementing i found out that in code :

float4 normalColor = tex2D(Bump,IN.texture0);
float2 normalOffset = 0.05 *normalColor.rg;
return tex2D(normalSampler, IN.texture0+normalOffset);

changing value 0.05 to 0.5 affect FPS.

for me it's strange , do you know any reason why it's happening ?
Quote:Original post by joeblack
during implementing i found out that in code :

float4 normalColor = tex2D(Bump,IN.texture0);
float2 normalOffset = 0.05 *normalColor.rg;
return tex2D(normalSampler, IN.texture0+normalOffset);

changing value 0.05 to 0.5 affect FPS.

for me it's strange , do you know any reason why it's happening ?
Because you're probably reading texels further apart on the texture, which is causing cache misses on the GPU.

This topic is closed to new replies.

Advertisement