DirectX 9 Driver (*Updated*)

BlitzMax Forums/BlitzMax Programming/DirectX 9 Driver (*Updated*)

DStastny(Posted 2009) [#1]
New version Uploaded.

This version address two things.

1. Centering of Window Mode displays like the Dx7/Open GL Drivers like 1.32 Max drivers
2. Render Buffering(Input Lag)

I have included a demo application provided by GFK(thanks and hope you don't mind my including for others to test) that demonstrated the problem. I have chosen a method that should minimize performance impacts. The method as a result of a massive Google hunt and came down to what was recommend by both ATI and NVidia to address the issue.

The mechanims uses a Direct3D Device Query and I think(*hope*) should work on all hardware. If not there is some additonal code in there that uses the DX7 driver method and if need be we can determine when to enable that code(big performance hit, but if its all the hardware can do...)

This will slow down max FPS(6/7%) as we are preventing render buffering,, which is really a GPU/CPU concurrency issue, not so much buffering which is why it is so difficult to track down.

I have implemented a driver option that allows disabling of the functionality all together.(See the readme or samples, if it really is big issue)


At this time there are 2 items I think outstanding that I would like to address before callng this a 1.0 although as of right now I think on modern hardware this is pretty much as good as it can get.

1. MIPMAPPING - currently i utilize hardware mipmap generation, I suspect there are still some crappy cards out there that dont support this. I am just lazy to write the code to generate MIPMAPs

2. VYSYNC - tearing here too I think there are issues with older hardware. I am also not to happy with the current implemention. The issue here really is change on part of direct X around Direct X 8 in that VSYNC is not option of present(BMAX Flip) but option on creation of the device.(BMAX Graphics) To make this BlitzMax like I emlulate VSYNC on the BMAX Flip command.

Frankly I dont think this works well. I think on final release, I am going to make it option on the driver and ignore the Flip Flag. What this means is you would set VSYNC on or off before calling the GRAPHICS command.

All feedback is appreciated.

Doug Stastny


GW(Posted 2009) [#2]
Cool,
did you fix the issue with using the dx9 driver causes your app to want to create a socket connection to some mystery address?


GaryV(Posted 2009) [#3]
did you fix the issue with using the dx9 driver causes your app to want to create a socket connection to some mystery address?
That is likely DX itself and has little to do with the driver. There are likely tips somewhere in a thread on how to disable DX phoning home on your system.


DStastny(Posted 2009) [#4]
@GW

GaryV is correct this has nothing to due with my driver. Feel free to examine the source.


Doug


skidracer(Posted 2009) [#5]
GW, type dxdiag from the command prompt and untick the Check for WHQL digital signatures on first page.


GfK(Posted 2009) [#6]
I have included a demo application provided by GFK(thanks and hope you don't mind my including for others to test) that demonstrated the problem.
No probs.

Just tried the new version on my cruddy old test system - no lag whatsoever now. :)


DreamLoader(Posted 2009) [#7]
nice one


Tachyon(Posted 2009) [#8]
The issues I have with this D3D9 driver are following. Note that none of these problems exist with the default OpenGL or D3D7 driver:

1) It doesn't seem to lock to VSYNC properly. On my 60hz LCD, carefully timed (60fps vsync'd) sequences using D3D7 and OGL work as expected, but run too fast under D3D9. For me to use D3D9, vsync needs to work like the default BMAX vsync.

2) I get a visible seam between two "seamless" textures used on a backdrop. Sorry, I can't post a screenshot.

3) On my custom GUI I use black lines, drawn with gradating ALPHA values, to give the appearance of drop shadows on menus and windows. These lines appear solid black under D3D9, not semi-transparent like they are under D3D7/OGL. It's like SetAlpha doesn't work with DrawLine or DrawRect.


DStastny(Posted 2009) [#9]
@Tachyon,

I appreciate the feedback. A CAPs dump of you video card would be helpful.



1) As for the VYSNC, if you read my comments above i intend to change the functionality of it as the technique I use is not reliable across cards known issue on my part. I would be interested to know are you setting up a time rate on the Graphics Command or just using Flip True/False and cards refresh rate. I suspect I cant read the Raster Scan line. Probably unsupported on the card.

2)I have not seen this, but I am assuming you are tiling a Image? Is the image scaled? Might be my concern about mipmapping. If your card cant do hardware mipmapping this might be doing it.

3) Can you give me a snippet on how you gradating the lines. Are you drawing lines in a loop and dropping the alpha intensity. Not sure here, my testing of Alpha blending seem to work. Any other blend modes active.

In any case sorry you can produce sample code as when people produce samples I can address the issues faster.

If I can get your card caps that would be appreciated.

Doug


DStastny(Posted 2009) [#10]
@Tachyon alphablend bug confirmed. Thanks

I cant duplicate the tile issue with a seamless texture using TileImage.
Are you tiling the image yourself with DrawImage?

Fix for Alpha problem.

in Dx9Max2dGraphicsDriver.bmx

this
format_code('
Method SetAlpha( alpha:Float )
_Drawcolor=(Int(255*alpha) Shl 24)|(_Drawcolor&$ffffff)
End Method
')

to this

format_code('
Method SetAlpha( alpha:Float )
_Drawcolor=(Int(255*alpha) Shl 24)|(_Drawcolor&$ffffff)
' Set drawing verts colors
_VerticesColor[VERTD]=_DrawColor
_VerticesColor[VERTD+6]=_DrawColor
_VerticesColor[VERTD+12]=_DrawColor
_VerticesColor[VERTD+18]=_DrawColor
End Method
')

That was a booboo in 0.6 when I switched the way rendering worked.

Still trying to duplicate the seamless texture issue.

Thanks
Doug


Tachyon(Posted 2009) [#11]
@DStastny:

My card is a GeForce GTX 280 (top-of-the-line card just 6 months ago). I always use the latest certified driver from nVidia. I'm sure you can find CAPs info online.

Regarding the seam, after playing with the tile image, it does appear to have something to do with the alpha transparency of the .png file I use. I have made some adjustments to the image and the seam went away. However, it should still be noted that the visible seam was not there under D3D7/OGL.


DStastny(Posted 2009) [#12]
@Tachyon

But based upon the generation it I do know it supports the CAPS i use. What OS are you using and are you seeing the sync issue in Fullscreen or windows mode. If in windows mode do you have any themes ?


As for the image interesting.. So you had alpha channel and were tiling an alpha blended image or solid blend?

As for the seam I believe you. But you have to understand how difficult it is to find a rendering glitch if I cant duplicate the issue.

What are you using to make your PNG. There might be issue in the texture loading although it pretty much uses the same pixmap code that Dx7/OGL uses. Hmmm

Thanks
Doug


Grisu(Posted 2009) [#13]
Thanks for the module!

Can MaxGUI apps under XP or vista benefit from using this driver as well?


DStastny(Posted 2009) [#14]
@Grisu yest you can use the Driver wit MaxGUI. There is a simple dumb example in the samples that shows two canvases in use.

I am working up the next update: which fixes the AlpahBlending bug, i posted fix for above as well as a reworked VSYNC mechanism that seems to be working pretty good.

Doug


Grisu(Posted 2009) [#15]
Great!

The driver somehow "zoomes" the images displayed in a canavas.
See example image here.
Any idea why might cause this?

Apart from that the driver seems faster than the Dx7 one.. :o)


DStastny(Posted 2009) [#16]
@Grisu, That looks like a bug on my part just not sure exactly what.

Is that dialog just a simple image in the picture? Or collection of images? Using simple DrawImage.

I see two canvas, are they both Graphics Canvas? I defiantly see the right size image messed up but it looks like the left side is butchered up pretty bad.

If you have any sample mock up that duplicates it, I should be able to quickly find and isolate the issue. I suspect I am not handling something correctly when the canvases are sized.

I will see if I can make a more complex demo MAXGUI and track it down. I will try to mock up what i see in your picture.


*EDIT* I think I can duplicate it. I have modified my example and definitely is not behaving the same way as DX7. I should be able to figure it out.

Thanks for feedback

Doug


DStastny(Posted 2009) [#17]
@Grisu

Ok, I found it my test program pretty much sucked. Both my canvases where the same size so I could not see the problem. I missed the fact that each canvas had to reset the viewport and matrix with the SetGraphics calls so everything would skew all wonky depending on which view port was created first.

Will be fixed in next released. I need to move some code around in the Max2d driver and clean it up.

Thanks for the picture and report!

Doug


Grisu(Posted 2009) [#18]
You're welcome.

Ouch, you were faster than me posting some example code... ;(

Will retest the module when you're done.


SLotman(Posted 2009) [#19]
Just found a bug, that's really a show stopper:



Same image, same code, the top one on dx9, the low one on dx7/opengl.

If I rescale things up or down (with setscale), images that didn't show this borders sometimes show it down or to the right, or as in this picture, on both sides (never on top-left) - the sample above shows this error when on 640x480 resolution, but on 800x600 it appears normally. On 1024x768 the problem appear once again.

The image above is 200 x 44. Enlarging it to 256 x 64 makes no difference at all.

Very strange problem...

Edit: Just noticed, seems to be something related to image filtering. Loading images with flag 0 (Loadimage "name",0) seems to fix the border problem, but images don't rescale very well...


SLotman(Posted 2009) [#20]
Don't know if this is a "correct" fix, but solved the problem here:

on TD3D9Max2DDriver, the DrawFrame method, just after:

format_code('
Method DrawFrame( frame:TDX9ImageFrame,x0#,y0#,x1#,y1#,tx#,ty#)
Local deviceTexture : TDx9DeviceTexture=frame._Dx9DeviceTexture
Local p: Float Ptr= Varptr(deviceTexture._Vertices[0])
Local d: Int Ptr = Int Ptr (p)

')

add this:

format_code('
x1:+(0.3*_jx)
y1:+(0.3*_jy)
')

And now I don't get black borders anymore!
I don't know why this fixes it, but it does... :)


DStastny(Posted 2009) [#21]
Hey Slotman,

If I read it right you are scaling the image down or up?

Whats your video card again? Are you using MIPMAPPEDIMAGE flag when you load the image?

I think you are seeing the one part in the read me that I commented about not implementing yet, if the hardware does not support mipmapping, or there is minor difference in hardware mipmap generation vs software rendered.

Ill try to make an image up and see if I can duplicate and get off my lazy butt and fix mipmapping.

Doug


SLotman(Posted 2009) [#22]
Scaling both up and down. Let me clarify: I'm re-scaling my whole game according to user desktop resolution. It works fine on dx7-ogl, but on dx9 I was seeing those artifacts on some resolutions - mainly on 640x480 and 1024x768, which is both scaled down and above from "normal" (1:1) 800x600 resolution.

And no, I´m not using the MIPMAPPEDIMAGE. But using it (or MASKEDIMAGE, FILTEREDIMAGE, etc) doesn't solve it at all.

The only thing I got to remove the lines was to disable the image filtering (D3DTEXF_LINEAR -> D3DTEXF_POINT) but then scaling images gets pretty bad; or the strange hack I posted above, which does work.

I'm on a ATI Radeon X1300.
If it helps, here's my CAPS:
format_code('
Adapater Info
----------------------------------------------------------------
(removed so it won't kludge the thread)
')


DStastny(Posted 2009) [#23]
Hmmm,

I think I might now whats going on since textures are loaded to power of 2. There is a step that is done in the Dx7 driver I dont do, regarding smearing the edges of the textures. Did not quite understand what that was doing.

The issues with your hack is fixing the issue as you are adjusting the coordinates by a partial pixel. This has do do with how pixels are mapped to textual and alogorim is used.

Any chance you could post simple example on how you are calculating this scaling factor? It would make it significantly easier for me to track down.

Thanks
Doug


DStastny(Posted 2009) [#24]
Well nope, its not what I thought.

I am stumped I cant seem to duplicate the problem. Well I can if I intentionally start butchering the texels/mapping.

Are your images PNG by chance? Can you post a link/email me the image you are using that demonstrates the problem?

Thanks
Doug


DStastny(Posted 2009) [#25]
@Slotman

I think I might have found it...

Replace these methods in the object TDx9DeviceTexture in

Dx9Max2dGraphicsDriver.bmx

format_codebox('
Method OnDeviceCreate()
dxlog "Texture created"+Self.ToString()
Local usage:Int=0
Local level:Int=1
If (_Flags&MIPMAPPEDIMAGE)
usage=D3DUSAGE_AUTOGENMIPMAP
level=0
End If
Local hr:Int= _Driver._DXDriver.Direct3DDevice9().CreateTexture(_TextureWidth,_TextureHeight,level,usage,_TextureFormat,D3DPOOL_MANAGED,_Texture,Null)
' Local hr:Int= _Driver._DXDriver.Direct3DDevice9().CreateTexture(_TextureWidth,_TextureHeight,level,usage,_TextureFormat,D3DPOOL_DEFAULT,_Texture,Null)
If hr<>D3D_OK Then Throw "Failed to Create Texture:"+HR
' lock the surface
Local lockrect:D3DLOCKED_RECT=New D3DLOCKED_RECT
If _Texture.LockRect(0,lockrect,Null,0)<> D3D_OK
_Texture.Release_
_Texture=Null
Throw "Failed to Lock Texture"
End If
' Move the pixmap to offscreen surface
Local sp:TPixmap=TPixmap.CreateStatic(lockrect.pBits,_TextureWidth,_TextureHeight,lockrect.Pitch,PF_BGRA8888)
sp.Paste(_Pixmap,0,0)
SmearEdges(lockrect,_TextureWidth,_TextureHeight)
' unlock the surface
_Texture.UnlockRect(0)
' Texture ready
End Method

' from DX7 driver clean up filtering artifacts on
' scaled textures
Method SmearEdges(lockrect:D3DLOCKED_RECT,width:Int,Height:Int)
Local p:Byte Ptr
Local n:Int
Local x:Int
Local y:Int
Local c:Int
If _pixmap.Width<>Width
n=1
If _Flags & MIPMAPPEDIMAGE n=Width-_pixmap.Width
For y=0 Until _pixmap.Height
p=lockrect.pBits+y*lockrect.Pitch
c=Int Ptr(p)[_pixmap.Width-1]
For x=0 Until n
Int Ptr(p)[_pixmap.Width+x]=c
Next
Next
EndIf
If _pixmap.Height<>Height
n=1
If _Flags & MIPMAPPEDIMAGE n=Height-_pixmap.height
p=lockrect.pBits+(_pixmap.height-1)*lockrect.Pitch
For y=1 To n
MemCopy p+y*lockrect.Pitch,p,Width*4
Next
EndIf
End Method
')

There was one method in the loading of textures in Dx7 driver I never quite understood what it did..... I think I know now. I could not exactly duplicate what you where seeing in down scaling but up scaling I saw a similar artifact. This change cleaned it up.

Your comment on messing with filtering gave me the clue.

Please let me know if this cleans it up as I want to upload a an update that fixes a few other issues. The stupid alpha bug mentioned above as well as MaxGUI Canvass problem.

I still have to update to latest release of Max...

Thanks for all the feedback and help you provided. Let see if this addresses the issue.

Doug


Tachyon(Posted 2009) [#26]
I wonder if this would fix my seam problem (post #8 above)?


DStastny(Posted 2009) [#27]
@Tachyon,

It might. Lot of factors, scaling, filtering, pixmap size, and to make it more fun video drivers and cards.

I know I fixed the alpha issue, and I reworked the VYSYNC that should fix the other issue your reported.

This might be fix to seam problem. What is the size of your tiles? Any scaling?

Doug


SLotman(Posted 2009) [#28]
Yup, that was the problem!

I was indeed using PNGs, and using this 'SmearEdges' fixed it :)


GW(Posted 2009) [#29]
Are all (or any) of these imports necessary in dx9graphics? why were they included?

Edit: I guess i should rephrase my question. I try to recompile the module to remove these auto imports I get the error "cannot find interface to 'brl.maxlua'

any ideas?
How can i restrict what bmax tries to import when doing a 'makemod' on this module?

format_code('
import brl.blitz
import pub.directx
import brl.graphics
import brl.linkedlist
import brl.appstub
import brl.audio
import brl.basic
import brl.bmploader
import brl.d3d7max2d
import brl.data
import brl.directsoundaudio
import brl.eventqueue
import brl.freeaudioaudio
import brl.freetypefont
import brl.gnet
import brl.jpgloader
import brl.map
import brl.maxlua
import brl.maxutil
import brl.oggloader
import brl.openalaudio
import brl.pngloader
import brl.retro
import brl.tgaloader
import brl.threads
import brl.timer
import brl.wavloader
import pub.freejoy
import pub.freeprocess
import pub.glew
import pub.macos
')


plash(Posted 2009) [#30]
Wow. That is totally unnecessary.


Brucey(Posted 2009) [#31]
Heh. Cool.

Are they actually part of an import list, or is that what you had to add to get it to compile?

Whenever you get that error "cannot find interface", it usually means your build is out-of-sync (assuming that everything else is correct).

If you are using someone else's pre-compiled module, you will generally need to have at least the brl+pub modules they have. Or you will see this.
If you don't, you will need to rebuild the module (you should be able to get away with only building the one).

From the BlitzMax/bin folder, you can run this command-line to do it :
format_code('
bmk makemods -a modulename
')
where "modulename" is the name of the module that you would Import - for example, bah.libxml
(I tried to look it up on the google code page, but the source isn't in the repository.. and I wasn't going to download the zip just to check that).

:o)


DStastny(Posted 2009) [#32]
None of those modules are directly referenced by the module.

I think Brucey is on to the problem as the module source zip is precompiled with 1.32 so you will need to rebuild the modules if you using a different version.

just type "bmk makemods -a dbs" and "bmk makemods -h dbs" if you want threaded versions.

Doug


GW(Posted 2009) [#33]
Sorry for the newbish question, I'm using 1.30. Everything after that seems to have major issues.
Has there been changes to the bMax native DX9 stuff after 1.30 ?
What would cause this error.

format_code('
C:\Dev\BlitzMax\bin>bmk makemods -a dbs
Compiling:d3d9caps.bmx
flat assembler version 1.66
3 passes, 24675 bytes.
Compiling:GraphicsDx9.bmx
Compile Error: Identifier 'D3DADAPTER_IDENTIFIER9' not found
[C:/Dev/BlitzMax/mod/dbs.mod/dx9graphics.mod/GraphicsDx9.bmx;459;2]
')


DStastny(Posted 2009) [#34]
As of 1.32 the directx9 headers have been made part of the official distribution so the module now imports the direct x references from the pub\directx module that is part of 1.32

The module is currently only compatible with 1.32 and above.

You could however back port the pub/directx module into the 1.30 modules and it should work correctly.

Doug


GW(Posted 2009) [#35]
Thanks for clear that up.


Dabhand(Posted 2009) [#36]
Possible bug:-

format_code('
SuperStrict

Import dbs.d3d9max2d

SetGraphicsDriver D3D9Max2DDriver()
Graphics 640, 480

Local mx:Int
Local my:Int

Repeat
Cls
mx = MouseX()
my = MouseY()

SetAlpha(0.0)
DrawCrosshair(mx, my) '<---Should be invisible
SetAlpha(1)
DrawText mx + ":" + my, 0, 0
Flip
Until KeyDown(KEY_ESCAPE)
End

Function DrawCrosshair(xp:Int, yp:Int)
Const midspace:Int=28,size:Int=14

SetLineWidth 3
DrawLine xp-midspace,yp,xp-midspace-size,yp
DrawLine xp+midspace,yp,xp+midspace+size,yp
DrawLine xp,yp-midspace,xp,yp-midspace-size
DrawLine xp,yp+midspace,xp,yp+midspace+size
End Function
')

The crosshair should become invisible due to the SetAlpha(0.0) command, but it stays as bright as a button?

Removing 'SetGraphicsDriver D3D9Max2DDriver()' and rolling back to the default driver fixes it, so obviously something is wrong.

Dabz


DStastny(Posted 2009) [#37]
It is a bug fixed up not uploaded yet.

see
Here


Dabhand(Posted 2009) [#38]
Oh, I see... I'll let it go then! :)

Dabz


Nigel Brown(Posted 2009) [#39]
Using some example code I found, I am testing speed of writting to dual screens. When using the D3D9Max2DDriver I am getting unexplained errors, please give it a go:

format_code('
Import dbs.d3d9max2d

Local screen:TGraphics[2]
Local x:Double[2,2] , y:Double[2,2]
Global quit% = False

'SetGraphicsDriver GLMax2DDriver()
'SetGraphicsDriver D3D7Max2DDriver()
SetGraphicsDriver D3D9Max2DDriver()

EnablePolledInput()

screen[0] = CreateGraphics( 640, 480, 0, 60, 0 )
screen[1] = CreateGraphics( 640, 480, 0, 60, 0 )
x[0 , 1] = 1
y[0 , 1] = 1.5
x[1 , 1] = 1
y[1 , 1] = 1.5


AddHook systemEventHook , _hook

While Not quit

For Local n = 0 To 1

SetGraphics( screen[n] )

x[n , 0] :+ x[n , 1]
y[n , 0] :+ y[n , 1]
If x[n , 0] > GraphicsWidth() Or x[n , 0] < 1 Then x[n , 1] = - x[n , 1]
If y[n , 0] > GraphicsHeight() Or y[n , 0] < 1 Then y[n , 1] = - y[n , 1]
Cls
DrawText("O" , x[n , 0] , y[n , 0] )
SetColor 255,n*255,n*255
If KeyDown(KEY_SPACE) And n=1 Then DrawOval 0 , 0 , 640 , 480

Flip
Next

Wend

Function _Hook:Object(iId:Int,tData:Object,tContext:Object)
Local Event:TEvent = TEvent(tData)
If Event.ID <> EVENT_MOUSEMOVE Then Print "[" + Event.ID + "] " + Event.ToString()
If event = Null Then Return tData
Select event.id
Case EVENT_APPTERMINATE
quit = True
End Select
Return tData
End Function
')


DStastny(Posted 2009) [#40]
@Nigel

I have to admit I did not even know you could do that that. I have to think about how to make something like that work.

That would work fine with two MAXGUI windows since the windows are created externally and you have some control over the windows.

Just curious why would you do something like this?

I will be on vacation til end of May so I wont be able to look at it til the end of the month.

Thanks
Doug


Nigel Brown(Posted 2009) [#41]
@DStastny

Thanks for the reply. I am developing a game that uses two monitors and this application does a good job of showing the consequence of using a non accelerated 2nd screen, just drag one of the windows to a second screen and watch how slow things become. I was hoping that DX9 did a better job than DX7 but not been able to test as yet.


DStastny(Posted 2009) [#42]
@Nigel

No problem on the reply. As for performance degradation with Dx7 probably OpenGL as well and why Dx9 does not currently work, has to do with the way resources are managed with the underlying drivers and how textures etc are shared, with a call to CreateGraphics .... there not.

Basically if you load an image and draw it on one Graphics Context then switch to another it disposes the one resource and reloads its. So nothing is ever cached and it just thrashes resources

With MAXGUI it uses the AttachGraphics call which sets up a shared context and that will not suffer that penalty. Under this the DX9 Driver works fine as I actually tested this conceptually with Dx9 and recently committed a fix as I was not correctly reseting the projection matrix on switch.

Dx9 will work significantly better than Dx7 as internally it setups separate swap chains for each window. Dx7 creates one huge hidden back buffer and blits internally manages rendering to a sub region the size of the window. I have not explored totaly how OpenGL works but most like creates a separate shared Opengl context for each DC (Device Context) of the window its attached.

I can probably make CreateGraphics work like Dx7/Dx9 but the internals of Texture management will suffer the same penalty. Unless you create the images and ensure they are only rendered in the correct window.

I recommend you try using MaxGUI to create the windows and attach Canvases and think you get what your looking for as well as performance to boot.

Doug


Tommo(Posted 2009) [#43]
I encounter the slow-down on my second monitor too. Not only with bmx, but also other games. Dx9 just performs the same.
I think it might be not a problem of the gfx API. I googled about it and find some similar reports, but no solution yet.