Issue with spritebach and custom shaders

Hi,

Since 3.3 (and at least dev builds up last Tuesday or Wednesday), the following code is producing unexpected results:

...
effect.CurrentTechnique = effect.Techniques[0];
effect.Parameters["sk_ratio"].SetValue(sk_ratio);
effect.Parameters["sk_jitter"].SetValue(sk_jitter);
effect.Parameters["sk_texture"].SetValue(sk_texture);
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, effect);
spriteBatch.Draw(title_rendertarget, title_position, null, Color.White * Alpha, 0, title_origin, 1.4f, SpriteEffects.None, 0);
spriteBatch.End();
...

with the pixel shader code is:

#include "Macros.fxh"

#ifdef SM4
#define TECHNIQUE_FRAGMENT_ONLY(name, psname) \
    technique name { pass { PixelShader = compile ps_4_0_level_9_3  psname(); } }
#else
#define TECHNIQUE_FRAGMENT_ONLY(name, psname ) \
    technique name { pass { PixelShader = compile ps_2_0  psname(); } }
#endif

DECLARE_TEXTURE(sb_texture, 0);
DECLARE_TEXTURE(sk_texture, 1);

BEGIN_CONSTANTS

    float2 sk_ratio;
    float2 sk_jitter;
    float2 rgb_displacement;

END_CONSTANTS


float4 SketchPixelShader(float4 position : SV_Position, float4 color : COLOR0, float2 tex_coord : TEXCOORD0) : SV_Target0 {

    float4 color1 = SAMPLE_TEXTURE(sb_texture, tex_coord);
    float4 color2 = SAMPLE_TEXTURE(sk_texture, (tex_coord + sk_jitter) * sk_ratio);
    return color1 * color2;
}

TECHNIQUE_FRAGMENT_ONLY(SketchTechnique,   SketchPixelShader  );

It appears that the texture sk_texture in sampler 1 isn’t getting correctly passed to the device resulting in nothing being rendered.

By adding the following line:

GraphicsDevice.Textures[1] = null; 

After the:

effect.Parameters["sk_texture"].SetValue(sk_texture);

The shader with the correct textures is correctly rendered. I suspect that some kind of dirty flag isn’t being properly set within the Effect.SetValue(…)

I can reproduce the issue with ATI/AMD and nVidia cards using WinDX, didn’t had time to try it on GL and Windows Phone

I narrowed down the problem, it appears to only happen if a rendertarget swap occurs.

The following illustrates the problem:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace DTBug {
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Game {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D texture_0, texture_1;
        Vector2 sk_ratio, sk_jitter;
        RenderTarget2D rt;
        Effect effect;

        public Game1()
            : base() {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize() {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent() {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            texture_0 = Content.Load<Texture2D>(@"mono");
            texture_1 = Content.Load<Texture2D>(@"sketch");
            effect = Content.Load<Effect>(@"title");

            sk_ratio = new Vector2(
                texture_0.Width / (float)texture_1.Width,
                texture_0.Height / (float)texture_1.Height);

            rt = new RenderTarget2D(GraphicsDevice, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight, true, graphics.PreferredBackBufferFormat, graphics.PreferredDepthStencilFormat);

        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent() {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime) {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime) {

            GraphicsDevice.SetRenderTarget(rt);
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();
            spriteBatch.Draw(texture_0, new Vector2(260, 120), Color.White);
            spriteBatch.End();

            GraphicsDevice.SetRenderTarget(null);
            GraphicsDevice.Clear(Color.Orange);
            

            // TODO: Add your drawing code here
            effect.Parameters["sk_ratio"].SetValue(sk_ratio);
            effect.Parameters["sk_jitter"].SetValue(sk_jitter);
            effect.Parameters["sk_texture"].SetValue(texture_1);
            effect.CurrentTechnique = effect.Techniques[0];

            //GraphicsDevice.Textures[1] = null;  // uncoment to fix 

            spriteBatch.Begin(0, null, null, null, null, effect);
            spriteBatch.Draw(rt, new Vector2(0, 0), Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

btw in windows open GL it works correctly

Bump!

I was about to write about this exact same issue.

I am trying to render a RenderTarget2D with a custom SpriteBatch Effect which needs a displacement map texture, and I noticed that I have to call GraphicsDevice.Textures[1] = whatever/null; so that the texture parameter is correctly applied when the SpriteBatch initiate a draw call.

I also noticed that if I don’t set manually GraphicsDevice.Textures[1] = whatever/null;, the sampler1 texture is being correctly set on the first draw call, but subsequent draw calls doesn’t set it anymore.

I suspect this to have something to do with sampler states and TextureCollection._dirty, but I haven’t figured out what may be causing this yet.

I am pretty sure this worked a couple month ago. I’ll open an issue and try to narrow this down.

EDIT: found your issue, https://github.com/mono/MonoGame/issues/3710 I’ll take a look.

I’ve found the problem, simple solution provided in:
https://github.com/mono/MonoGame/issues/3710

Hi guys,
so is this issue fixed in 3.4 ??

Because i’m using a bloom shader that swaps rendertargets and i’m losing all the textures when i draw it. It shows only the shader effect without the textures.