DirectX 9 Target

Ferdi

New member
3rd Party Target Dev
Joined
Jun 21, 2017
Messages
87
I have been looking into creating a Direct X 9 target. First I had to create a new transcc to get a DirectX 9 target in there. Second, I tried compiling a simple rectangle example to make sure target directory is working and also transcc is working. Once all those are working I tried to get Mojo 1 going. I thought I had to rewrite Mojo 1, but it turn out I can used the variables in Mojo 1 and just plug it into Direct X 9 with minor modification of course.

In mojo.glfw.cpp at line 221, we can see that the Mojo 1 Open GL custom vertex is float x, float y, float textureU, float textureV, and 4 bytes color (alpha, red, green, blue). In DirectX 9 the custom vertex is float x, float y, float z, float rhw, then the 4 bytes color, and then float textureU, and float textureV.

So the difference between Open GL and DirectX 9 is the addition of z and rhw. and moving the texture co-ordinate to the back. Code like drawing rectangle needs to be updated to conform to this. So in Open GL drawing rectangle is like this, mojo.glfw.cpp, line 420:

C++:
    vp[0 ]=x0;vp[1 ]=y0;(int&)vp[4 ]=colorARGB;
    vp[5 ]=x1;vp[6 ]=y1;(int&)vp[9 ]=colorARGB;
    vp[10]=x2;vp[11]=y2;(int&)vp[14]=colorARGB;
    vp[15]=x3;vp[16]=y3;(int&)vp[19]=colorARGB;

In DirectX 9 it is like this:
C++:
    vp[ 0]=x0;vp[ 1]=y0;vp[ 2]=0.5f;vp[ 3]=1.0f;(int&)vp[ 4]=colorARGB;
    vp[ 7]=x1;vp[ 8]=y1;vp[ 9]=0.5f;vp[10]=1.0f;(int&)vp[11]=colorARGB;
    vp[14]=x2;vp[15]=y2;vp[16]=0.5f;vp[17]=1.0f;(int&)vp[18]=colorARGB;
    vp[21]=x3;vp[22]=y3;vp[23]=0.5f;vp[24]=1.0f;(int&)vp[25]=colorARGB;

Now for the Flush code (drawing code), in Open GL it is:
C++:
void gxtkGraphics::Flush(){
    if( !vertCount ) return;

    if( primSurf ){
        glEnable( GL_TEXTURE_2D );
        primSurf->Bind();
    }
      
    switch( primType ){
    case 1:
        glDrawArrays( GL_POINTS,0,vertCount );
        break;
    case 2:
        glDrawArrays( GL_LINES,0,vertCount );
        break;
    case 3:
        glDrawArrays( GL_TRIANGLES,0,vertCount );
        break;
    case 4:
        glDrawElements( GL_TRIANGLES,vertCount/4*6,GL_UNSIGNED_SHORT,quadIndices );
        break;
    default:
        for( int j=0;j<vertCount;j+=primType ){
            glDrawArrays( GL_TRIANGLE_FAN,j,primType );
        }
        break;
    }

    if( primSurf ){
        glDisable( GL_TEXTURE_2D );
    }

    vertCount=0;
}

And for the DirectX 9 is:
C++:
void gxtkGraphics::Flush(){
    if( !vertCount ) return;

    if( primSurf )
    {
        primSurf->Bind();
    }
    else
    {
        d3dDev->SetTexture(0, NULL);
    }

    d3dDev->BeginScene();    // begins the 3D scene

        // do 3D rendering on the back buffer here
        // select which vertex format we are using
        // d3dDev->SetFVF(CUSTOMFVF);
        d3dDev->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);

        switch( primType ){
        case 1:
            d3dDev->DrawPrimitiveUP(D3DPT_POINTLIST, vertCount, vertices, VERTEX_SIZE * sizeof(float));
            break;
        case 2:
            d3dDev->DrawPrimitiveUP(D3DPT_LINELIST, vertCount / 2, vertices, VERTEX_SIZE * sizeof(float));
            break;
        case 3:
            d3dDev->DrawPrimitiveUP(D3DPT_TRIANGLELIST, vertCount / 3, vertices, VERTEX_SIZE * sizeof(float));
            break;
        case 4:
            d3dDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, vertCount, vertCount / 4 * 2, quadIndices, D3DFMT_INDEX16, vertices, VERTEX_SIZE * sizeof(float));
            break;
        default:
            for( int j=0;j<vertCount;j+=primType ){
                d3dDev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, primType - 2, &vertices[j], VERTEX_SIZE * sizeof(float));
            }
            break;
        }

    d3dDev->EndScene();    // ends the 3D scene

    vertCount=0;
}

So what this does is whatever we were drawing in Open GL we just plug it into DirectX 9 with that modification of custom vertex from 5 items in Open GL to 7 items in DirectX 9. Hopefully this should create as little problem.

A wierd bug I encounter is Z needs to be 0.5 to always draw. It was drawing at z = 1.0. But then I think I tried playing with 3D like lighting, z buffer etc etc and when I tried DX9 Mojo 1 code again, it doesn't draw. I haven't figure out the root of the problem for this.

The other problem I am having is that I have managed to get DrawText (which uses mojo font) going. But not very good, and it is a hack. Because when DirectX load an image it loads it to power of 2 texture (ie 32, 64, 128 etc etc).

The code currently is quite messy, lots of example code that is redudant. Even that flush code, BeginScene and EndScene should only be called once per draw frame. A screenshot where it is up to:

dx9a.png


Anyway things I still need to look at:
* loading an image.
* direct sound.
* direct input but I read using window messages is better, so I may just rip GLFW code.
 
Last edited by a moderator:

MikeHart

Administrator
Joined
Jun 19, 2017
Messages
3,310
Awesome @Ferdi : Wouldn't DX11 be more future proof? Which changes did you actually do to Transcc?
 

Ferdi

New member
3rd Party Target Dev
Joined
Jun 21, 2017
Messages
87
DX11 is already done isn't it? It is the WinRT target for Mojo 1 anyway. (I am not 100% sure on this) If we need it for Windows, hopefully we can just reuse that code. This target is more for compatibility. That's my hope anyway.

I modified 1 file and copy another file:

1. The modified file is src/transcc/builders/builders.monkey. Any new target needs to be added in this file. If you see in monkey code something like TARGET="html5" or TARGET="glfw" it is defined in this file.

2. I copy the file glfw.monkey to dx9.monkey. I pretty much kept the same way of compiling glfw using the mingw makefile. I did change the makefile though to add the dx9 library and include directories. Also I removed Open GL include and libraries.

I think I will write a small tutorial on how to create a new target, specifically on changing transcc. Let me think about it.
 

MikeHart

Administrator
Joined
Jun 19, 2017
Messages
3,310
If you see in monkey code something like TARGET="html5" or TARGET="glfw" it is defined in this file.

Ahh, ok. Didn't thought about that. That definitely should be made more configurizeable, so you don't have to touch it. It is C++ after all in both cases.
 

Ferdi

New member
3rd Party Target Dev
Joined
Jun 21, 2017
Messages
87
@therevills Sorry for the late reply. Yeah ... but I kinda need it for more than just Mojo 1. I need it more to be like how gles20 module is like, ie the Direct 3D methods are extended out (hope that make sense). But in doing that, I might as well get Mojo 1 going.

First I tried to get DirectInput going, but it just did not plug nicely into Monkey input. So I ended up ripping GLFW window messages. This works much better but the mouse x and y co-ordinate would not align properly. I found it is because I did not calculate the window size correctly. The solution was also in GLFW. It is this function:

C++:
//========================================================================
// Translate client window size to full window size (including window borders)
//========================================================================

static void GetFullWindowSize( int clientWidth, int clientHeight, int dwStyle, int dwExStyle,
                               int *fullWidth, int *fullHeight )
{
    RECT rect;

    // Create a window rectangle
    rect.left   = (long)0;
    rect.right  = (long)clientWidth - 1;
    rect.top    = (long)0;
    rect.bottom = (long)clientHeight - 1;

    // Adjust according to window styles
    AdjustWindowRectEx( &rect, dwStyle, FALSE, dwExStyle );

    // Calculate width and height of full window
    *fullWidth = rect.right - rect.left + 1;
    *fullHeight = rect.bottom - rect.top + 1;
}

Joystick is still not in. At this point, it is only keyboard and mouse.

I think I am going to "attempt" to remove Open GL code from GLFW. And create like a skeleton Windows template target. Then we can put the DirectX stuff on top of it.

I have uploaded a DirectX 9 test showing keyboard and mouse is working into my dropbox account. I have not tried doing retry strategy during initialization, so I only check for HAL (which is hardware). I will make sure it falls back to software if hardware is not available. I only tested it on Windows 7 and XP.

Dropbox Link: https://www.dropbox.com/s/0wum931dhadiu8r/dx9inputtest.zip?dl=0 (1.31MB)

Middle mouse button - change color
Right mouse button - when press down change cursor to circle

WASD or arrow key - move the square around.
space key - change color

TODO
* Attempt to remove open gl code from GLFW.
* Joystick is not working.
* DirectSound.
* And loading an image.
 
Last edited by a moderator:

MikeHart

Administrator
Joined
Jun 19, 2017
Messages
3,310
Good luck with this. I am can imagine that it is a huge effort. Thanks for tackling it.
 

Ferdi

New member
3rd Party Target Dev
Joined
Jun 21, 2017
Messages
87
Just a quick update. DirectSound is working. But DirectSound doesn't seems to have channels system. I need to look into it further. I used the ogg and wav loader from GLFW, and put the data from those into DirectSound. I need to start cleaning up code, so I can upload to Github. Either I look into loading texture properly, or framework. The choice is using GLFW or create my own simple framework. I was going to upload an example of DirectSound working, but it is half working, so I decided not to do it.

TODO
* Framework
* Image loading
* Joystick
* Channels
 

Ferdi

New member
3rd Party Target Dev
Joined
Jun 21, 2017
Messages
87
Source Code

The source code is at Github: https://github.com/Ferddi/cerberus/tree/dx9

The current source code is in branch dx9.

DLL or No DLL

I think I write a separate post for this, I want to try out the voting system.

Toggle(ing) Fullscreen

bouncyalien example: https://www.dropbox.com/s/kljo9mr9udq7w8w/bouncyaliens.zip?dl=0

This bouncyalien example is modified. In the example when it goes to fullscreen it does this:

Code:
            SetSwapInterval 1            'I reckon there's a 98% chance this will give us 60fps on YOUR PC!
            SetUpdateRate 0

I am not sure exactly what SetSwapInterval is. So it is currently not working. Also the example uses different sizes for windows and fullscreen. I may not be able to do this in DX9, because in OpenGL it destroy the window and recreate it. While in DX9 I need to reset the d3d device, and textures (more on this below).

Weird Bug in Fullscreen

So when the program goes into fullscreen mode, the z buffer must be cleared. If not it will not draw anything. Even if I turn off Z buffer altogether I still need to clear it when it is in fullscreen. In window mode if I turn off z buffer, I don't need to clear z buffer for d3d to draw. Strange.

C++:
    _dx9fwD3dDev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

D3DPOOL_MANAGED and D3DPOOL_DEFAULT

Currently all images are loaded using D3DPOOL_MANAGED. But if that fails it should retry using D3DPOOL_DEFAULT. When using D3DPOOL_DEFAULT, and there is fullscreen toggle (or window minimized and maximized) the images will be lost, and require to be restored. I need to figure all these out. It is still not in.

DirectSound

audiotest example showing directsound is working: https://www.dropbox.com/s/vtus0g5tm9yb3g0/audiotest.zip?dl=0

The key to directsound is using DuplicateSound method. This create a new soundbuffer using the same memory as the soundbuffer that is loaded. So for each channel it does the following:

C++:
    if(chan->sample->sampleId == -1 || chan->sample->sampleId != sample->sampleId)
    {
        if(chan->sample->sampleId > 0)
        {
            chan->sample->dsBuffer->Release();
            chan->sample->sampleId = -1;
        }
        if(chan->sample->sampleId < 0)
        {
            result = _dx9fwDsound->DuplicateSoundBuffer(sample->dsBuffer, &chan->sample->dsBuffer);
            if(FAILED(result))
            {
                dx9fwError(__FILE__, __LINE__, "DuplicateSoundBuffer FAILED!");
            }
            chan->sample->sampleId = sample->sampleId;
            chan->sample->hertz = sample->hertz;
        }
    }

Joystick

Joytest example: https://www.dropbox.com/s/93nd2wcgd6gtysi/joytest.zip?dl=0

The code for joystick is rip from GLFW.

Alpha Blending

mojotest example: https://www.dropbox.com/s/p4gsanyio9n2b02/mojotest.zip?dl=0

blobmonster example: https://www.dropbox.com/s/ef8moo4xpgjwy68/blobmonster.zip?dl=0

TODO
* run all the examples
* SetSwapInterval
* need to do something to texture when reseting d3d and using D3DPOOL_DEFAULT
* read write pixels not working
* reload sound in audiotest
 

Ferdi

New member
3rd Party Target Dev
Joined
Jun 21, 2017
Messages
87
Source Code

The source code is at Github: https://github.com/Ferddi/cerberus/tree/dx9

I have merged all the latest stuff from develop branch over at KrautApps. So should be the latest.

D3DPOOL_DEFAULT vs D3DPOOL_MANAGED

Useful information: https://msdn.microsoft.com/en-us/library/windows/desktop/bb172584(v=vs.85).aspx

D3DPOOL_DEFAULT means video memory. We can't just create a texture using D3DPOOL_DEFAULT. First we have to create it using D3DPOOL_SYSTEMMEM then we use the function IDirect3DDevice9::UpdateTexture to copy from D3DPOOL_SYSTEMMEM to D3DPOOL_DEFAULT.

D3DPOOL_MANAGED means it does what I say in the first paragraph automatically for us. So we just use D3DPOOL_MANAGED, and we don't have to worry about lost textures and reset textures.

Swap Interval

Swap interval is vsync. To do it in Direct3D 9 you need to do this before you call Reset:

C++:
void dx9fwSetSwapInterval( int swapInterval )
{
    if ( swapInterval == 1 )
    {
        _dx9fwD3dParams.PresentationInterval       = D3DPRESENT_INTERVAL_ONE;
       _dx9fwD3dParams.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
    }
    else
    {
        _dx9fwD3dParams.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;
       _dx9fwD3dParams.FullScreen_RefreshRateInHz = 0;
    }
}

So unfortunately the Bouncy Aliens example need to change. SetSwapInterval needs to be called before SetDeviceWindow like the following example:

Code:
            SetSwapInterval 1            'I reckon there's a 98% chance this will give us 60fps on YOUR PC!
            SetUpdateRate 0
            SetDeviceWindow 1024,768,1

I may think about this some more.

ReadPixels and WritePixels

dynamicimage example: https://www.dropbox.com/s/9zfcge2kqx9gydc/dynamicimage.zip?dl=0

First you have to get the back buffer surface using GetBackBuffer function. But this back buffer surface is in video memory. So you have to create another surface in system memory and copy the video memory back buffer surface to the system memory surface. Then you can do lock rect, copy the buffer and unlock rect.

TODO

* reload sound in audiotest
* smooths scroll example
* reflection test example
 
Last edited:

Ferdi

New member
3rd Party Target Dev
Joined
Jun 21, 2017
Messages
87
I have updated with fix to smooth scroll example and reflection test example. Unfortunately for smooths scroll example SetDeviceWindow needs to be called after SetSwapInterval. It is because they way DirectX9 reset works.

I have managed to progress a little further with reload sound, but if I stress test reload sound it still crashs. My plan is to get a MSVC build going, so I can use the debugger, and see exactly what is happening.

TODO

* Get MSVC going.
* reaload sound in audio test example.
 

Ferdi

New member
3rd Party Target Dev
Joined
Jun 21, 2017
Messages
87
Source Code

The source code is at Github: https://github.com/Ferddi/cerberus/tree/dx9

I have merged all the latest stuff from develop branch over at KrautApps. So should be the latest.

MSVC

MSVC is compiling. I am using 2015. MSVC requires different include header files and libraries to GCC, which I have included in the target directory. Hopefully when comping, there won't be lots of errors because wrong header files are used.

Reload Sounds

Reload sounds now works like in GLFW. When gxtkSample is destroyed or Discard is called, it puts the sound buffer into a list. Why put it into a list? Because we need to wait until the sound stop before the sound buffer is destroyed. This is done in the function FlushDiscarded.

But that is not where the crash is. The crash occurred because of this line:

C++:
if( data ) gc_ext_malloced( (*length)*(*channels)*(*format) );

Depending on the compiler, if it is a Microsoft compiler it calls InterlockedExchangeAdd, and if it is a GCC it calls __sync_fetch_and_add. I believe it is for multi-threading, but not 100% sure. I am not familiar with the all garbage collection code in CX or multi-threading. So for now I just commented that line out.

Bugs

If anyone uses this and find bugs, all I asked is a code snippet that reproduce the problem (or a step by step instruction). If I can't reproduce it, I can't fix it. Thanks.
 

Ferdi

New member
3rd Party Target Dev
Joined
Jun 21, 2017
Messages
87
Hi Mike, sorry for the very late reply. It is on branch, so it is easy to merge, plus you can just use git to download just the dx9 branch to your repository. I think that should be possible. I have already merge everything from Xaron develop branch, so it should be up to date. I am going to leave it on a branch for a while. Because I have a feeling I haven't found all the problem. I guess if you want to look at the target only you could use git to diff 2 branches, something like git diff dx9 develop.
 
Top Bottom