Order of Tex2D in shader

Hi !
I was working on one of my shaders, scratching my head asking myselft why one of my textures was not where it should be used in the computations.
I then just realized that when using SpriteBatch, the texture passed in the .Draw() HAS to be the first to be used by tex2d() in the pixelshader, otherwise it will be swept with another one.

Is this the normal behavior : call tex2D/any sampler function in the same order they are set with .SetValue in c#, and this order must be the same as the declarations of samplers in hlsl ?

OMG. That would explain a lot.
I just had the weirdest problems myself with a displacement shader.
@kosmonautgames pointed me in the direction of the newer sampler semantics which made it work.

// This is the texture that SpriteBatch will try to set before drawing.
Texture2D ScreenTexture;
SamplerState TextureSampler
    Texture = <ScreenTexture>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;
	AddressU = Wrap;
	AddressV = Wrap;

// The r-channel of the value map is the displacement x-value.
// The g-channel of the value map is the displacement x-value.
// The b-channel of the value map is the lighting-weight.
Texture2D ValueMap;
SamplerState ValueSampler
	Texture = <ValueMap>; // not really necessary but here to avoid some nasty MG bugs
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;
	AddressU = Clamp;
	AddressV = Clamp;

float4 PixelShader_Displacement(float4 pos : SV_POSITION, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : SV_TARGET0
	float4 c1 = ValueMap.Sample(ValueSampler, texCoord);
	//c1 = c1 * 2.0f - 1.0f;
	float4 c2 = ScreenTexture.Sample(TextureSampler, texCoord);
	return c1 * 0.5f + c2 * 0.5f;

Before that I was using tex2d and it was giving me the wrong texture and I couldn’t figure out why…

I’ve not yet tried with .Sample(). It may be the solution :wink:

Yes it is. Sure it will work.
But your observation explains the inconsistencies I was getting before that.

In conclusion, is this an MG’s issue or a misuse of spritebatch and effects with multiple textures ?

Good question.
I was posting here in the hope that someone else, more knowledgeable, would consider that.
Don’t think it can be misuse though. It’s a totally plausible use case imo.

@jjagg do you have any idea about that?

Spritebatch sets the texture in register t0. If you don’t explicitly set the registers for your textures the compiler will assign them for you. So make sure you bind the texture you want SpriteBatch to assign to to t0


sampler samp : register(s0);

Texture2D tex;
SamplerState texSampler
	Texture = <ValueMap>; // not really necessary but here to avoid some nasty MG bugs
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;
	AddressU = Clamp;
	AddressV = Clamp;

Would actually assign the screen-texture to tex instead of the sampler samp?
So both samplers would have the texture passed by MG in the draw-call?
Or would samp be empty?

I’m not sure how textures will be assigned. It’s not obvious how you’d want it to be assigned either. If you make it unambiguous for yourself, you don’t have to worry about the compiler not doing what you want. So if you need a texture to be in a specific register then explicitly assign it that way.

My guess is the compiler will generate a texture for the sampler ‘samp’ and the texture that’s first sampled from when compiling your shaders will be put in t0 (this is really just a guess). That would mean what texture goes in t0 depends on your pixel shader and order of compilation.

Your example would become (assuming tex in register t0).

sampler samp : register(s0);

Texture2D tex : register(t0);
sampler texSampler = sampler_state
    Texture = <tex>; // I assume you meant to put tex here
    MinFilter = Linear;
    MagFilter = Linear;
    Filter = Linear;
    AddressU = Clamp;
    AddressV = Clamp;

Side note that is slightly related and may clarify some stuff:
The reason for the different syntax options is that in DX9 the texture a sampler samples from is a part of the sampler state so you’d do tex2D(sampler, texCoord). That changed in DX10 so you can now do texture.Sample(sampler, texCoord). MonoGame allows legacy syntax for compatibility, but it’s using the DX11 compiler and it assigns textures as you set them using the effect syntax (Texture = <TextureName>;).

That makes sense.
I tried all different methods of assignment after reading your gist about that topic and none of them worked. Tried both samplers using the registers (0 and 1), tried named textures, didn’t work either.
Of course I always read from the second texture (displacement map) before reading from the one assigned by mg. So that suspicion here seemed to explain a lot.
I try to do a sample project the next few days but no promises…

I have also tried using the registers but it didnt solve the problem.
Only changing the order of my calls to tex2d has shown me that the order of the calls must match the declaration of the samplers at the top of the hlsl.
The register 0 is used by draw as you said. But if i call tex2d on another sampler before the one set on s0 it ignores the first call as if the second texture was just black. From what i noticed thanks to copypaste fail.

You mean you explicitly declare textures and set their registers? Because that should really work. Note that sampler registers and texture registers are not really bound together. I.e. the sampler at register s0 does not necessarily sample from the texture at t0.

This is the explanation. I will investigate if i have time and come back to confirm

Sorry it took so long but I was sick… Again…

@Jjagg as promised:
Here is a small project I created to showcase the whole thing.

Maybe that helps.
None of the shaders work with the exception of the shader titled ‘new’ that uses the ‘new’ way of addressing the sampler (not tex2D).
There seems to be no difference if you use ps_4_0_level_9_1 or ps_4_0 at all.

Please feel free to prove me wrong; Point out that I made grave mistakes when writing the shaders; etc…
Always striving to learn :slight_smile:

Thanks, this makes testing easy. I’ll give this a try tonight.


That’s why I made it :smile:


Oops, I completely forgot about this. I’m short on time now though :confused:

I’m currently using this kind of declaration to avoid the problem

Texture2D brightpass;
SamplerState blurSampler
    Texture = <brightpass>;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = LINEAR;
	AddressU = CLAMP;
	AddressV = CLAMP;

But it forces to use ps_5_0 :confused: which limits compatibily with older systems.