3
votes

I've been working on a cross-platform framework/abstraction-layer for a number of months now, based on the XNA framework for WP7 and Mono for Android and iOS. Having got the majority of functionality working for Android, I have recently returned to WP7 in order to get a simple game working on both platforms.

The trouble is, I've encountered a very strange graphical error. The error does not occur in a Windows-based XNA game, and only occurs on WP7 when the orientation is Landscape (either by default or specified). The glitch appears on the emulator, but not the physical device. For testing purposes, I am displaying a single image on a red background.

Windows XNA

Windows http://www.genius-i.com/images/Windows.png

Windows Phone 7 (Portrait)

Portait http://www.genius-i.com/images/Portrait.png

Windows Phone 7 (Landscape)

Landscape http://www.genius-i.com/images/Landscape.png

Obviously the landscape image is not coming out as expected. While it's all handled through an abstraction layer, the XNA platform files are identical, the only difference between the Windows and WP7 versions being the target platform/framework.

Similarly, the calls to the graphics device and renderer are identical.

During Construction ( Extracted from various classes and functions )

device = new Microsoft.Xna.Framework.GraphicsDeviceManager(game);
device.PreferredBackBufferHeight = 480;
device.PreferredBackBufferWidth = 800;
Backend.SupportedOrientations = DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
// Whether or not I set the orientation, the glitchoccurs.

During Initialisation ( Extracted from various classes and functions )

renderer = new SpriteBatch(device.GraphicsDevice);

During Drawing

device.Clear(Color.Red);
renderer.Begin();
renderer.Draw(ZNGTexture, new Rectangle(10, 400, 145, 66), null, Color.White, 0f, Vector.Zero, SpriteEffects.none, 0f);
renderer.End()

Does anyone have any insight into what might cause a graphical glitch such as the one I've described?

Thanks for any help you might offer, and sorry to intrude with a second question over the course of a few days.

Full Code

To help in identifying the issue, I'm presenting a good deal of the actual code. I originally excluded it because it's a bit long, and the classes are simple wrappers for most of XNA's functionality.

Game Class

public class Game : Microsoft.Xna.Framework.Game, IGame
{
    #region Data: Fields

    private GraphicsBackendXNA graphics;
    private Renderer2DXNA renderer;
    ContentLoaderXNA content;

    #endregion

    #region Data: Properties

    public GraphicsBackend Backend { get { return graphics; } }
    public Renderer2D Renderer { get { return renderer; } }
    public ContentLoader Loader { get { return content; } }

    #endregion

    #region Methods: Construction

    public Game()
    {
        graphics = new GraphicsBackendXNA(this);
        content = new ContentLoaderXNA(this.Services, "Content");
        FinaliseConstruction();
    }

    protected virtual void FinaliseConstruction() { }

    #endregion

    #region Methods: Initialisation

    protected override void Initialize()
    {
        Core.Console.Initialise(); // Initialised once for error logging.
        base.Initialize();
        renderer = new RendererXNA(graphics);
        Core.Console.Initialise(renderer); // Initialised a second time for graphics services.

        Transform.ScreenSize = new Vector2(Backend.ScreenWidth, Backend.ScreenHeight);

        Core.Audio.MediaPlayer.Initialise(new Audio.MediaHandler());
        Core.Audio.SoundEffect.Initialise(Audio.SoundEffectXNA.SetVolume);

        Core.Input.Input.Set(new KeyboardReaderXNA(), new MouseReaderXNA(), new GamePadReaderXNA(), new Input.Touch.TouchPanelXNA(), new GamePadOutputXNA());
        Core.Input.InputManager.Initialise();
    }

    #endregion

    #region Methods: Loading and Unloading Content

    protected override void UnloadContent()
    {
        content.Unload();
        base.UnloadContent();
    }

    #endregion

    #region Methods: Drawing

    protected override void EndDraw()
    {
        Camera2D.AllOff();
        base.EndDraw();
    }

    #endregion

Graphics Backend Class

The graphics backend (XNA) class is a simple wrapper class. It extends the GraphicsBackend class, which is abstract and contains no functionality.

public class GraphicsBackendXNA : GraphicsBackend
{
    #region Data: Fields

    private Microsoft.Xna.Framework.GraphicsDeviceManager device;

    #endregion

    #region Data: Properties

    public override Display Display { get { return device.GraphicsDevice.PresentationParameters.ToHeron(); } }
    public override Core.Graphics.Viewport Viewport { get { return device.GraphicsDevice.Viewport.ToHeron(); } }

    public override int ScreenWidth { get { return device.PreferredBackBufferWidth; } set { device.PreferredBackBufferWidth = value; } }
    public override int ScreenHeight { get { return device.PreferredBackBufferHeight; } set { device.PreferredBackBufferHeight = value; } }

    public override bool IsFullScreen { get { return device.IsFullScreen; } set { device.IsFullScreen = value; } }
    public override Core.DisplayOrientation SupportedOrientations
    {
        get { return ((Core.DisplayOrientation)((int)device.SupportedOrientations); }
        set { device.SupportedOrientations = (Microsoft.Xna.Framework.DisplayOrientation)((int)value); }
    }

    #endregion

    #region Methods: Construction

    public GraphicsBackendXNA(Microsoft.Xna.Framework.Game game)
    {
        device = new Microsoft.Xna.Framework.GraphicsDeviceManager(game);
    }

    #endregion

    #region Methods: Settings

    public override void ApplyChanges()
    {
        device.ApplyChanges();
    }

    #endregion

    #region Methods: Rendering

    public override void Clear(Colour colour)
    {
        device.GraphicsDevice.Clear(new Microsoft.Xna.Framework.Color(colour.R, colour.G, colour.B, colour.A));
    }

    #endregion

    #region Methods: Renderer

    public override Renderer2D CreateRenderer() { return new Renderer2DXNA(this); }

    #endregion

Renderer 2D XNA Class

The Renderer 2D XNA class is another simple wrapper. It inherits from the Renderer2D base class, which implements a great number of abstract methods for drawing content. it has some very basic functionality which I will details first, but I won't bother including all the blank methods.

public class Renderer2D : IDisposable
{
    private GraphicsBackend backend;
    private Matrix currentMatrix;

    public virtual GraphicsBackend Backend { get { return backend; } protected set { backend = value; } }
    public Matrix CurrentMatrix { get { return currentMatrix; } set { currentMatrix = value; } }

    public Renderer2D(GraphicsBackend backend) { this.backend = backend; CurrentMatrix = Matrix.Identity; }
}

public class Renderer2DXNA : Renderer2D
{
    #region Static: 1 Pixel Texture

    private static Texture2D filler;
    private static bool Initialised { get { return filler != null; } }

    public static void Initialise(GraphicsBackendXNA backend)
    {
        filler = CreateFiller(backend)
    }

    private static Texture2D CreateFiller(GraphicsBackendXNA backend)
    {
        Texture2D rt = new Texture2D(backend.Device.GraphicsDevice, 1, 1);
        rt.SetData<Microsoft.Xna.Framework.Color>(new Microsoft.Xna.Framework.Color[] { Microsoft.Xna.Framework.Color.White });
        return rt;
    }

    #endregion

    #region Data: Fields

    private SpriteBatch renderer;

    #endregion

    #region Methods: Construction

    public Renderer2DXNA(GraphicsBackendXNA backend) : base(backend)
    {
        if (!Initialised)
            Initialise(backend);

        renderer = new SpriteBatch(backend.Device.GraphicsDevice);
    }

    #endregion

    #region Methods: Begin and End

    public override void Begin(Core.Graphics.SpriteSortMode sortMode, BlendMode blendMode, Core.Matrix transformMatrix)
    {
        renderer.Begin(); // Obviously not fully implemented, but simple for now.
    }

    public override void End() { renderer.End(); }

    #endregion

    #region Methods: Draw Image

    public override void Draw(Image Image, Core.Rectangle Boundary, Core.Rectangle? Source, Colour DrawColour, float RotationRadians, Core.Vector2 Origin, Core.SpriteEffects Effects, float Layer)
    {
        if (!(Image is ImageXNA))
            return;

        ImageXNA realImage = Image as ImageXNA;
        Microsoft.Xna.Framework.Rectangle finalSource = 
            Source.HasValue ? Source.Value.ToXNARectangle()
            : new Microsoft.Xna.Framework.Rectangle(0, 0, realImage.Width, realImage.Height);

        renderer.Draw(
            realImage.Texture,
            Boundary.ToXNARectangle(),
            finalSource,
            new Microsoft.Xna.Framework.Color(DrawColour.R, DrawColour.G, DrawColour.B, DrawColour.A),
            RotationRadians,
            Origin.ToXNAVector2(),
            (SpriteEffects)((int)Effects),
            Layer);
    }         

    #endregion

}
// I'll leave off string and shape drawing, considering they're not being used here.
1
Could you see what happens if you actually use GraphicsDeviceManager properly? You should be setting the desired graphics settings in the constructor, which Game will then use to initialise the device before Initialize is called. No idea if this will fix your problem - but it's worth a shot.Andrew Russell
I tried moving all the graphics settings into the constructor and dropped the call to ApplyChanges, as recommended in the link you provided. Unfortunately the problem persists. It's obvious to me that I've stuffed up somewhere along the line, but it's very strange that it only occurs on landscape settings on the phone, that being the ONLY thing changed between the examples. I'll keep looking, but any idea what could cause this sort of glitch under these specific circumstances? (Also, thanks for your help so far, it may not have fixed the problem but it's good practice anyway.)Nathan Runge
Glad to help - pity it wasn't as simple as fixing that up. Can you clarify: is the code you posted the minimum code required to reproduce the issue? Can you boil it down to a simple reproduction case? And also: does this happen on hardware or the simulator or both?Andrew Russell
I'm putting together the full set of relevent code that applies to the issue, the above code is the full set of code that makes any reference to the graphics device or spritebatch. I've just run a test, and it seems the error does NOT occur on the actual hardware, just the emulator. This is certainly good news, but seems even more confusing.Nathan Runge
I've added a lot of the code for the more important classes. There are some other relevant classes, structs and enums which I can get on shortly. I've checked them in fair detail though, and so far no issues.Nathan Runge

1 Answers

2
votes

adding device.IsFullScreen = true; in the constructor fixed similar problem for me. I would see the pattern of diagonal lines whenever I tried to draw anything.

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

        graphics.PreferredBackBufferWidth = 480;
        graphics.PreferredBackBufferHeight = 800;
        graphics.IsFullScreen = true;

        // Frame rate is 30 fps by default for Windows Phone.
        TargetElapsedTime = TimeSpan.FromTicks(333333);

        // Extend battery life under lock.
        InactiveSleepTime = TimeSpan.FromSeconds(1);
    }

Here's how my glitch looked like