SamplerState question

A topic that came up again for me - how do i properly implement sampler states?

I want to have a Texture, and a seperate Sampler, which can be used for multiple input textures. But these are super unreliable and I want to know what i am doing wrong.

Here is a quick example:
If I change the light type from volumetric to normal it will change the sampler state of a whole different shader (the directional light shader in that case) and the shadows look broken now.


First a recap

The monogame framework has some of these:

Texture2D Texture: register(t0);
sampler Sampler : register(s0);

The old XNA way was
sampler2d Sampler : register(s0);

In this guide (porting from dx9 to dx11) link
it says
this is the way now:

Texture2D texture;
SamplerState g_samLinear
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Wrap;
AddressV = Wrap;
};

I use the xna semantics so it looks like this

SamplerState blurSamplerPoint
{
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter = POINT;
MinFilter = POINT;
Mipfilter = POINT;
};

The way to sample a texture then is

myTexture.Sample(mySamplerState, uv);


Now the problem

However, this doesn’t work. It will sample the texture, yes, but not with the filters specified in the sampler.

A way to make it work is, ironically, to use the old xna way of specifying a texture inside the sampler.

SamplerState texSampler
{
Texture = ;
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter = POINT;
MinFilter = POINT;
Mipfilter = POINT;
};

Which obviously defeats the purpose of specifying a few universal samplers.

Now that would be fine, BUT more problems come our way - sometimes, if another sampler is used by some other shader (so 2 seperate fx files, 2 seperate samplerstates, textures etc.) the next sampler willl be overwritten by the values of the previous one.

For example if I use one technique of a light shader, which reads the shadow map more than once, another shader will be broken.

Example 1:

Two different shaders, one does the depth map reading for ao and the other one samples the skybox

Texture2D DepthMap;
SamplerState texSampler
{
Texture = < DepthMap >; // if this is disabled the sampling won’t work fast, the shader takes about 80% more time, if it is enabled the sky is wrong
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter = POINT;
MinFilter = POINT;
Mipfilter = POINT;
};
DepthMap.SampleLevel(texSampler, texCoord, 0).r;

TextureCube SkyboxTexture;
SamplerState CubeMapSampler
{
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter = LINEAR;
MinFilter = LINEAR;
Mipfilter = LINEAR;
};

SkyboxTexture.Sample(CubeMapSampler, normal.xyz);

So if I don’t use the DepthMap, my skybox is fine (it is drawn after the ao, which uses the depthmap).
If i use it, my Skycube will also use a point filter, which i don’t want.


Just an idea: do you still have this problem after setting registers on the sampler ?

texture SourceTex;
sampler gInputSampler : register(s3) = sampler_state
{
    Texture = <SourceTex>;
    AddressU = WRAP;
    AddressV = WRAP;
    MagFilter = LINEAR;
    MinFilter = LINEAR;
    Mipfilter = LINEAR;
};

It may force the GPU to clear each state when setting all of these fields everywhere ?
Or it may be a problem in MG, some samplers’ state could be kept from a shader apply to another one ?

I have looked into the sampler state stuff because of an issue someone had with it. I’m currently rewriting the effect parser (which parses the sampler state among other things) and when I’m done I plan to rewrite some of the texture/sampler handling parts of MGFX.

The way this works in MonoGame is sampler state is parsed and removed from the shader in a pre-compilation step. It’s then stored so the sampler state can be applied when when a shader that uses that sampler is applied (when an effectpass that uses that shader is applied). There’s no magic here, MG is just replicating the effects framework functionality. So what’s probably happening in your case is the sampler slot is incorrect, which causes the wrong sampler state to be set. Not sure how that happens, but the MGFX code is a bit weird when reflecting texture and sampler data so there are probably issues there (which is why I want to make some changes).

About the syntax, I wrote up a little gist with details on how you should do it.

Note that matching sampler and texture slot will not work because of reflection limitations (though it’s actually used in MG itself). I have a plan for fixing that, but we have to parse sampler and texture declarations ourselves, and that’s a bit complicated. Also note that the last option ‘sampler2d Sampler : register(s0);’ is currently not working correctly (which is the bug that made me investigate this whole thing). That leaves you with either using the old syntax (and setting the texture in the sampler state) or the new syntax. If you want to use one sampler state for sampling from different textures, use the new syntax. If not, I still recommend the new syntax in case you want to reuse sampler states for sampling multiple textures later. Either way, pick one and stick with it. Don’t mix 'n match old and new syntax.

With that explanation it should be clear that you were actually doing things right the first time using

SamplerState blurSamplerPoint
{
	AddressU = CLAMP;
	AddressV = CLAMP;
	MagFilter = POINT;
	MinFilter = POINT;
	Mipfilter = POINT;
};

So I guess this is just a big fat nasty bug in MG… Sorry for the anticlimax! For a workaround you should set the sampler state in code rather than in your effect.

I have tried again with all my samplers using this semantic (without any texture reference) and it will 100% not work correctly.

Example - I need the texture to wrap, but the default is clamp.

SamplerState TextureSampler
{
/Texture = (AlbedoMap);/
Filter = Anisotropic;
MaxAnisotropy = 8;
AddressU = Wrap;
AddressV = Wrap;
};

SamplerState TextureSampler
{
Texture = (AlbedoMap);
Filter = Anisotropic;
MaxAnisotropy = 8;
AddressU = Wrap;
AddressV = Wrap;
};

Yeah, my point is that that’s a bug :confused:

That’s a nasty/wicked bug to notice