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.