Sign in to follow this  
Telanor

Question about downsampling

Recommended Posts

A lot of times, usually in bloom/hdr samples, I see people taking a fullscreen image, rendering it to a 1/2 sized RT, then rendering that to a 1/4 sized RT. The pixel shader for these 2 passes is nothing more then a linear sampler being run on the input.

Is this done because the linear sampler returns a 2x2 area sample and you don't want to lose any information by downsampling straight to a 1/4 sized RT? How bad would it be to just skip straight to the 1/4th size? And what about upsampling then? I've seen the same process done for that case as well.

Also, a side question: If I need to bring a fullscreen image down to 1/8th size, would it be faster to just ask DX to generate mipmaps and use the mipmap of the size I need rather than doing 3 downscale passes? The answer will probably be "try it yourself" but I figured I'd ask anyway...

Share this post


Link to post
Share on other sites

Going straight to 1/4 of the size can lose single pixel size bright spots - due to the 2x2 sample you pointed out. For bloom this can make a huge difference, as now your highlights can shimmer in and out of existence as that bright pixel moves in screenspace. The upsampling on each level gives a smoother result than going straight from 1/4 back to 1/1.

 

That said, depending on your hardware (i'm looking at both main current gen consoles here, but it probably applies to PC GPUs) its faster to go straight to the 1/4 size with a shader that samples 4 times instead. This is due to actual cost of the physical write to texture memory for the intermediate step being expensive itself. Doing this also means no precision loss that would otherwise be incurred by downsampling RGBA8 textures, as the shader does the filtering in 32-bit.

Share this post


Link to post
Share on other sites

If you were to "skip" right to the 1/4 resolution you will alias due to undersampling. In order to sample at or above nyquist rate you need 2x the number of samples in each dimension, which is satisfied if you use a 2x2 filter kernel. The aliasing will manifest as blockiness in still images, and temporal flickering in moving images (which is much worse).

Share this post


Link to post
Share on other sites

Also, a side question: If I need to bring a fullscreen image down to 1/8th size, would it be faster to just ask DX to generate mipmaps and use the mipmap of the size I need rather than doing 3 downscale passes? The answer will probably be "try it yourself" but I figured I'd ask anyway...

 

In general I'd expect it to be somewhat hardware-dependent. My intuition is that it might be faster to use mipmaps only because the down-sampling for that purpose could potentially be implemented as a fixed function on the hardware but unfortunately I have no idea if that's true in practice.

Share this post


Link to post
Share on other sites

That said, depending on your hardware (i'm looking at both main current gen consoles here, but it probably applies to PC GPUs) its faster to go straight to the 1/4 size with a shader that samples 4 times instead.


Something like this?
 
float2 InputSize = float2(1/1280, 1/720);

float4 PS(VertexOutput input) : SV_TARGET0
{
	float4 color;
	
	color = InputTexture0.Sample(LinearSampler, input.TexCoord + InputSize * float2(1, 1));
	color += InputTexture0.Sample(LinearSampler, input.TexCoord + InputSize * float2(-1, -1));
	color += InputTexture0.Sample(LinearSampler, input.TexCoord + InputSize * float2(-1, 1));
	color += InputTexture0.Sample(LinearSampler, input.TexCoord + InputSize * float2(1, -1));
	
	return color / 4;
}
Edited by Telanor

Share this post


Link to post
Share on other sites

Yep smile.png

 

I always end up drawing myself diagrams like this when I'm trying to work out texture sampling locations:

EuZ05CG.png

Black rects are original (100%) texels.

Red rects are half-res texels.

Blue rect is a quarter res texel.

 

If you're doing post-processing using a full-screen quad, then the pixel shader will be getting executed at the center of the texel.

For a full-res post-processing shader, this means your unmodified tex-coords (input.TexCoord) will be in the centre of the black rect.

For half-res, they will be at the centre of the red quads, which is exactly at the black +, so bilinear filtering will give you the average of 4 original texels.

For quarter res, your tex-coords will be at the centre of that image, exactly at the red +. From there if you add originalPixelSize * (1,1), you'll be offsetting to exactly on one of the black +'s, which is exactly what you want!

 

So, your code should give you the average value of all of the original texels (black quads) in the above diagram, by sampling at all 4 of the black +'s.

Edited by Hodgman

Share this post


Link to post
Share on other sites
I did a few tests. Doing linear sampling ended up being very slightly faster than generating mipmaps. Also doing 4 linear samples in 1 pass instead of 1 sample in 2 passes was again very slightly faster.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this