DirectDraw

13 views
Skip to first unread message

Greg Hazel

unread,
Jul 5, 2004, 5:50:53 PM7/5/04
to wx-...@lists.wxwidgets.org
I'm about to start a DirectDraw implementation for faster 2d drawing than GDI. I
heard there used to be work on a wxDirectDrawDC, but I can't find any code from
it. Was it scrapped?
Does anyone have anything to contribute? Starting from scratch is a lot of work.

-Greg


Ryan Norton

unread,
Jul 5, 2004, 11:55:08 PM7/5/04
to wx-...@lists.wxwidgets.org
Hi Greg,

Actually, I was going to do one myself... however if you're going
to go ahead and do one that's great!

Don't start from scratch though - take some code out of allegro - it's
essentailly public domain, and they've implemented most of the drawing
routines.

I'll be interested at the approach you will take - i.e. whether to use
a bitmap like allegro does or go directly to a surface...

Anyway, I'll help out any way I can - I could even do the integration
with wx
if you'd like...

Regards,
Ryan


Greg Hazel

unread,
Jul 6, 2004, 1:02:00 AM7/6/04
to wx-...@lists.wxwidgets.org
> Actually, I was going to do one myself... however if you're
> going to go ahead and do one that's great!

Don't let me discourage you from working on it - my experience with DirectDraw
consists of about 8 hours.
I've got the basics working in my application through ifdef, but I'm nowhere
near a wxDC implementation.
It seems like the DirectDraw API provides an easy way to get a DC from the
Surface. Do you know if this is just a hackish way to implement it, or if it's
just as good as using all the native functions?

> Don't start from scratch though - take some code out of
> allegro - it's essentailly public domain, and they've
> implemented most of the drawing routines.

Looks like useful stuff, but I failed miserably trying to get their ellipse
function to work with my code.

> I'll be interested at the approach you will take - i.e.
> whether to use a bitmap like allegro does or go directly to a
> surface...

>From what I've encountered so far, a surface seems ideal as a native bitmap
format. Why use a separate bitmap?

-Greg


Ryan Norton

unread,
Jul 6, 2004, 2:55:53 AM7/6/04
to wx-...@lists.wxwidgets.org
Hi Greg,

> > Actually, I was going to do one myself... however if you're
> > going to go ahead and do one that's great!
>
> Don't let me discourage you from working on it - my experience with
DirectDraw
> consists of about 8 hours.

Ooh boy... well I guess there's a first time for anything :).

> I've got the basics working in my application through ifdef, but I'm
nowhere
> near a wxDC implementation.
> It seems like the DirectDraw API provides an easy way to get a DC
from the
> Surface. Do you know if this is just a hackish way to implement it,
or if it's
> just as good as using all the native functions?

Most likely same speed as using GDI - mayby slower.

If you're going to do DirectDraw then you'll want to use the
Lock() and Unlock() functions of the surface (i.e.
IDirectDrawSurface).
(http://msdn.microsoft.com/archive/en-us/directx9_c/directx/htm/idirec
tdrawsurfacelock.asp).

After which you'll need to code all drawing routines by hand (i.e.
there's no
DrawCircle :)). Plus you need to output data that matches the pixel
format -
which means getting down and dirty with palletes if it's 8 bit...

It isn't as complicated as it sounds - there are no shortage of
tutorials for this
(gamedev.net and codeguru.com are a good place to start,
experts-exchange.com is good also). If you need a good book, pick up
isometric game programming with directx7 - you can learn
all this stuff in a weekend with a book like that...

> From what I've encountered so far, a surface seems ideal as a native
bitmap
> format. Why use a separate bitmap?

Well, technically the dc isn't supposed to actually draw on the window
until _after_
the paint event. However, IMHO if you can get away with locking the
surface
in the dc's constructor and unlocking it in the destructor that would
be nice
from a speed standpoint (you need to make sure nothing else tries
to draw to the window because it probably won't be able to).

Also, to same memory/time you'll want to modify window.cpp of
wxWidgets
to create a directdraw object at the window's creation and release it
on
window destruction (ifdef it out with wxUSE_DIRECTDRAW).

Anyway, as you can tell I did this for a few years :). I'll try to
answer
any questions thrown at me as best I can :).

Regards,
Ryan


Greg Hazel

unread,
Jul 6, 2004, 4:50:58 AM7/6/04
to wx-...@lists.wxwidgets.org
> Most likely same speed as using GDI - mayby slower.

Funny you should say that, after getting DirectDraw situated in my application I
noticed absolutely no difference in speed vs native GDI, barring the one case
where I did use GetDC() to DrawEllispe(), the result of which was lower
performance than GDI. On that note, I tried GDI+ briefly and saw a decrease in
performance too (granted, it was sort of a GDI/GDI+ jumble). Would it even be
worth the time to add GDI+ to wx?



> If you're going to do DirectDraw then you'll want to use the

> Lock() and Unlock() functions of the surface ).

Somehow I got away with not using Lock() and Unlock() in my application. Are
they just for thread safety?

> > From what I've encountered so far, a surface seems ideal as a native
> bitmap
> > format. Why use a separate bitmap?
> Well, technically the dc isn't supposed to actually draw on
> the window until _after_ the paint event. However, IMHO if
> you can get away with locking the surface in the dc's
> constructor and unlocking it in the destructor that would be
> nice from a speed standpoint (you need to make sure nothing
> else tries to draw to the window because it probably won't be
> able to).

I think this is the same way the GL implementation works.

> Also, to same memory/time you'll want to modify window.cpp of
> wxWidgets to create a directdraw object at the window's
> creation and release it on window destruction (ifdef it out
> with wxUSE_DIRECTDRAW).

Speaking of which, it looks like there'd have to be some pretty strange code to
do DirectDraw fullscreening properly. Should I ignore this and stick to a
fullscreen frame with DDSCL_NORMAL? I can't see a performance difference in my
application.



> Anyway, as you can tell I did this for a few years :). I'll
> try to answer any questions thrown at me as best I can :).

Thanks!

-Greg


Ryan Norton

unread,
Jul 6, 2004, 12:47:40 PM7/6/04
to wx-...@lists.wxwidgets.org
Hi Greg,


> > Most likely same speed as using GDI - mayby slower.
>
> Funny you should say that, after getting DirectDraw situated in my
application I
> noticed absolutely no difference in speed vs native GDI, barring the
one case
> where I did use GetDC() to DrawEllispe(), the result of which was
lower
> performance than GDI. On that note, I tried GDI+ briefly and saw a
decrease in
> performance too (granted, it was sort of a GDI/GDI+ jumble). Would
it even be
> worth the time to add GDI+ to wx?

Only if normal GDI is not going to work in longhorn...

>
> > If you're going to do DirectDraw then you'll want to use the
> > Lock() and Unlock() functions of the surface ).
>
> Somehow I got away with not using Lock() and Unlock() in my
application. Are
> they just for thread safety?

SPEED - Lock and Unlock provide direct access to video memory. If
you're not going to use those methods you may as well stick with GDI
:).
Done correctly you could get 100% performance boost... at least :).

> > Also, to same memory/time you'll want to modify window.cpp of
> > wxWidgets to create a directdraw object at the window's
> > creation and release it on window destruction (ifdef it out
> > with wxUSE_DIRECTDRAW).
>
> Speaking of which, it looks like there'd have to be some pretty
strange code to
> do DirectDraw fullscreening properly. Should I ignore this and stick
to a
> fullscreen frame with DDSCL_NORMAL? I can't see a performance
difference in my
> application.

Well, fullscreen mode is already handled by wxDisplay... sort of.
There's probably a small amount of work to be done there (i.e. not
creating a new directdraw object).

Regards,
Ryan


Greg Hazel

unread,
Jul 6, 2004, 6:04:44 PM7/6/04
to wx-...@lists.wxwidgets.org
> Only if normal GDI is not going to work in longhorn...

Surely it is.



> SPEED - Lock and Unlock provide direct access to video
> memory. If you're not going to use those methods you may as
> well stick with GDI :).
> Done correctly you could get 100% performance boost... at least :).

Well that would explain my performance problem. I'm having an issue with locking
though:

{
memset(&ddsd, 0, sizeof(DDSURFACEDESC2));
ddsd.dwSize = sizeof(DDSURFACEDESC2);

surface->Lock(NULL, &ddsd, DDLOCK_NOSYSLOCK | DDLOCK_WRITEONLY | DDLOCK_WAIT,
NULL);
//error checking removed

surface->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbfx); //some
color inserted in ddbfx

surface->Unlock(NULL);
}

The Lock() is successful, but the Blt is not (DDERR_LOCKEDSURFACES). Now it says
in MSDN:
"Do not call DirectDraw blit functions to blit from a locked region of a
surface."
But I'm calling the blit function to blit -to- a locked region, not from it.
Using surface->PageLock(0); returns no error, but does not seem to effect
performance.
Lock()ing seems to work fine if I stick to pixel data. Is Blt()ing an Unlock()ed
only function? (please tell me it's not, and that copying the pixel data by hand
while locked is not faster)

-Greg


Ryan Norton

unread,
Jul 6, 2004, 7:06:16 PM7/6/04
to wx-...@lists.wxwidgets.org
Hi Greg,

<snip>


> The Lock() is successful, but the Blt is not (DDERR_LOCKEDSURFACES).
Now it says
> in MSDN:
> "Do not call DirectDraw blit functions to blit from a locked region
of a
> surface."
> But I'm calling the blit function to blit -to- a locked region, not
from it.
> Using surface->PageLock(0); returns no error, but does not seem to
effect
> performance.
> Lock()ing seems to work fine if I stick to pixel data. Is Blt()ing
an Unlock()ed
> only function? (please tell me it's not, and that copying the pixel
data by hand
> while locked is not faster)

Once its locked, the only way ANYTHING - app, thread or otherwise can
draw
to a surface is through the lpSurface member of the DDSURFACEDESC
structure
passed from Lock - and that's the whole point of calling lock :).

The easiest way to do this is to set the pixel format to 16 bits, and
shift
wxColours based on the pixel color masks.

I.E. what you'll need to do is -
1. Take a look at DDSURFACEDESC at the lpSurface and lPitch members.
lPitch is width of a scanline in BYTES (not PIXELS :)) - this is
how you tell the width of the surface... (I'll talk about lpSurface in
a minute :))
http://msdn.microsoft.com/archive/en-us/directx9_c/directx/htm/ddsurfacedesc.asp

2. Now the other member, the most important one is ddpfPixelFormat.
This
is what tells you what format the pixels will be in. It is of type
DDPIXELFORMAT -
http://msdn.microsoft.com/archive/en-us/directx9_c/directx/htm/ddpixelformat.asp

There are three members that matter -
dwRBitMask
dwGBitMask
dwBBitMask

Now, typically video cards in 16 bit use the RGB555 variety, which
means they allocate 5 bits to each color (WX, by contrast, allocates
8 to each [max 255], this will come up later).

For example, the bitmask members in RGB555 would be (in binary) -

dwRBitMask - 0111110000000000
dwGBitMask - 0000001111100000
dwBBitMask - 0000000000011111

You'll probably notice what's going on - the highlighted bits
are the ones that are valid - i.e. the blue value of the pixel
is between 0 and 31.

An example of how to draw a pixel on a surface using
a wxColour and an RGB555 pixel format -
---------
//convert our surface to a 16 bit pointer so
//we can put a value on it
wxUint16* pSurface = (wxUint16*) ddsd.lpSurface;

//convert our wxColour to an RGB555
*pSurface |= color.Blue();
*pSurface |= color.Green() << 5;
*pSurface |= color.Red() << 10;
---------
Hopefully you're still following...

Anyway, you can't assume the pixel format is of RGB555,
or that the red green and blue values are in any particular order,
so once you get/set the pixel format of the surface you need to
do some math to figure out how much to shift each member
of the wxColour.

Regards,
Ryan

Ryan Norton

unread,
Jul 6, 2004, 9:54:13 PM7/6/04
to wx-...@lists.wxwidgets.org, Chris Bishop
Hi Chris,

> Forgive me for interrupting in a thread I'm not directly
involved in,
Feel free to interrupt :).

> but surely for the conversion from wxColor values to
RGB555 we first
> need to scale the wxColour components by 32/256 - i.e.
(top 5 bits) >> 3. Thus
> your code:
>
> RN> ---------
> RN> //convert our surface to a 16 bit pointer so
> RN> //we can put a value on it
> RN> wxUint16* pSurface = (wxUint16*) ddsd.lpSurface;
>
> RN> //convert our wxColour to an RGB555
> RN> *pSurface = color.Blue() >> 3;
> RN> *pSurface |= (color.Green() & 0xf8) << 2;
> RN> *pSurface |= (color.Red() & 0xf8) << 7;
> RN> ---------
>
> should be (also assuming *pSurface has not necessarily
been
> initialised to 0):
Right - except I think you got what I said mixed up with
what you said :).

Anyway, the reason for the ommission was that I was trying
to keep
things as simple as possible... one thing that's kind of
tough to do
if find out how much to shift the colors...
------------
int nShift = 0, nBits;
DWORD* pTempMask, *pTempBits, *pTempShift;
short wRBits = 0, wBBits = 0, wGBits = 0,
wRShift = 0, wBShift = 0, wGShift = 0;

for(int i = 0; i < 3; ++i)
{
nBits = 0;
if (dwRBitMask & (1 << nShift))
{
pTempMask = &dwRBitMask;
pTempBits = &wRBits;
pTempShift = &wRShift;
}
else if (dwBBitMask & (1 << nShift))
{
pTempMask = &dwBBitMask;
pTempBits = &wBBits;
pTempShift = &wBShift;
}
else
{
pTempMask = &dwGBitMask;
pTempBits = &wGBits;
pTempShift = &wGShift;
}

do{
*pTempBits |= 1 << nBits;
}while(*pTempMask & (1 << ++nShift));

*pTempShift = nShift - 8; // - 8 for wxColour's 255 max
}
----------
then when you need to convert your colour -


//convert our wxColour to an RGB555

*pSurface |= (color.Blue() & wBBits) << wBShift;
*pSurface |= (color.Green() & wGBits) << wGShift;
*pSurface |= (color.Red() & wRBits) << wRShift;
----------

Anyway - that may or may not work - I'm sure there's errors
in there someplace.... however, AFIAK the general idea
should work....

Regards,
Ryan


Chris Bishop

unread,
Jul 6, 2004, 9:10:29 PM7/6/04
to wx-...@lists.wxwidgets.org
Forgive me for interrupting in a thread I'm not directly involved in,
but surely for the conversion from wxColor values to RGB555 we first
need to scale the wxColour components by 32/256 - i.e. (top 5 bits) >> 3. Thus
your code:

RN> ---------
RN> //convert our surface to a 16 bit pointer so
RN> //we can put a value on it
RN> wxUint16* pSurface = (wxUint16*) ddsd.lpSurface;

RN> //convert our wxColour to an RGB555
RN> *pSurface = color.Blue() >> 3;
RN> *pSurface |= (color.Green() & 0xf8) << 2;
RN> *pSurface |= (color.Red() & 0xf8) << 7;
RN> ---------

should be (also assuming *pSurface has not necessarily been
initialised to 0):

---------


//convert our surface to a 16 bit pointer so
//we can put a value on it
wxUint16* pSurface = (wxUint16*) ddsd.lpSurface;

//convert our wxColour to an RGB555
*pSurface |= color.Blue();
*pSurface |= color.Green() << 5;
*pSurface |= color.Red() << 10;
---------


Best regards,

Chris Bishop

Wednesday, July 7, 2004, 11:06:16 AM, you wrote:

RN> Hi Greg,

RN> <snip>


>> The Lock() is successful, but the Blt is not (DDERR_LOCKEDSURFACES).

RN> Now it says


>> in MSDN:
>> "Do not call DirectDraw blit functions to blit from a locked region

RN> of a


>> surface."
>> But I'm calling the blit function to blit -to- a locked region, not

RN> from it.


>> Using surface->PageLock(0); returns no error, but does not seem to

RN> effect


>> performance.
>> Lock()ing seems to work fine if I stick to pixel data. Is Blt()ing

RN> an Unlock()ed


>> only function? (please tell me it's not, and that copying the pixel

RN> data by hand


>> while locked is not faster)

RN> Once its locked, the only way ANYTHING - app, thread or otherwise can
RN> draw
RN> to a surface is through the lpSurface member of the DDSURFACEDESC
RN> structure
RN> passed from Lock - and that's the whole point of calling lock :).

RN> The easiest way to do this is to set the pixel format to 16 bits, and
RN> shift
RN> wxColours based on the pixel color masks.

RN> I.E. what you'll need to do is -
RN> 1. Take a look at DDSURFACEDESC at the lpSurface and lPitch members.
RN> lPitch is width of a scanline in BYTES (not PIXELS :)) - this is
RN> how you tell the width of the surface... (I'll talk about lpSurface in
RN> a minute :))
RN> http://msdn.microsoft.com/archive/en-us/directx9_c/directx/htm/ddsurfacedesc.asp

RN> 2. Now the other member, the most important one is ddpfPixelFormat.
RN> This
RN> is what tells you what format the pixels will be in. It is of type
RN> DDPIXELFORMAT -
RN> http://msdn.microsoft.com/archive/en-us/directx9_c/directx/htm/ddpixelformat.asp

RN> There are three members that matter -
RN> dwRBitMask
RN> dwGBitMask
RN> dwBBitMask

RN> Now, typically video cards in 16 bit use the RGB555 variety, which
RN> means they allocate 5 bits to each color (WX, by contrast, allocates
RN> 8 to each [max 255], this will come up later).

RN> For example, the bitmask members in RGB555 would be (in binary) -

RN> dwRBitMask - 0111110000000000
RN> dwGBitMask - 0000001111100000
RN> dwBBitMask - 0000000000011111

RN> You'll probably notice what's going on - the highlighted bits
RN> are the ones that are valid - i.e. the blue value of the pixel
RN> is between 0 and 31.

RN> An example of how to draw a pixel on a surface using
RN> a wxColour and an RGB555 pixel format -
RN> ---------
RN> //convert our surface to a 16 bit pointer so
RN> //we can put a value on it
RN> wxUint16* pSurface = (wxUint16*) ddsd.lpSurface;

RN> //convert our wxColour to an RGB555
RN> *pSurface |= color.Blue();
RN> *pSurface |= color.Green() << 5;
RN> *pSurface |= color.Red() << 10;
RN> ---------
RN> Hopefully you're still following...

RN> Anyway, you can't assume the pixel format is of RGB555,
RN> or that the red green and blue values are in any particular order,
RN> so once you get/set the pixel format of the surface you need to
RN> do some math to figure out how much to shift each member
RN> of the wxColour.

RN> Regards,
RN> Ryan

RN> ---------------------------------------------------------------------
RN> To unsubscribe, e-mail: wx-dev-un...@lists.wxwidgets.org
RN> For additional commands, e-mail: wx-de...@lists.wxwidgets.org


Ryan Norton

unread,
Jul 7, 2004, 1:19:49 AM7/7/04
to wx-...@lists.wxwidgets.org
Better yet -
*pSurface = ((color.Blue() & wBBits) << wBShift) |
((color.Green() & wGBits) << wGShift) |
((color.Red() & wRBits) << wRShift);

Nice - that's under 30 clock cycles on modern machines...

Ryan

Reply all
Reply to author
Forward
0 new messages