This project is read-only.

Split Screen Performance

May 2, 2010 at 1:45 AM
Edited May 2, 2010 at 1:46 AM

Hello,

We're trying to implement a split screen game and see huge performance drop with more than one player. (1p 60fps, 2p 30fps, 3p 25fps, 4p 20fps),

Thus I wanted to know whether this drop is to be expected (since it has to render up to 4 times per frame) or whether we're just doing something seriously wrong.

 

       protected override void Draw(DrawState state)
		{
            if (drawLock) return;
            if (numActivePlayers > 0) drawToScreen.ClearBuffer.Enabled = true;
            if (numActivePlayers > 0) drawToScreen.ClearBuffer.ClearDepthEnabled = true;
            //perform the draw to the screen.
            for (int i = 0; i < numActivePlayers; i++)
            {
                graphics.GraphicsDevice.RenderState.DepthBufferEnable = true;
                graphics.GraphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
                graphics.GraphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Wrap;
                viewportModifiers[i].Enabled = true;
                drawPlayerID = i; //For Playerspecific Drawing Routines they may get game.DrawPlayerID
                drawToScreen.Camera = activePlayers[i].camera;
                drawToScreen.Draw(state);
                drawToScreen.ClearBuffer.Enabled = false;
                viewportModifiers[i].Enabled = false;
            }
            drawMenu.Draw(state);
            //at this point the screen has been drawn...
        }
This is our Main Draw Method
drawToScreen and drawMenu being DrawTargetScreens and vieportModifiers an array of modifiers that are added with upon creation
                drawToScreen.InsertModifier(0, viewportModifiers[i]);


The first three lines in the for loop reset the drawstate since the SpriteBatch tampers some how with it.
Thank you for your help!
Best

Rafael

 

May 2, 2010 at 10:37 AM
Edited May 2, 2010 at 10:42 AM

I highly recommend you only use one DrawTargetScreen object in your game. There are a number of reasons for this.

I also highly recommend that you do not change the render state directly, but I see you say you are using SpriteBatch (The Xen equivalent to SpriteBatch is SpriteElement in Xen.Ex.Graphics2D). SpriteBatch is pretty well known for messing stuff up in a lot of games :-)
 Xen internally keeps track of all current render state (including texture addressing state, active buffers, etc). As you have probably discovered, if you directly change this on the GraphicsDevice, then you can easily get into a situation where you will set a render state using DrawState.RenderState, or a texture sampler state via a shader (for example), and Xen will not apply your change correctly. This happens because internally it believes the state doesn't need changing, but because it has been changed elsewhere it does. There are significantly performance reasons for doing this. You can use a call to 'state.DirtyInternalRenderState()' to tell it the state has changed.

Further, I would recommend you do split screen in the following way:

For each player, they have a DrawTargetTexture2D. Sized according to how many players there are (eg, the size of the screen for 1 player, half for two players).
Then, in the draw screen object, simply have a Quad draw the texture to the players part of the screen (so you don't need to mess with viewports). Then finally, the menu is drawn on top.

The main setup would then look like:

			screen = new DrawTargetScreen();

			//setup the player views:
			for (int i = 0; i < players; i++)
			{
				//create the draw target texture
				DrawTargetTexture2D playerTexture = new DrawTargetTexture2D(camera, width, height, SurfaceFormat.Color, DepthFormat.Depth24Stencil8);
				//add some object to the texture, used to draw the scene for the player
				playerTexture.Add(new PlayerSceneDrawer(i));

				//create a quad to display the texture on screen
				TexturedElement display = new TexturedElement(playerTexture, new Vector2(width,height), false);
				//offset display position accordingly
				display.Position = new Vector2(offset);
				//add to the screen
				screen.Add(display);

				//store the target
				textures.Add(playerTexture);
			}

			//add menu to screen
			screen.Add(menu);

 

The main Draw method would then be really really simple:

			//Main render loop:
			//draw the player views
			foreach (DrawTargetTexture2D view in textures)
				view.Draw(state);

			//draw the screen
			screen.Draw(state);