Blending works differently in 3.7?

I recently updated to MonoGame 3.7 and I’m unsure whether or not this is a bug in the framework or in my code, so I figured I’d ask here first.

I have a method, DrawToScreen, which I use for source/destination blending like so:

        /// <summary>
        /// Draws this entity to the screen.
        /// </summary>
        /// <param name="spriteBatch">The <see cref="SpriteBatch"/> to use.</param>
        internal virtual void DrawToScreen(SpriteBatch spriteBatch)
        {
            // Defaults
            BlendState bs = BlendState.AlphaBlend;
            float srcMul = 1.0F, dstMul = 0.0F;

            if (Anim != null && Anim.CurrElem != null)
            {
                // Additive blending with src/dest
                if (Anim.CurrElem.Transparency.Type == TransparencyType.Add)
                {
                    srcMul = Anim.CurrElem.Transparency.Source / 256.0F;
                    dstMul = Anim.CurrElem.Transparency.Dest / 256.0F;

                    bs = new BlendState()
                    {
                        AlphaBlendFunction = BlendFunction.Add,
                        ColorBlendFunction = BlendFunction.Add,

                        AlphaSourceBlend = Blend.One,
                        ColorSourceBlend = Blend.SourceAlpha,

                        AlphaDestinationBlend = Blend.One,
                        ColorDestinationBlend = Blend.BlendFactor,
                        BlendFactor = new Color(dstMul, dstMul, dstMul, 1.0F)
                    };
                }
                // Subtractive blending
                else if (Anim.CurrElem.Transparency.Type == TransparencyType.Subtract)
                {
                    srcMul = 1.0F;//Anim.CurrElem.Transparency.Source / 256.0F;
                    dstMul = 0F;//Anim.CurrElem.Transparency.Dest / 256.0F;

                    bs = new BlendState()
                    {
                        AlphaBlendFunction = BlendFunction.ReverseSubtract,
                        ColorBlendFunction = BlendFunction.ReverseSubtract,

                        AlphaSourceBlend = Blend.SourceAlpha,
                        ColorSourceBlend = Blend.SourceColor,

                        AlphaDestinationBlend = Blend.One,
                        ColorDestinationBlend = Blend.One,
                    };
                }
            }

            spriteBatch.Begin(SpriteSortMode.BackToFront, bs, SamplerState.PointWrap, DepthStencilState.DepthRead, RasterizerState.CullNone);
            spriteBatch.Draw(SpriteLayer, Vector2.Zero, SpriteLayer.Bounds, new Color(Color.White, srcMul), 0F, Vector2.Zero, Vector2.One, SpriteEffects.None, LayerDepth);
            spriteBatch.End();

            foreach (var child in children)
            {
                child.DrawToScreen(spriteBatch);
            }
        }

For the most part, you can ignore subtractive blending portion as that’s never reached in this particular example, but when the dstMul is 0, I get screen flickering for my background every 3 frames. My main draw code in my game class is as follows:

        /// <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(null);
            if (BG != null)
                GraphicsDevice.Clear(BG.BackgroundColor);
            else
                GraphicsDevice.Clear(Color.CornflowerBlue);
            Effect palettize = Content.Load<Effect>("PaletteShader");
            palettize.Parameters["PalSampler"].SetValue(palTexture);

            bool endCalled = false;

            // Draw the players once loading is over.
            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointWrap, null, RasterizerState.CullNone, palettize, BG.Camera.GetTransformation(spriteBatch.GraphicsDevice));
            // First, we need to draw to the render targets using the regular Draw() method.
            if (RoundState > RoundState.Loading)
            {
                // Draw the BG
                BG.Draw(spriteBatch, palettize, bgTextures, gameTime);

                for (int i = 0; i < Players.Length; i++)
                {
                    if (Players[i] != null)
                        Players[i].Draw(spriteBatch, palettize, plTextures[i], gameTime);
                }

                spriteBatch.End();

                // Draw the HUD
                spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointWrap, null, RasterizerState.CullNone, palettize);
                HUD.Draw(spriteBatch, palettize, hudTextures, gameTime);
                spriteBatch.End();

                // Now we draw to the screen texture
                GraphicsDevice.SetRenderTarget(screenRenderTarget);
                GraphicsDevice.Clear(BG.BackgroundColor);

                BG.DrawToScreen(spriteBatch);

                for (int i = 0; i < Players.Length; i++)
                {
                    if (Players[i] != null)
                        Players[i].DrawToScreen(spriteBatch);
                }

                // Draw the HUD
                HUD.DrawToScreen(spriteBatch);

                //spriteBatch.End();

                GraphicsDevice.SetRenderTarget(null);

                // Draw everything in the RenderTarget to screen
                spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, null, RasterizerState.CullNone);
                spriteBatch.Draw(screenRenderTarget, screenRenderTarget.Bounds, Color.White);
                spriteBatch.End();
                endCalled = true;
            }

            base.Draw(gameTime);

            if (!endCalled)
                spriteBatch.End();
        }

I did some trial-and-error testing and it appears the flickering occurs when dstMul is 0. 3.6 didn’t do this, yet 3.7 does. Why? The math should check out just the same: if you want to draw a solid sprite, set the destination blend to 0 and the source blend to whatever on top. However, it’s as if upper layers aren’t blending at all, but rather being drawn on top in this instance, with the transparent region totally ignored. Either that or the destination below is getting set to 0 even after it’s been drawn.

Found the source of the problem. I had an animation, a light, that flickers every 2 ticks. For some reason I thought the dest transparency should have been set to 0; however it needed to be set to 256. Changing that got everything to display correctly!

Still, I wonder why the entire rest of the scene blanks out in 3.7 when that was not the case in 3.6

I don’t know if this has anything to do with your problem, but you shouldn’t create a new BlendState every frame. Create the BlendStates you need in the beginning, and then reuse them every frame. You can set the BlendFactor directly on the GraphicsDevice, so you don’t need to generate many different BlendStates to smoothly change the BlendFactor.

Didn’t know that! But yes, that is a good idea. I’ll make those changes when I get back home. Thanks for the advice!