Adding Virtual Resolution to Grey Alien Framework

BlitzMax Forums/BlitzMax Programming/Adding Virtual Resolution to Grey Alien Framework

therevills(Posted 2011) [#1]
Hi All,

Just wondering apart from Jake has anyone added Virtual Resolution support to Grey Alien Framework (v1.11)?

If so could you please share your code?

I've started to add support for it based on this post:
http://www.blitzbasic.com/Community/posts.php?topic=94408

I've added this to Commoncode.bmx at line 144:
Global WindowWidth:Int = 800
Global WindowHeight:Int = 600
Global VirtualRatioX:Float
Global VirtualRatioY:Float
Global UsingVirtualResolution:Int = 0


Then in CommonTypes.bmx:

TGame.GraphicsCreate after the If FirstTime statement (Line 858):



Then change all the Graphics commands to use WindowWidth and WindowHeight instead of ScreenWidth and ScreenHeight eg:

			If GraphicsModeExists(WindowWidth, WindowHeight, 32) Then
				OK = Graphics(WindowWidth, WindowHeight, 32, Hertz)
				Depth = 32
			EndIf


Then right at the bottom of TGame.GraphicsCreate:
DoSetVirtualResolution()


and add DoSetVirtualResolution() to TGame:
	Method DoSetVirtualResolution()
		If ScreenWidth <> WindowWidth Then
			SetVirtualResolution(ScreenWidth, ScreenHeight)
			VirtualRatioX = ScreenWidth / Float(WindowWidth)
			VirtualRatioY = ScreenHeight / Float(WindowHeight)
			UsingVirtualResolution = 1
		End If
	End Method


So I've done all this and having issue with the mouse stuff...

I added this to TMousePointer.UpdateCoords:

		If pt <> Null Then
			'Use client area position so we can see if the cursor is outside of window.
			Game.SetClientCoords() 'Need this in case window has been dragged as drag code doesn't alway set it soon enough.
			If UsingVirtualResolution Then
				x = (pt.x - Game.ClientX) * VirtualRatioX
				y = (pt.y - Game.ClientY) * VirtualRatioY
			Else
				x = pt.x - Game.ClientX
				y = pt.y - Game.ClientY
			EndIf
		EndIf


But this messes up the Menu mouse overlaps...

Last edited 2011

Last edited 2011


therevills(Posted 2011) [#2]
Okay...

Not sure why but GAF sets Mouse.X and Mouse.Y in TMousePointer.UpdateCoords and TGame.DoPeekEvent, so I've added the ratio stuff there too:

			Case EVENT_MOUSEMOVE
			
				If UsingVirtualResolution Then
					Mouse.X = EventX() * VirtualRatioX
					Mouse.Y = EventY() * VirtualRatioY
				Else
					Mouse.X = EventX()
					Mouse.Y = EventY()			
				End If


Is there anything else I need to be aware of with the Virtual Resolution setting in GAF?

Thanks!


therevills(Posted 2011) [#3]
Hmmm strange...

I changed my screen resolution to 800x600 and ran this code:



For some reason it only displays the line from 0, 0 to 640, 480 not the full screen, there is a slight flicker at the start which looks like it does initially draw the line 0,0,1024,768.

I noticed that in my GAF game when I was alt-tabbing it also does this effect, I've added a fix to TGame.CheckWindow to recreate the graphics context but it seems very strange: (see edit below)

If Suspended = True
	'should resume the music/speech? (only if not paused)
	
	If UsingVirtualResolution
		Mouse.ClearButtons()
		GraphicsCreate()
		RestoreMouseCoords()
	End If


[edit]
Just found that Chroma raised this issue:
http://www.blitzbasic.com/Community/posts.php?topic=93081

And his fix is alot cleaner than mine so ignore the above and just add this to DoSetVirtualResolution:

	Method DoSetVirtualResolution()
		If ScreenWidth <> WindowWidth Then
			SetVirtualResolution(ScreenWidth, ScreenHeight)
			VirtualRatioX = ScreenWidth / Float(WindowWidth)
			VirtualRatioY = ScreenHeight / Float(WindowHeight)
			SetViewport 0, 0, ScreenWidth, ScreenHeight
			UsingVirtualResolution = 1
		End If
	End Method

[/edit]

Last edited 2011

Last edited 2011


therevills(Posted 2011) [#4]
Final post of the night...

I've found a couple of posts saying that the Virtual Resolution code doesnt work for DirectX7 on certain PCs...

http://www.blitzbasic.com/Community/posts.php?topic=94446#1083571

So do I disable the Virtual resolution stuff if the player only has DX7 installed?

Also I'm a bit worried about this post too:

http://www.blitzbasic.com/Community/posts.php?topic=94616#1086210

What was the conclusion?

Thanks!

Last edited 2011


Grey Alien(Posted 2011) [#5]
You also need to modify TScreen.ImageLoad() so that it uses Flags-1 if UsingVirtualResolution is true so that images are smoothed when scaled.

It's true that viewport used to have issues, but I've used the viewport in DX9 with Virtual Resolution no problems with no reports of fails (it might still fail, but I bet number of PCs it fails on is low).

DoSetVirtualResolution() is called after I successfully set fullscreen in Graphics Create:

Local OK:TGraphics = Null
If FullScreen Then
   ...
   If FullScreen Then DoSetVirtualResolution()
EndIf


and after setting Windowed mode:
If OK = Null Then
   ...
Else
   DoSetVirtualResolution()
EndIf


You are indeed correct with adding SetViewport(0, 0, ScreenWidth, ScreenHeight) to DoSetVirtualResolution(). I did that too.

Yes I'm only using it for DX9. I'm now using DX9 by default with the framework. One issue is the input lag on DX9 in full-screen and windowed mode. This works in full-screen, but not windowed (in windowed I offer users the option of turning off the custom cursor).

' -----------------------------------------------------------------------------
' ccFixLagDX9: Fixes DirectX9 rendering lag in BlitzMax
' -----------------------------------------------------------------------------
Function ccFixLagDX9()
	Local Temp:TPixmap
	Temp = GrabPixmap(1, 1, 1, 1)
End Function


Call it before ccFlip() in PostDraw().

		?Win32
			'This lag fix works for DX9 and OpenGL.
			If (DriverDX9 Or Driver = 1) And FullScreen Then ccFixLagDX9()
		?


To fix mouse issues I did this to DoMoveMouse()

	Method DoMoveMouse(x:Int, y:Int)
		'This correct scales the coords if a virtual resolution is being used.
		If UsingVirtualResolution = 1
			'This is untested if Full Screen is bigger than windowed e.g. 1024x768 fullscreen and only 800x600 in windowed due to Desktop res of 1024x768.
			MoveMouse x / VirtualRatioX, y / VirtualRatioY						
		Else
			...
		End If
	End Method


Also modified the EVENT_MOUSE_MOVE similar to your code but with ccRound on there

			Case EVENT_MOUSEMOVE
				'If the virutal resolution doesn't match the screen size in full screen or windowed mode, we need to scale the coordinates to compensate.				
				If UsingVirtualResolution Then
					Mouse.X = ccRound(EventX() * VirtualRatioX)
					Mouse.Y = ccRound(EventY() * VirtualRatioY)
				Else
					Mouse.X = EventX()
					Mouse.Y = EventY()
				EndIf


Also tweaked TMousePointer.UpdateCoords here:

If pt <> Null Then
   ...
   x = pt.X-Game.ClientX
   y = pt.Y-Game.ClientY

'If the virtual resolution doesn't match the screen size, we need to scale the coordinates to compensate.
   If Game.UsingVirtualResolution Then
	x = ccRound(x * Game.VirtualRatioX)
	y = ccRound(y * Game.VirtualRatioY)
   EndIf
EndIf


Oh one last thing, change ccFixMacWindowY() to use WindowHeight not ScreenHeight.

All that stuff took a lot of figuring out and testing but it worked well in the end and I shipped Spring Bonus with it.

Thanks for being careful to only post modifications and not original code, like I've been. BFG own the original as I'm sure you know, so I can't post that code, but mods should be fine.

Last edited 2011

Last edited 2011


therevills(Posted 2011) [#6]
Thanks Jake :)

I'll test this tonight...

I notice that the minimum specs for Spring Bonus is still DirectX7, but you have updated your framework to DX9. Does this mean SB doesnt run on a machine with DX7?

[edit]
Jake replied via email to me:

If it detects no DX9 it will use DX7 but won't use SetVirtualRes.

So if a PC only has DX7 it will display at your screenwidth/height and will not use the virtual resolution.

Thanks a lot Jake :)

Last edited 2011


therevills(Posted 2011) [#7]
I've just changed DoSetVirtualResolution to this:

	Method DoSetVirtualResolution()
		If DriverDX9 Or Driver = 1
			If ScreenWidth <> WindowWidth Then
				SetVirtualResolution(ScreenWidth, ScreenHeight)
				VirtualRatioX = ScreenWidth / Float(WindowWidth)
				VirtualRatioY = ScreenHeight / Float(WindowHeight)
				SetViewport 0, 0, ScreenWidth, ScreenHeight
				UsingVirtualResolution = 1
			Else
				UsingVirtualResolution = 0
			End If
		Else
			UsingVirtualResolution = 0
		EndIf
	End Method


So if you give the user the option to switch between graphics drivers it will turn on or off the virtual resolution if needed.


Grey Alien(Posted 2011) [#8]
Ah I see. Well I'm making it automatic that it'll drop to DX7 but if they have 9 it'll use that, so no choice :-)


therevills(Posted 2011) [#9]
Its more the choice between OpenGL and DirectX ;)

So if they have DirectX7 installed they can't use the virtual resolution, but if you switch to OpenGL it will use it... and the new code allows the user to then switch back to DirectX7 without issues (as the variable is now set back to 0 so it doesnt mess up with the mouse coordinates etc).


Grey Alien(Posted 2011) [#10]
Yes it's good. It's just that I don't allow sriver swapping in my casual games, but I probably would if I shipping an indie style game.


therevills(Posted 2012) [#11]
Any idea why switching between window mode and full screen causes a 50% drop in the FPS rate?

Using DirectX9 and in window mode I get 60FPS, as soon as I go into Fullscreen mode (without the virtual res) it drops to 30FPS!!!

When using OpenGL it is fine, 60FPS in fullscreen and 60FPS in window mode...

[edit]
Found it... the slow down is caused by ccFixLagDX9... :/

The PC specs which slowed down in Full screen mode and DX9:
CPU: Intel Core 2 Duo E8400 3GHz
RAM: 2GB
GPU: NVIDIA Quadro NVS 290 (driver 275.36)
OS: Windows 7 Enterprise (32bit)

So the question is: to lag the mouse or to lag the game?!

Last edited 2012

Last edited 2012


Grey Alien(Posted 2012) [#12]
Wow really, so it's missing a whole frame on that system if it's dropping to 30FPS. Is that GPU any good? Sounds like going back to DX7 is the solution sigh. But then no virtual res.

Last edited 2012


therevills(Posted 2012) [#13]
I'm investigating how to add hardware mouse cursors to Blitzmax, as thru my research I believe this is the "correct" fix to the mouse lag issue in DirectX games.

No promises though ;)

No virtual res = no game >800x600...

For a short term workaround, maybe a simple IF statement to check the performance is < 50(?) FPS then disable the ccLagFix....

Last edited 2012


therevills(Posted 2012) [#14]
I've found some code on the Unity forum which can load a .cur file and I've added it to Blitzmax... the only issue I've got now is that my image is 64x64 but when I use it, it is resized to 32x32 :/

http://forum.unity3d.com/threads/70163-Hardware-Cursor-Plugin

Heres the c++ code:

extern "C" __declspec( dllexport ) void InitializeCursor(int cursorId, char* standardPath)
{
	wchar_t newPath[1024];
	MultiByteToWideChar(CP_UTF8, 0, standardPath, -1, newPath, 1024);
	HCURSOR cursor = ::LoadCursorFromFileW(newPath);
  //HCURSOR cursor = LoadCursor(NULL, IDC_WAIT); 
	cursors[cursorId] = cursor;
}

extern "C" __declspec( dllexport ) void SetCurrentCursor(int cursorId)
{
	HWND result = GetForegroundWindow();
	SetClassLongA(result, -12, (long)cursors[cursorId]);
	SetCursor(cursors[cursorId]);
	InvalidateRect(result, NULL, TRUE);
}


And the BlitzMax:
?win32
Extern
	Function InitializeCursor(cursorId:Int, path$z)
	Function SetCurrentCursor(cursorId:Int)
EndExtern
?

Function ccInitializeCursor(id:Int, path:String)
	?win32
		InitializeCursor(id, path)
	?
End Function

Function ccSetCurrentCursor(id:Int)
	?win32
		SetCurrentCursor(id)
	?
End Function


And to use:

	ccInitializeCursor(0, "pointer.cur")
	ccSetCurrentCursor(0)


Currently I've got my pointer.cur in the same folder as the exe.

[edit]
According to this Microsoft page, the system imposes a standard size of 32x32 and will only use 64x64 if the DPI is too high...

http://support.microsoft.com/kb/307213
[/edit]

Last edited 2012


Grey Alien(Posted 2012) [#15]
Very interesting. Don't forget that the lag is a *display lag* so keys also appear to be slow as well, not just the mouse. Not a problem for casual games, but definitely for arcade games. In fact I first discovered this lag making a platformer minigame that used keys.

Last edited 2012


therevills(Posted 2012) [#16]
Ah yes - I did forget about the lag keys/joysticks too...

Using a hardware mouse cursor does totally get rid of the lag with the mouse though ;)


Grey Alien(Posted 2012) [#17]
Good to know thanks.


Grey Alien(Posted 2012) [#18]
Oh and btw, for Spring Bonus what I did was add the lagfix code and had an option to turn off the custom cursor and just display the windows cursor instead as a lot of people on BFG want that option, and that seemed fine. I didn't get any reports of horrid 30FPS issues, but maybe they didn't notice!

If you use a custom hardware cursor it just occurred to me that any clicks you make or "drawing"/dragging you did with that cursor would seem to lag behind due to the display lag. For example if you pick up objects that stick to the cursor and are dropped elsewhere, they would appear to lag behind the cursor.


therevills(Posted 2012) [#19]
for Spring Bonus what I did was add the lagfix code


Ah cool... I was going to ask if you added the lagfix to Spring Bonus :)

you did with that cursor would seem to lag behind due to the display lag


Yep that is true... it seems that there really isnt a good solution apart from turning vsync off :(


therevills(Posted 2012) [#20]
To bring this back on topic, I've added Widescreen support to GAF - thanks to James' code:

http://www.blitzbasic.com/codearcs/codearcs.php?code=2879#comments

In CommonTypes.bmx

Declare the main variable "WideScreen"
Type TGame
...
	'Screen and timing vars
	Field FullScreen:Int = 1
	Field WideScreen:Int = 0
...


Add it to the ini file:

	'--------------------
	'Ini
	'--------------------
	Method IniFill()
...
		Ini.Add("WideScreen", WideScreen)
	End Method


More ini file stuff

	Method IniExtract()
		'Extract the Game variables from the Ini object
...
				Case "WIDESCREEN"
					WideScreen = Int(Line.Value)
			End Select
...



And the guts of the Virtual resolution stuff with added Widescreen support:

	Method DoSetVirtualResolution()
		If DriverDX9 Or Driver = 1
		
			' Reset of display needed when re-calculating virtual graphics stuff/clearing borders...

			SetVirtualResolution GraphicsWidth (), GraphicsHeight ()
			SetViewport 0, 0, GraphicsWidth (), GraphicsHeight ()
			SetOrigin 0, 0
			
			' Got to clear both front AND back buffers or it flickers if new display area is smaller...
			Cls
			Flip
	
			Cls
			Flip
			
			Cls
			Flip
			
			Cls
			Flip
						
			If FullScreen And WideScreen
		
				Local gwidth:Int
				Local gheight:Int
	
				gwidth = DesktopWidth
				gheight = DesktopHeight
				
				Local virtualratio:Float = Float(ScreenWidth) / Float(ScreenHeight)
				Local graphicsratio:Float = Float (gwidth) / Float (gheight)
	
				
				Local gtovratio:Float = graphicsratio / virtualratio
				Local vtogratio:Float = virtualratio / graphicsratio
				
				If graphicsratio >= virtualratio
					Local vscale:Float = Float (gheight) / Float(ScreenHeight)
					Local pixels:Float = Float(ScreenWidth) / (1.0 / vscale)
					Local half_scale:Float = (1.0 / vscale) / 2.0
	
					Local sx:Float = Float(ScreenWidth) * gtovratio
					
					SetVirtualResolution sx, ScreenHeight
					
					vxoff = (gwidth - pixels) * half_scale
					vyoff = 0
	
					SetViewport vxoff, vyoff, ScreenWidth, ScreenHeight
					SetOrigin vxoff, vyoff
					VirtualRatioX = sx / Float(WindowWidth)
					VirtualRatioY = ScreenHeight / Float(WindowHeight)
					UsingVirtualResolution = 1
				Else
					Local vscale:Float = Float (gwidth) / Float(ScreenWidth)
					Local pixels:Float = Float(ScreenHeight) / (1.0 / vscale)
					Local half_scale:Float = (1.0 / vscale) / 2.0
					
					Local sy:Float = Float(ScreenHeight) * vtogratio
					
					SetVirtualResolution ScreenWidth, sy
					vxoff = 0
					vyoff = (gheight - pixels) * half_scale
	
					SetViewport vxoff, vyoff, ScreenWidth, ScreenHeight
					SetOrigin vxoff, vyoff
					VirtualRatioX = ScreenWidth / Float(WindowWidth)
					VirtualRatioY = sy / Float(WindowHeight)
					UsingVirtualResolution = 1
				EndIf
			Else
				If ScreenWidth <> WindowWidth Then
					SetVirtualResolution(ScreenWidth, ScreenHeight)
					VirtualRatioX = ScreenWidth / Float(WindowWidth)
					VirtualRatioY = ScreenHeight / Float(WindowHeight)
					SetViewport 0, 0, ScreenWidth, ScreenHeight
					UsingVirtualResolution = 1
				Else
					If Not WideScreen Then UsingVirtualResolution = 0
				End If
			EndIf
		Else
			UsingVirtualResolution = 0
		EndIf
	End Method


And a helper toggle method:

	Method ToggleWideScreen()
		If Self.WideScreen
			WideScreen = 0
			vxoff = 0
			vyoff = 0
			UsingVirtualResolution = 0
		Else
			WideScreen = 1
		End If
		Mouse.ClearButtons() 'for safety in case a button was used to togglefullscreen
		If Game.FullScreen Then DoSetVirtualResolution()
		RestoreMouseCoords()
		IniUpdate()
		FixedRateLogic.Init() 'avoids skips due to old variable values
		FixedRateLogic.HistoryClear()
	End Method


Done :)

Last edited 2012


Grey Alien(Posted 2012) [#21]
Wow that is coolness! Thanks for sharing.

So how are you using this yourself? Are you making a widescreen game (16:9) and using the system to add bars above and below on 4:3 screens (and small ones on 16:10 screens and any other sizes)? And how are you deciding on the resolution to make the window/fullscreen? Some people decided to make full screen the same as the desktop but other said it slowed their game down a lot.

When I looked into making GAF widescreen a while ago there were lots of differing opinions as to how to handle it and I didn't need it myself at the time so didn't add it it. If anything, for casual games, it's just needed for giving the user an option to add bars to the side for 4:3 games shown on a widescreen monitor so the game is the correct aspect ratio, not stretched (as some monitors do and others don't, and some allow you to toggle).


Grey Alien(Posted 2012) [#22]
couple of code questions:

what is this for? If Not WideScreen Then UsingVirtualResolution = 0
and how come you only call DoSetVirtualResolution() for FullScreen mode in the toggle function?

Sorry it's late and I haven't tested myself yet so it's not obvious :-)


therevills(Posted 2012) [#23]
So how are you using this yourself?


I'm coding at 1024x768 so 4:3 and I've added an option to the option screen so the user can switch between the correct ratio of the game (by turning on widescreen) or having the game stretched across the screen.

If Not WideScreen Then UsingVirtualResolution = 0


Just to ensure that the UsingVirtualResolution boolean is turned off.

how come you only call DoSetVirtualResolution() for FullScreen mode in the toggle function?


As I've only coded the Virtual resolution stuff to work in full screen and the user can enable widescreen mode if the game is in window mode.


zeb(Posted 2012) [#24]
Hi therevills!

Thank you for the improvements! I will try it surely.
One question please:
How do you added the DX9 in the framework? I made an experiment a time ago, with only dx9, seems it works well, but i need to change the blitzmax source code, adding a method in the dx module ( it's only one line... ) but I don't like touch the blitzmax code...
Regards


BlitzSupport(Posted 2012) [#25]
@therevills: One thing I notice in your code there:

Local gwidth:Int
' ...
gwidth = DesktopWidth


DesktopWidth () is a valid BMX function, so running just the two lines above will fail (DesktopWidth in this case is a function pointer).

I therefore assume you've got a global Int called DesktopWidth somewhere that's wiping out the BMX function, as this compiles:

Local DesktopWidth:Int = 10
Local gwidth:Int
gwidth = DesktopWidth


Thought I ought to point it out anyway.


Grey Alien(Posted 2012) [#26]
@therevills: Ah yes that makes sense thanks, now I read it in the morning :-) This code could be used to write a widescreen HOG or something though right?

@BlitzSupport: Yes DesktopWidth is a global that is set elsewhere in teh framework.

Last edited 2012


therevills(Posted 2012) [#27]
This code could be used to write a widescreen HOG or something though right?


Yep! James did a brilliant job with this :)

How do you added the DX9 in the framework?


Opps.. yeah thats really needed on this post too...

In CommonTypes.bmx:

Type TGame
...
	Field DriverDX9:Int = 0


	Method GraphicsCreate() 
...
		'Set DirectX or BlitzMax OpenGL driver (not pure OpenGL)
		Local DriverOK%=0
		?Win32
		If Driver=0 Then
			'DirectX
			DebugInt = 101
			'Is there a Direct3D 7 Driver installed?
			If D3D9Max2DDriver() Then
				DebugInt=10101
				'The code can reach here even if the driver is not DirectX7 or higher I've discovered.
				If Not ccDirectXVersionIsValid() Then
					'Make sure that ccGraphicalErrors=0 otherwise this error won't show!
					ccRunTimeError("Sorry, you need DirectX V7.0 or higher!")
				Else
					DirectXVersion = ccGetDirectXVersion()
				EndIf
				DebugInt=102
				SetGraphicsDriver D3D9Max2DDriver()
				DriverOK = 1
				DriverDX9 = 1
			ElseIf D3D7Max2DDriver() Then
...
		Else
			'OpenGL
			DebugInt=106
			'Is there an OpenGL Driver installed?
			If GLMax2DDriver() Then			
				DebugInt=107
				SetGraphicsDriver GLMax2DDriver()
				DriverOK = 1
			ElseIf D3D9Max2DDriver() Then
				DebugInt = 10101
				SetGraphicsDriver D3D9Max2DDriver()
				DriverOK = 1
				Driver = 0
				DebugInt = 110
				IniUpdate()
				DriverDX9 = 1
			Else
				'Hmm, OK, OpenGL not present, is DirectX available?
				DebugInt=108
				If D3D7Max2DDriver() Then
...
			?Win32
				'Only call this in DirectX mode
				'This command fails if DirectX is not installed
				If D3D7Max2DDriver() Or D3D9Max2DDriver() Then
					DebugInt=113
					DesktopRefreshRate = GetDeviceCaps(GetDC(0),VREFRESH)
				EndIf
			?
...
		'Get GameRefreshRate, may not be same as DesktopRefreshRate in full-screen mode
		GameRefreshRate = -1 'means value not read
		DebugInt=136
		?Win32
			'This command fails if DirectX is not installed
			If D3D7Max2DDriver() Or D3D9Max2DDriver() Then
				DebugInt=137
				GameRefreshRate = GetDeviceCaps(GetDC(GetActiveWindowSafe()), VREFRESH)
			EndIf
		?


	Method GetActiveWindowSafe%()
...
                    If Driver = 0 Then
			Local my_driver:TD3D7GraphicsDriver = D3D7GraphicsDriver()
			Local my_graphics:TD3D7Graphics = my_driver.Graphics()
			'If the my_graphics is null, use DX 9 driver instead.
			If my_graphics <> Null Then
				Return my_graphics._hwnd
			Else
				Local my_driver:TD3D9GraphicsDriver = D3D9GraphicsDriver()
			
				Local my_graphics:TD3D9Graphics = my_driver.Graphics()
			    Return my_graphics._hwnd
			EndIf


I think thats it... Oh and I did have to add this:

Method Graphics:TD3D9Graphics()
        Return _graphics
    End Method


To dxgraphics.mod\d3d9graphics.bmx.... good luck ;)

Last edited 2012


Grey Alien(Posted 2012) [#28]
Small bug. This line near the bottom:

			If D3D7Max2DDriver() Or D3D7Max2DDriver() Then


should be:

			If D3D7Max2DDriver() Or D3D9Max2DDriver() Then



therevills(Posted 2012) [#29]
Opps - Thanks Jake ;)

I've updated the above code :)

Last edited 2012


Grey Alien(Posted 2012) [#30]
Also instead of this:

SetGraphicsDriver D3D9Max2DDriver()
DriverDX9 = 1



I call this:

	Method SetDXDriver(DX7:Int)
		?Win32
		If DX7 Then
			DriverDX7 = 1
			DriverDX9 = 0
			SetGraphicsDriver D3D7Max2DDriver()
		Else
			DriverDX7 = 0
			DriverDX9 = 1
			SetGraphicsDriver D3D9Max2DDriver()
			'Ideally there would be some code here to check if DX9 wasn't used and DX7 was so the DriverDX7 flag could be set.
			'Oh wait, there is now!
			If Not D3D9Max2DDriver()
				DriverDX7 = 1
				DriverDX9 = 0
  				SetGraphicsDriver(D3D7Max2DDriver())
				'This may mean that DoSetVirtualResolution() fails due to DX7, but the small netbooks it required on are all DX9 anyway.
			EndIf
		EndIf
		?
	End Method


Last edited 2012


Grey Alien(Posted 2012) [#31]
No, thank you, because I updated my code with some of yours (like I wasn't testing for DX9 driver in a few places.)

Last edited 2012


Grey Alien(Posted 2012) [#32]
You also might want to update DebugPrint a bit, I found this useful for testing:

			'Drivers and memory
			If Driver = 0 Then
				Local using:String = " (using DX"
				If DriverDX7 Then using = using + "7)"
				If DriverDX9 Then using = using + "9)"
				C.OutputNL("Gfx Drv " + "DirectX " + DirectXVersion + using)
			Else
				C.OutputNL("Gfx Drv " + "OpenGL")
			EndIf



therevills(Posted 2012) [#33]
Yep I've got something like that too... but it was getting a bit of a mess posting all that code...

			If Driver = 0 Then
				If DriverDX9 Then C.OutputNL("Using DirectX9")
				C.OutputNL("Gfx Drv " + "DirectX " + DirectXVersion)
			Else
				C.OutputNL("Gfx Drv " + "OpenGL")
			EndIf
			If WideScreen = 1 Then
				C.OutputNL("Using Widecreen")
			Else
				C.OutputNL("NO widescreen")
			EndIf
			C.OutputNL("DesktopWidth  " + DesktopWidth)
			C.OutputNL("DesktopHeight " + DesktopHeight)
			C.OutputNL("ScreenWidth   " + ScreenWidth)
			C.OutputNL("ScreenHeight  " + ScreenHeight)
			C.OutputNL("WindowWidth   " + WindowWidth)
			C.OutputNL("WindowHeight  " + WindowHeight)
			If UsingVirtualResolution Then
				 C.OutputNL("UsingVirtualResolution = True")
			Else
				C.OutputNL("UsingVirtualResolution = False")
			End If
...
			C.OutputNL("Mouse Lag Fix " + lagFixOn + " F1 to toggle")


Last edited 2012


Grey Alien(Posted 2012) [#34]
Nice, I've just added those virtual res related ones. Still haven't added hardware cursor or widescreen support yet, it's on my to do list (have bookmarked this thread). Working on another main project at the moment for someone else with a team and need to crunch.

Last edited 2012


therevills(Posted 2012) [#35]
Cool!

BTW I've just tested Spring Bonus on this work PC and I can see the slow down with the lagfix in full screen mode, its playable but I can tell its not running at 60FPS.


Grey Alien(Posted 2012) [#36]
OUCH. Well worth knowing thanks. That PC isn't a bad spec right (I didn't know if the video card was good/bad)? So it's like I could leave off lagfix and maybe there'd be a small lag but at least the game would be 60FPS smooth and still offer the ability to switch off the custom cursor for people who hate the lag. I wonder how many sales I lost due to lag appearing bad? It actually converted the best of all my match-3s on BFG and I didn't see any complaints.

I have heard a few people on BFG moaning that custom cursors (in other games) are REALLY slow, like awful, and BFG QA confirmed this, so not just a few frames lag, like tons. Could be a DX9 issue where the buffering is extreme or something. That's why I added the option in to not use a custom cursor at all. Could retro fit it to my old games if I could be arsed :-)

Last edited 2012


Grey Alien(Posted 2012) [#37]
Hey therevills, I added Widescreen to Spring Bonus using the code above and it works well thanks. Couple of weird things I noticed though:

- The mouse goes off the right hand side of the screen into the black border. Did you limit the mouse X coord in your framework somehow (I noticed it doesn't go to far right in your latest solitaire game)?
- Something weird is going on with the left hand side of the screen. If I move the mouse cursor over there its tip is able to draw in a 2 pixel column on the black border to the left of the main background and also any dialogs that slide on/off leave a trace in that thin column.

In your solitaire game your mouse cursor doesn't quite reach the left side (mouse offset?) and so you can't see the same effect, BUT I think there is a thin strip down the left side that is not the right colour when you go from title to options for example. Check it out.


Grey Alien(Posted 2012) [#38]
OK found the problem with the left side. It's a rounding error on vxoff, vyoff because SetViewPort uses ints and SetOrigin uses floats.

So try this:

SetViewport ccRound(vxoff), ccRound(vyoff), ScreenWidth, ScreenHeight
SetOrigin ccRound(vxoff), ccRound(vyoff)

It makes them consistent.

Last edited 2012

Last edited 2012


Grey Alien(Posted 2012) [#39]
One more thing. I notice you are resetting vxoff and vyoff to zero in ToggleWidescreen. Why is that? If they are not used anywhere else in the code except DoSetVirtualResolution() they might as well be local to DoSetVirtualResolution() which is what I've done (declared as floats)

Last edited 2012


Grey Alien(Posted 2012) [#40]
Ah crap, turns out that fix only worked on the PC that had 1600x900 resolution (for my 1024x768 game). On my 1920x1200 PC the problem still exists on the left border. I expect I could fix it by drawing over it every frame but that seems a bit lame. Probably the solution is some careful rounding of some of the other variables. I already tried making them all Double but that didn't work.

[edit]Hmm, after some more experimentation I'm thinking the solution may be to Floor sx and sy like this as well as keeping the rounding from my previous post, but I'd love a second opinion.

SetVirtualResolution Floor(sx), ScreenHeight



...


Having some more thoughts now, I wonder if sx/sy should always be rounded to an EVEN number such that sx - ccRound(vxoff)*2 = ScreenWidth.

Last edited 2012


therevills(Posted 2012) [#41]
Its been quite awhile since I looked at this...

I notice you are resetting vxoff and vyoff to zero in ToggleWidescreen. Why is that? If they are not used anywhere else in the code except DoSetVirtualResolution()


Looks like I now use them in DoMoveMouse:

	Method DoMoveMouse(x:Int, y:Int)
		'This correct scales the coords if a virtual resolution is being used.
		If UsingVirtualResolution = 1
			'This is untested if Full Screen is bigger than windowed e.g. 1024x768 fullscreen and only 800x600 in windowed due to Desktop res of 1024x768.
			MoveMouse ((x + vxoff) / VirtualRatioX) , ((y + vyoff) / VirtualRatioY)
		Else
			MoveMouse(x, y)
		End If
	End Method


And UpdateCoords:

	Method UpdateCoords()
		'Uses Windows API call to get exact coords relative to desktop.
		'Doesn't use MouseX() and MouseY() as they rely on PolledInput.
		'Also doesn't use event-based updates because they would only update X/Y
		'if a MouseMove event was received since PollEvent() was last called (unlikely).
		Local pt:TPoint
		?Win32
			pt = ccGetCursorPos() 'This is position relative to the top left of the desktop
		?
		?MacOS
			pt = ccGetCursorPosOSX()
			'On Macs the mouse Y coord is reversed (0 is at the bottom of the screen)
			'So I need to reverse it based on the Desktop Height.
			pt.Y = Game.DesktopHeight-pt.Y
		?
		If pt <> Null Then
			'Use client area position so we can see if the cursor is outside of window.
			Game.SetClientCoords() 'Need this in case window has been dragged as drag code doesn't alway set it soon enough.
			
			x = pt.x - Game.ClientX
			y = pt.y - Game.ClientY
			
			If UsingVirtualResolution Then
				x = ccRound(x * VirtualRatioX) - Game.vxoff
				y = ccRound(y * VirtualRatioY) - Game.vyoff
			EndIf
			
		EndIf
	End Method


And DoPeekEvent:
			Case EVENT_MOUSEMOVE
			
				If UsingVirtualResolution Then
					Mouse.X = ccRound(EventX() * VirtualRatioX) - vxoff
					Mouse.y = ccRound(EventY() * VirtualRatioY) - vyoff
				Else
					Mouse.X = EventX()
					Mouse.Y = EventY()			
				End If


Also just to let you know I had to actually disable the Widescreen support for Mac, on my Mac it worked fine but BFG found on some it didnt display correctly. Looking at the screen shot they sent me, I think the viewport doesnt seem to be "cutting" correctly or something:



And since I couldnt reproduce it I just disabled that option for Mac within the game.

Last edited 2012


Grey Alien(Posted 2012) [#42]
Thanks for the info. Hmm guess I'd better disable it on Mac too. I've never heard about viewports failing on Mac before. Wonder if it's videocard specific?

So are you clamping the mouse on the right hand side of the screen somehow as I notice yours doesn't go into the black border on the right (it does on mine)? EDIT: OK I just added vxoff and vyoff to my other code and now the mouse goes off both the left and the right by equal amounts (instead of only off the right). Weird.

Also what are your thoughts about avoiding the thin column on the left that seems to get drawn on when it shouldn't? I've tried a bunch of things and think I have a solution. Will post code later.

Last edited 2012


Grey Alien(Posted 2012) [#43]
OK here's what I did in the end. I discovered that if sx-vxoff*2 = ScreenWidth, sometimes you can't see the edge pixels on the right hand side. So I've deliberately shrunk it down a bit more to the nearest even number which seems to work.

					Local sx:Float = Float(ScreenWidth) * gtovratio

					'Round sx down to the nearest even number that will look a bit smaller than the ScreenWidth required.
					Local finalsx:Int = Floor(sx-1)
					If finalsx Mod 2 = 1 Then finalsx:-1

					SetVirtualResolution finalsx, ScreenHeight
										
					vxoff = (gwidth - pixels) * half_scale
					vyoff = 0

					Print sx
					Print finalsx
					Print vxoff
					Print finalsx-ccRound(vxoff)*2
	
					SetViewport ccRound(vxoff), vyoff, ScreenWidth, ScreenHeight
					SetOrigin ccRound(vxoff), vyoff


Need to do the same for sy and vyoff

Last edited 2012


Grey Alien(Posted 2012) [#44]
btw, I did some testing and I don't think you need vxoff and vyoff in UpdateCoords() because it is only called in windowed mode and of course widescreen is not turned on in windowed mode so vxoff and xyoff =0. Mind you it doesn't harm to have it in there.


Grey Alien(Posted 2012) [#45]
Here's what I did in the end to clamp the mouse in DoPeekEvent:

...
					Mouse.X = ccRound(EventX() * VirtualRatioX) - vxoff
					Mouse.Y = ccRound(EventY() * VirtualRatioY) - vyoff

					'It's possible for the mouse to go offscreen in widescreen mode, so we need to do some clamping.
					If FullScreen And Widescreen Then
						If Mouse.X<0 Then
							Mouse.X=0
							DoMoveMouse(Mouse.X,Mouse.Y)
						EndIf
						If Mouse.Y<0 Then
							Mouse.Y=0
							DoMoveMouse(Mouse.X,Mouse.Y)
						EndIf
						If Mouse.X>=ScreenWidth Then
							Mouse.X=ScreenWidth-1 'May cause a slight jiggle on the right side.
							DoMoveMouse(Mouse.X,Mouse.Y)
						EndIf
						If Mouse.Y>=ScreenHeight Then
							Mouse.Y=ScreenHeight-1
							DoMoveMouse(Mouse.X,Mouse.Y)
						EndIf
					EndIf	
...				



therevills(Posted 2012) [#46]
Yep, Ive got similar code in DoPeekEvent to handle the mouse:

			Case EVENT_MOUSEMOVE
			
				If UsingVirtualResolution Then
					Mouse.X = ccRound(EventX() * VirtualRatioX) - vxoff
					Mouse.y = ccRound(EventY() * VirtualRatioY) - vyoff
				Else
					Mouse.X = EventX()
					Mouse.Y = EventY()			
				End If
				
				' this stops the custom cursor going offscreen when using the virtual res...
				If FullScreen
					If Self.Mouse.useSystemPointer = 0
						If Mouse.x < 0
							Mouse.x = 1 ' 1 instead of 0, because it would hang!?
							DoMoveMouse(1, Mouse.y)
						End If
		
						If Mouse.x > ScreenWidth
							Mouse.x = ScreenWidth - 1
							DoMoveMouse(ScreenWidth - 1, Mouse.y)
						End If
		
						If Mouse.y < 0
							Mouse.y = 1
							DoMoveMouse(Mouse.x, 1)
						End If
		
						If Mouse.y > ScreenHeight
							Mouse.y = ScreenHeight - 1
							DoMoveMouse(Mouse.x, ScreenHeight - 1)
						End If
					EndIf
				EndIf



Grey Alien(Posted 2012) [#47]
Ha! almost the same :-)


Grey Alien(Posted 2012) [#48]
Found a couple of bugs in my implementation that may affect you:

1) the global vxoff and vyoff must be set to 0 at the start of DoSetVirtualResolution() just in case they were set previously (due to full screen widescreen mode being used) and then you go back to windowed mode in which widescreen isn't being used. If you don't do that you get some bad mouse sync issues.

2) In ToggleWideScreen() you should ALWAYS call DoSetVirtualResolution(), not just if the game is in fullscreen. This is because if you are in windowed mode and it is scaled down on a low resolution laptop, and you toggle widescreen on/off, when it goes off it will turn off UsingVirtualResolution in your code sample and never turns it back on again (via DoSetVirtualResolution) which is bad news because it should be on. This causes the mouse to go out of sync.

Hope those make sense.


Grey Alien(Posted 2012) [#49]
@therevills: Figured out what was causing the mouse repositioning to rehang in rare cases when using widescreen. Here's my code:

					'It's possible for the mouse to go offscreen in widescreen mode, so we need to do some clamping.
					'But *only* for the custom cursor as it looks bad with the windows pointer (it springs back into position)
					If FullScreen And Widescreen And CustomMousePointer Then

						Local FloatX:Float = (EventX() * VirtualRatioX) - vxoff
						Local FloatY:Float = (EventY() * VirtualRatioY) - vyoff

						'Calc edge coords using virtual ratio, otherwise the cursor can sometimes get stuck with the following code.
						Local LeftEdge:Int = 0
						Local TopEdge:Int =  0
						Local RightEdge:Int = ScreenWidth-1
						Local BottomEdge:Int =  ScreenHeight-1

						'Need to use Ceil and Floor to stop mouse getting stuck due to infinitely repositioning due to being off by a fraction of a pixel.
						'This may result in the cursor jiggling at some edges in some resolutions.
						If Ceil(FloatX)<LeftEdge Then
							Mouse.X=LeftEdge
							DoMoveMouse(Mouse.X,Mouse.Y)
						EndIf
						If Ceil(FloatY)<TopEdge Then
							Mouse.Y=TopEdge
							DoMoveMouse(Mouse.X,Mouse.Y)
						EndIf
						If Floor(FloatX)>RightEdge Then
							Mouse.X=RightEdge
							DoMoveMouse(Mouse.X,Mouse.Y)
						EndIf
						If Floor(FloatY)>BottomEdge Then
							Mouse.Y=BottomEdge
							DoMoveMouse(Mouse.X,Mouse.Y)
						EndIf
					EndIf					


There's probably a better way of solving this (without the possible 1 pixel jiggle) but it's been driving me crazy trying to figure it out, so for now this will do.

Last edited 2012


Grey Alien(Posted 2012) [#50]
Also I was still getting issues with a column of pixels not being cleared on the side so I updated the code to have a slightly smaller viewport and that has sorted it (I hope):

				If graphicsratio >= virtualratio

					'Desktop is wider than game screen size OR the same
					Local vscale:Float = Float (gheight) / Float(ScreenHeight) 'How many desktop pixels are used per virtual pixel. e.g. 2.0 for an 800x600 game screen on a 1920x1200 desktop
					Local pixels:Float = Float(ScreenWidth) / (1.0 / vscale) 'Width in desktop pixels based on game screen scaled up so height matches desktop height.
					Local half_scale:Float = (1.0 / vscale) / 2.0 'This is half of how many virtual pixels are represented by desktop pixels. e.g. 0.25 for an 800x600 game screen on a 1920x1200 desktop

					'Work out many pixels wide (at current resolution set by Graphics()) we have to pretend the screen is					
					'so that the middle part that we want to draw to will be squashed to the correct aspect ratio.
					Local sx:Float = Float(ScreenWidth) * gtovratio
										
					'Round sx down to the nearest even number that will look a bit smaller than the ScreenWidth required.
					Local finalsx:Int = Floor(sx-1)
					If finalsx Mod 2 = 1 Then finalsx:-1

					SetVirtualResolution sx, ScreenHeight

					'Calc the leftmost pixel to start drawing at
'					vxoff = (gwidth - pixels) * half_scale
					vxoff = (finalsx-ScreenWidth)/2 'More accurate that method on line above.
					vyoff = 0
	
					'Make the viewport a little bit smaller than the virtual resolution to avoid a column of pixels on the edge which is not cleared.
					SetViewport ccRound(vxoff)+1, vyoff, ScreenWidth-2, ScreenHeight
					SetOrigin ccRound(vxoff), vyoff
										
					VirtualRatioX = sx / Float(WindowWidth)
					VirtualRatioY = ScreenHeight / Float(WindowHeight)
					UsingVirtualResolution = 1
				Else
...


Last edited 2012