-1

I'm building a game for Windows Phone 8 using MonoGame. A free version of it will have ads at the top. For displaying ads I'm using AdRotator. The game has option to post results to the Facebook as an image. Recently, while testing I've find out that game completely freezes while posting image to Facebook. During debugging I was able to detect code that causes that freeze. This code is a part of MonoGame:

var waitEvent = new ManualResetEventSlim(false);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
    var bitmap = new WriteableBitmap(width, height);
    System.Buffer.BlockCopy(pixelData, 0, bitmap.Pixels, 0, pixelData.Length);
    bitmap.SaveJpeg(stream, width, height, 0, 100);
    waitEvent.Set();
});

waitEvent.Wait();

So, MonoGame thread is waiting for waitEvent to be set, but action isn't called by BeginInvoke. When there is no AdControl everything is fine, so for me it looks like AdRotator and MonoGame are conflicting somehow, but I don't really understand why this could happen.

UPD: code sample that calls code from above

RenderTarget2D renderTarget = new RenderTarget2D(ScreenManager.GraphicsDevice, width, height, false, SurfaceFormat.Color, DepthFormat.Depth24);

ScreenManager.GraphicsDevice.SetRenderTarget(renderTarget);
ScreenManager.GraphicsDevice.Clear(Color.White);

SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
spriteBatch.Begin();

// drawing some graphics

spriteBatch.End();

ScreenManager.GraphicsDevice.SetRenderTarget(null);

using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
     using (var stream = store.OpenFile(FacebookShareFileName, FileMode.Create, FileAccess.ReadWrite))
     {
         renderTarget.SaveAsJpeg(stream, width, height);
     }
}

so the game freezes on

renderTarget.SaveAsJpeg(stream, width, height);
adogg
  • 29
  • 6
  • 4
    Where is this code called from? You have to be absolutely sure that you're not already in the UI thread, otherwise a deadlock will happen and your application will freeze. That's most likely what's happening. – Kevin Gosse Sep 17 '13 at 11:55
  • 1
    This is called *deadlock*. Your Wait() call prevents the BeginInvoke delegate from getting dispatched. You will have to remove the Wait(). – Hans Passant Sep 17 '13 at 12:47
  • @KooKiz: As I wrote, this code is called on MonoGame thread, which is different from UI thread (double check it in Threads window). Also this is happening only when there is AdControl present. Otherwise everything is ok. – adogg Sep 17 '13 at 14:31
  • @adogg When the freeze happens, check the callstack of the UI thread to understand what is blocking it – Kevin Gosse Sep 17 '13 at 14:32
  • @KooKiz: maybe I'm wrong, but for that I need to put a breakpoint inside the action that is called by BeginInvoke? But that breakpoint isn't hit at all. – adogg Sep 18 '13 at 06:59
  • @adogg No. When the application is freezed, press the 'pause' button of the debugger. From there, you can check the callstack of every thread by switching the "threads" dropdown list. You can also see all of them at a glance by using the "Parallel stacks" window (Debug -> Windows -> Parallel stacks). If "[External Code]" is displayed in one of the callstacks, right click on it and select "show external code" – Kevin Gosse Sep 18 '13 at 08:54
  • @KooKiz: thanks for the great explanation. I've just checked that and I can see 2 thread - one is executing code from above and another one pointing to Microsoft.Xna.Framework.UserAsyncDispatcher.AsyncDispatcherThreadFunction(). It looks like MonoGame still using something from XNA. Things are getting more complicated... – adogg Sep 18 '13 at 11:33
  • @adogg I'm not really surprised that MonoGame is implemented on top of XNA, that's by far the easiest way. And it's important to know that XNA's main loop is built upon Silverlight's DispatcherTimer, and therefore runs on the Dispatcher thread. I don't know if MonoGame changed that part, but if they didn't then it can help to understand your issue. That's a very interesting problem. If you don't mind me having a look at your code, contact me through my blog (in my profile) or twitter and we'll debug that together. – Kevin Gosse Sep 18 '13 at 11:39
  • @KooKiz: will try to create sample project and post it to MonoGame on GitHub. – adogg Sep 18 '13 at 11:43

1 Answers1

0

Try this,

Pass the Sync Context to the method.

SynchronizationContext uiThread = SynchronizationContext.Current;

    public WritableBitmap GetImage(SynchronizationContext uiThread))
    {
        WriteableBitmap bitmap = null;
        var waitHandle = new object();
        lock (waitHandle)
        {
            uiThread.Post(_ => 
            {
                lock (waitHandle)
                {
                   bitmap = new WriteableBitmap(width, height);
                   System.Buffer.BlockCopy(pixelData, 0, bitmap.Pixels, 0, pixelData.Length);
                   bitmap.SaveJpeg(stream, width, height, 0, 100);
                   Monitor.Pulse(waitHandle);
                }

            }, null);

            Monitor.Wait(waitHandle);
        }
        return bitmap;
   }

And invoke this method in a different thread;

        SynchronizationContext uiThread = SynchronizationContext.Current;
        var result = await Task.Factory.StartNew<WriteableBitmap>(() =>
        {
            return GetImage(uiThread);
        });

I didn't added the pixelData Your pixel array to the code. You please pass that information to your code. Also I typed the code from mind, not tested in VS editor. So you may find some typos, You please ignore that. Hope this will solve your issue. Enjoy coding

Bridge
  • 29,818
  • 9
  • 60
  • 82
Rakesh R Nair
  • 1,736
  • 2
  • 12
  • 30