This project is read-only.

Major updates - please get latest and test

Nov 18, 2009 at 1:24 AM

In order to add render targets I had to do a major restructuring of the code. Please help me out and build your game against the latest in source control and let me know if you have any problems. More about the new features soon, but here is a quick rundown of what's in there now:

  • Load textures from a BMP file
  • Load assets compiled as either Resources or Content. Only Content worked previously.
  • RenderTarget2D support - this may be a bit buggy right now
  • Use rendering to bitmap instead of the default canvas if desired. This has some limitations currently like rotation is not currently supported. This can be heavy on CPU so be careful.
  • Performance improvements
  • Additive blending supported in bitmap rendering mode

There's probably more I've forgotten as well.

Nov 26, 2009 at 4:41 AM
Edited Nov 26, 2009 at 4:42 AM

I just grabbed the latest source to test and I'm getting an error. Here is the code that is erroring:

 

foreach (var resource in resources)
{
	ResourceManager rm = new ResourceManager(resource.Replace(".resources", ""), assy);
	Stream DUMMY = rm.GetStream("app.xaml"); //Donno why, but without getting any real stream next statement doesn't work....

This code is in SilverArcade.SilverSprite.Manifest.Discovery.cs on line 112. The final line in the snippet is where the exception is being thrown. I use embedded resources in my application for my spritesheet framework and the "resources" pulled by the SilverSprite code in the above snippet include the resources in my projects.

The error thrown is "System.Resources.MissingManifestResourceException: Could not find any resources appropriate for the specified culture or the neutral culture."

 

 

Nov 26, 2009 at 2:49 PM

Ok I removed the resource discovery for now, it was something that I hope to use eventually but it isn't really being used right now. Please try again.

Nov 26, 2009 at 3:31 PM
Edited Nov 26, 2009 at 3:41 PM

Ok that is working now and I have run into two more problems. Both of which are easy fixes

#1 file: Graphics.SpriteFont.cs line: 90

problem (no error): MeasureString always returns 0,0 when it is first called (and is always behind)

original snippet:

if (tb.CheckAccess())
{
	size = new Vector2((float)tb.ActualWidth, (float)tb.ActualHeight);
	tb.Width = maxWidth;
	tb.Text = text;
}

 

fixed snippet:

if (tb.CheckAccess())
{
        tb.Text = text;
	size = new Vector2((float)tb.ActualWidth, (float)tb.ActualHeight);
	tb.Width = maxWidth;
}

#2 file: Graphics.SilverSprite.SpriteList.cs line: 142

error: "Invalid cross-threaded access exception"

original snippet:

public void ReleaseAll()
{
	while (Count > 0)
	{
		IDisposable image = this[0] as IDisposable;
		Sprite s = this[0] as Sprite;
		if (ParentCanvas.Children.Contains(s.Element))
		{
			ParentCanvas.Children.Remove(s.Element);
		}
		if (image != null)
		{
			image.Dispose();
		}
		RemoveAt(0);
	}
}

fixed snippet:

public void ReleaseAll()
{
    while (Count > 0)
    {
        IDisposable image = this[0] as IDisposable;
        Sprite s = this[0] as Sprite;

        if (ParentCanvas.CheckAccess())
        {
            if (ParentCanvas.Children.Contains(s.Element))
            {
	            ParentCanvas.Children.Remove(s.Element);
            }
        }
	    else
	    {
            ParentCanvas.Dispatcher.BeginInvoke(() =>
            {
                if (ParentCanvas.Children.Contains(s.Element))
                {
                    ParentCanvas.Children.Remove(s.Element);
                }
            });
        }
        if (image != null)
        {
	        image.Dispose();
        }
        RemoveAt(0);
    }
}
Nov 26, 2009 at 3:49 PM
Edited Nov 26, 2009 at 4:23 PM

Ok, now that I'm passed the above two issues I have another one that may be less trivial to solve (although still potentially trivial).

Code file: Graphics.Silverlight.ChildCanvasRenderer.cs

Error: "Collection was modified; enumeration operation may not execute." (no inner exception)

Line: 165

Stack Trace:

at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.ValueCollection.Enumerator.MoveNext()
at SilverArcade.SilverSprite.Graphics.Silverlight.ChildCanvasRenderer.End()
at SilverArcade.SilverSprite.Graphics.Silverlight.CanvasRenderer.End()
at SilverArcade.SilverSprite.Graphics.SpriteBatch.End()
at Cherub.Main.Logic.CreateTeam.Draw(GameTime gameTime)
at SilverArcade.SilverSprite.GameComponentCollection.Draw(GameTime gameTime)
at SilverArcade.SilverSprite.Game.Draw(GameTime gameTime)
at Cherub.Main.Logic.CherubGame.Draw(GameTime gameTime)
at SilverArcade.SilverSprite.Game.Tick()
at SilverArcade.SilverSprite.Game.sb_Completed(Object sender, EventArgs e)
at System.Windows.CoreInvokeHandler.InvokeEventHandler(Int32 typeIndex, Delegate handlerDelegate, Object sender, Object args)
at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, String eventName)

Snippet:

public void End()
{
    foreach (SpriteList dl in clippedImages.Values)
    {
        dl.EndSpriteBatch();
    }
    foreach (SpriteList dl in texts.Values)
    {
        dl.EndSpriteBatch();
    }
    foreach (SpriteList dl in bitmapTexts.Values)
    {
        dl.EndSpriteBatch();
    }
}

UPDATE

Looks like we have some race conditions here. The above error happens frequently, but not each time. I am also getting another error on a bit of code (shown below) sometimes.

Code File: Graphics.Silverlight.ClippedSpriteImage

Error: "Object reference not set to an instance of an object"

Line: 154

Stack Trace:

at SilverArcadSilverSprite.Graphics.Silverlight.ClippedSpriteImage.SetTintEffectAndColor()  
at SilverArcade.SilverSprite.Graphics.Silverlight.ClippedSpriteImage.set_Color(Color value)  
at SilverArcade.SilverSprite.Graphics.Silverlight.ChildCanvasRenderer.Draw(Texture2D texture, Vector2 position, Nullable`1 sourceRectangle, Color color, Single rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, Single layerDepth)  
at SilverArcade.SilverSprite.Graphics.Silverlight.ChildCanvasRenderer.Draw(DrawCommand cmd)  
at SilverArcade.SilverSprite.Graphics.Silverlight.CanvasRenderer.Draw(DrawCommand cmd)  
at SilverArcade.SilverSprite.Graphics.Silverlight.SilverlightRenderBase.End()  
at SilverArcade.SilverSprite.Graphics.Silverlight.CanvasRenderer.End()  
at SilverArcade.SilverSprite.Graphics.SpriteBatch.End()  
at Cherub.Main.Logic.CreateTeam.Draw(GameTime gameTime)  
at SilverArcade.SilverSprite.GameComponentCollection.Draw(GameTime gameTime)  
at SilverArcade.SilverSprite.Game.Draw(GameTime gameTime)  
at Cherub.Main.Logic.CherubGame.Draw(GameTime gameTime)  
at SilverArcade.SilverSprite.Game.Tick()  
at SilverArcade.SilverSprite.Game.sb_Completed(Object sender, EventArgs e)  
at System.Windows.CoreInvokeHandler.InvokeEventHandler(Int32 typeIndex, Delegate handlerDelegate, Object sender, Object args)  
at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, String eventName)

at SilverArcade.SilverSprite.Graphics.Silverlight.ClippedSpriteImage.SetTintEffectAndColor()
at SilverArcade.SilverSprite.Graphics.Silverlight.ClippedSpriteImage.set_Color(Color value)
at SilverArcade.SilverSprite.Graphics.Silverlight.ChildCanvasRenderer.Draw(Texture2D texture, Vector2 position, Nullable`1 sourceRectangle, Color color, Single rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, Single layerDepth)
at SilverArcade.SilverSprite.Graphics.Silverlight.ChildCanvasRenderer.Draw(DrawCommand cmd)
at SilverArcade.SilverSprite.Graphics.Silverlight.CanvasRenderer.Draw(DrawCommand cmd)
at SilverArcade.SilverSprite.Graphics.Silverlight.SilverlightRenderBase.End()
at SilverArcade.SilverSprite.Graphics.Silverlight.CanvasRenderer.End()
at SilverArcade.SilverSprite.Graphics.SpriteBatch.End()
at Cherub.Main.Logic.CreateTeam.Draw(GameTime gameTime)
at SilverArcade.SilverSprite.GameComponentCollection.Draw(GameTime gameTime)
at SilverArcade.SilverSprite.Game.Draw(GameTime gameTime)
at Cherub.Main.Logic.CherubGame.Draw(GameTime gameTime)
at SilverArcade.SilverSprite.Game.Tick()
at SilverArcade.SilverSprite.Game.sb_Completed(Object sender, EventArgs e)
at System.Windows.CoreInvokeHandler.InvokeEventHandler(Int32 typeIndex, Delegate handlerDelegate, Object sender, Object args)
at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, String eventName)

Snippet:

if (tintEffect == null)
{
    tintEffect = TintEffect.Create();
    rectangle.Effect = tintEffect;
}
tintEffect.Color = System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B);
Nov 27, 2009 at 1:05 PM

I checked in your recommended fixes to the first couple, the other ones seem to be threading related. I'm hesitant to put locks in the code since I would assume a very small percentage of people using this library are using threads.

Are you unloading a content manager in a background thread? That's what could cause some of these problems, if so is it possible to move the unload to the main thread?

Nov 27, 2009 at 3:36 PM
Edited Nov 27, 2009 at 3:41 PM

The only thing I'm really doing outside the main thread is making my WCF calls. The Draw code doesn't spawn any threads and I'm not destroying (or unloading) any objects when I get these errors. I believe all DrawableGameComponents in the Game object's Components list are drawn on the same thread correct?

One anomaly that I intended to work on after the problem was fixed is that for this particular screen I'm only getting between 2 and 4 FPS in SilverSprite (60 FPS in XNA). That could be what is causing these errors to be more apparent.

Nov 27, 2009 at 3:54 PM

Ok well I made a change that might fix some of the crashes you were seeing. Are things running slower than in the old code? 

Nov 27, 2009 at 7:51 PM

I haven't noticed any slower performance. I did want to profile the code though to see why it was so much slower in SilverSprite than XNA. I'm drawing some lines by stretching a 1x1 pixel Texture2D. Does anything with that technique jump out at you as something that would perform badly in SilverSprite?

Nov 27, 2009 at 8:12 PM
Edited Nov 29, 2009 at 7:54 PM

ok, I grabbed the latest and replaced my copy of SilverSprite with it. Everything I mentioned above seems to be working fine now (still working on the slowness above but unrelated to latest major changes).

There appears to be only one final problem with the latest source code. It is not an error, but a bug in MeasureString. It seems that since Dispatcher.Invoke is causing the measure operation to take place on a different thread, the MeasureString method from time to time returns 0,0 for a non empty string because the code on the original thread isn't waiting on the code for the UI thread to complete. In other words, after Dispatcher.Invoke is called but before Dispatcher.Invoke can complete MeasureString is completing. I had this problem as well when I was trying to put a fix in for the cross-threading error I reported a few months back. I never found a suitable solution other than moving Dispatcher.Invoke to farther up the stack into my code which is of course not a solution for everyone. Below is more information about the code.

Code File: Graphics.SpriteFont.cs

Snippet:

 

public Vector2 MeasureString(string text, float maxWidth)
{
    if (this is BitmapSpriteFont)
    {
        return ((BitmapSpriteFont)this).InternalMeasureString(text);
    }
    if (text == null) text = "";

    if (CacheStringMeasurements)
    {
        if (textSizes.ContainsKey(text))
        {
            return textSizes[text];
        }
    }

	Vector2 size = Vector2.Zero;

	if (tb.CheckAccess())
	{
		tb.Text = text;
		size = new Vector2((float)tb.ActualWidth, (float)tb.ActualHeight);
		tb.Width = maxWidth;
	}
	else
	{
		tb.Dispatcher.BeginInvoke(() =>
		{
			size = new Vector2((float)tb.ActualWidth, (float)tb.ActualHeight);
			tb.Width = maxWidth;
			tb.Text = text;
		});
	}


	if (CacheStringMeasurements)
	{
		textSizes.Add(text, size);
	}
    return size;
}

UPDATE

This works (but it's a little slow). I also fixed the bug we found earlier inside the BeginInvoke code block as well (it was in two places).

public Vector2 MeasureString(string text, float maxWidth)
{
    if (this is BitmapSpriteFont)
    {
        return ((BitmapSpriteFont)this).InternalMeasureString(text);
    }
    if (text == null) text = "";

    if (CacheStringMeasurements)
    {
        if (textSizes.ContainsKey(text))
        {
            return textSizes[text];
        }
    }

    Vector2 size = Vector2.Zero;

    if (tb.CheckAccess())
    {
        tb.Text = text;
        size = new Vector2((float)tb.ActualWidth, (float)tb.ActualHeight);
        tb.Width = maxWidth;
    }
    else
    {
        tb.Dispatcher.BeginInvoke(() =>
        {
            tb.Text = text;
	        size = new Vector2((float)tb.ActualWidth, (float)tb.ActualHeight);
	        tb.Width = maxWidth;			        
        });
    }


    if (CacheStringMeasurements)
    {
        textSizes.Add(text, size);
    }

    while (!string.IsNullOrEmpty(text) && size == Vector2.Zero)
    {
        Thread.Sleep(10);
    }

    return size;
}