Only clear and draw when Enter is pressed

If I wanted to just draw one frame and then only draw another one, if I hit Enter would this work fine with how MonoGame is setup?
I mean while the scene is not changing, a (re)draw would not be necessary, right? So I thought if I only need to draw a new frame every now and then can I setup this by not clearing the backbuffer and if on update the Enter key is pressed only then clear and draw one more frame until this happens again (Enter key is pressed)?

It’s kind of fallen out of favour but this type of rendering used to be referred to as retained mode rendering (as opposed to immediate mode). Nothing out of the box exists for retained mode in MonoGame (to my knowledge), but you can build yourself a framework for it.’

The basic idea is you construct a data structure that contains your objects and each object is aware of its properties. If something changes on an object, the scene detects that it needs to re-render itself (or part of itself) and then does so. You can achieve this with a render target that represents your scene and only redraw it if you need to, otherwise just draw the resulting texture.

I used a simple implementation of this in a font scaling prototype I did a while back, you can find the post here:

I keep texture for each text element that contains the rendered result at the appropriate scale and just draw that texture. If the font size for the text needs to change, I re-render the cached texture. You can use a similar approach with other objects.

This is just a simple example though. Robust implementations can be quite complex!

To take it a step further, if you want to prevent even the texture from rendering, I think you’d have to look under the hood at how MonoGame presents the backbuffer to the screen. For maybe a more short-term exploration, look into MonoGame (XNA) editors integrated into WinForms. That should give you additional control over when a render pass occurs.

Check Game.SupressDraw, if I understood correctly I think it does what you want to do.

3 Likes

SupressDraw() was exactly what I was looking for, always good to ask questions here :slight_smile:

3 Likes

I had time today to check the Smooth Font Scaling Demo and since I don’t have earlier versions of MonoGame installed I ported it over to MonoGame 3.8.

After that I tried to change the reoslution by changing the values in the MainGame.Initialize method but then the GUI elements did work as expected, the Mouse had to be on a position with offset to make them “think” the mouse was over them if that makes sense.
Is there something which I need to change somewhere in the code to make it work with a different resolution?

I changed

graphics.PreferredBackBufferWidth = 1820;
graphics.PreferredBackBufferHeight = 1024;

to

graphics.PreferredBackBufferWidth = 1920;
graphics.PreferredBackBufferHeight = 1080;

No… and that’s very strange. I went and tried it on my end with the same values you did, as well as any number of other values, and it always behaved as expected. I checked the code and I’m not doing any kind of scaling in either the picking or the rendering (other than the scaling of the font/texture itself). It works on the raw mouse position provided by MouseState.GetState().Position.ToVector2().

This is on MonoGame 3.7.1, but what would change in moving to MonoGame 3.8?

It was only a quick test when I have more time I will try to reproduce it and do a recording.

Maybe it helps to verify the actual values of the backbuffer and draw them.
Does MonoGame check maybe if the window would fit and reduces the resolution to the maximum visible area or something? Because with the Taskbar there is not the full height available for fullhd resolution.

Oh sorry! I didn’t see you had the height as 1080 and I was still using 1024. Yes, that’s the problem… the window itself scales weirdly. This is an issue outside my code and it definitely is annoying :slight_smile:

Just set the resolution to anything that will fit on your screen, or toggle fullscreen on (though don’t forget to add an Exit call somewhere so you can get out, I guess). It should work for you then.


		protected override void LoadContent()
		{
			spriteBatch = new SpriteBatch(GraphicsDevice);

			FontBank fontBank = new FontBank();
			fontBank.Load(this.Content, "Fonts");

			IFontScaler fontScaler = new SimpleFontScaler(fontBank);

			var textEntity = new TextEntity()
			{
				Colour = Color.Cyan,
				Text = "This is a test.",
				Bounds = new RectangleF(100, 100, 100, 25),
				FontName = "Calibri",
				FontSize = 20,
				Behaviour = TextEntityBehaviour.AutoScale,
			};

			var textEntityView = new TextEntityView(
				textEntity,
				fontScaler,
				spriteBatch,
				fontBank
			);

			var textEntityEditorView = new EditorView(
				textEntityView,
				spriteBatch
			);

			views.Add(textEntityEditorView);
			controllers.Add(new EditorViewController(textEntityEditorView));
			controllers.Add(new TextEntityViewController(textEntityView, graphics.GraphicsDevice, spriteBatch));

			//

			var Width = graphics.GraphicsDevice.PresentationParameters.BackBufferWidth;
			var Height = graphics.GraphicsDevice.PresentationParameters.BackBufferHeight;

			var textEntityResolution = new TextEntity()
			{
				Colour = Color.Red,
				Text = $@"{Width}x{Height}",
				Bounds = new RectangleF(100, 200, 400, 75),
				FontName = "Calibri",
				FontSize = 20,
				Behaviour = TextEntityBehaviour.AutoScale,
			};

			var textEntityViewResolution = new TextEntityView(
				textEntityResolution,
				fontScaler,
				spriteBatch,
				fontBank
			);

			var textEntityEditorViewResolution = new EditorView(
				textEntityViewResolution,
				spriteBatch
			);

			views.Add(textEntityEditorViewResolution);
			controllers.Add(new EditorViewController(textEntityEditorViewResolution));
			controllers.Add(new TextEntityViewController(textEntityViewResolution, graphics.GraphicsDevice, spriteBatch));

		}

So quickly tried out some stuff. The PresentationParameters.BackBufferWidth and *Height values are fine I guess, checked in the debugger and in the second screenshot and in the YouTube video I draw the values.

For the drawing I copy pasted the code I found and basically appended Resolution to the variable names. The changed code is in the last reply.

I tried with both fullscreen and window. In both cases I had an offset issue in the y direction, x direction seems to always work fine.

Oh and I found a variable named tolerance or something similar. It was set to 1.25f.
I changed the value (after I did the recording) to 1.0f but it did not solve the issue.

Per what I said above, don’t set your windows resolution to something smaller than can fit on your screen (including task bar). Try setting your resolution to something like 1024x768 and see if you have the same issue… I suspect you won’t.

If you do though, reach out to me on a private message. Maybe we can jump on discord together and get to the bottom of it.

Those just have to do with how close you have to be to the controls in order to interact with them. It’s just to make it easier for you, as a user, to grab the resize handle so you don’t have to be right in the middle of a 2 pixel wide circle.

All I really wanted you to see here was how I cached the text result into a Texture2D so that on subsequent draw calls, I draw a texture instead of having to render the text. You can use this method to build yourself a retained mode rendering package where components, or groups of components on your scene render to a static image. The first time this is refreshed the draw is costly, but subsequent draws are just the cached texture.

I used to work with a graphics drawing package that worked on this principle called HOOPS. Nobody’s ever heard of it but my coworkers at the time told me it was quite the thing back in the 90’s lol.

1 Like

This sounds like the same issue I encountered here: https://github.com/MonoGame/MonoGame/issues/7654

Only, in my case I was experiencing a horizontal offset because my taskbar was docked to the left at the time. Try it using DesktopGL and see if the problem goes away.

2 Likes

Ok I see what you mean with the retained mode and that is what you show in the project.
Seems to just ne an issue with MonoGame. Just wanted to show what I was experiencing so that is sometimes easier because I am sometimes describing things not optimal. I should improve on that.
Fonts are apart from the retained rendering demo also interesting for me. I just wondered why the offset occurred. I will check / draw the mouse coordinates and also switch between DirectX and OpenGL to see if this is the same issue like @HopefulToad mentioned.

1 Like

I think you are right that this is the same thing happening here. When using OpenGL this does not happen and the controls are working like they should.

Hopefully I did not confuse some of those values but what should be clear from the testing is that there are issues when using WindowsDX…

OpenGL @ FullHD (1920x1080) screen resolution - mouse coordinates @ Top-left corner

Backbuffer @ 1920x1080 and fullscreen true: 0,0
Backbuffer @ 1920x1080 and fullscreen false: 0,0

OpenGL @ FullHD (1920x1080) screen resolution - mouse coordinates @ Bottom-right corner

// moved the window to make the bottom-right corner visible!
this.Window.Position = new Point(-100, -200);
...
Backbuffer @ 1920x1080 and fullscreen true: 1919,1079
Backbuffer @ 1920x1080 and fullscreen false: 1919,1079

WindowsDX @ FullHD (1920x1080) screen resolution - mouse coordinates @ Top-left corner

// moved the window to make the top-left corner visible!
this.Window.Position = new Point(100, 100);
...
Backbuffer @ 1920x1080 and fullscreen true: 0,24
(After switching to desktop and returning to the app it changes: 0,0)
...
Backbuffer @ 1920x1080 and fullscreen false: 0,0

WindowsDX @ FullHD (1920x1080) screen resolution - mouse coordinates @ Bottom-right corner

Backbuffer @ 1920x1080 and fullscreen true: 1919,1103
(After switching to desktop and returning to the app it changes: 1919,1079
...
Backbuffer @ 1920x1080 and fullscreen false: 1919,1059

Yep, seems like the same bug. A user on Github gave me a detailed analysis of the problem here in that same issue. It was a bit technical and some of it went over my head, but the issue seems to be some code in MonoGame using Screen.WorkingArea improperly, resulting in a mouse location offset error affected by the taskbar.

Try setting your taskbar to auto-hide and check the results. I believe it will cause the error to not appear, since the WorkingArea would then return the full display area.

1 Like

I don’t know the MonoGame source so I also don’t know much about what is going on there but since you mentioned there you never heard of WndProc… This comes from the WinAPI being used and how it works. There the WndProc is a thing which is used in some way. I think an application window registers to windows and then gets callbacks from windows whenever something happens to it like when it gets moved or resized. The application has to deal with that then.
Maybe for OpenGL the screensize or relevant area is calculated by some other code although I wonder why because should be the same for both backends.

Nah it’s ok, all part of communication. I just wanted to make sure you were getting the intent of what I wanted to show you. I think the solution provided by @KakCAT is definitely up your alley, but that kind of retained drawing style can save frames but keep your game feeling responsive. It’s just a fair bit of work to set up and leads to some awkward feeling API interactions sometimes :slight_smile:

It’s interesting that this doesn’t happen on OpenGL! Interestingly, I used to see this issue at my old job (where I used HOOPS). We couldn’t make windows that spanned across two monitors because of similar rendering/offsetting problems. From what was discussed in HopefulToad’s github issue though, it sounds like the issue is on the MonoGame side.