PDA

View Full Version : TDirectDrawSurfaces not working in hardware mode?



Evil_Toaster
21-08-2006, 07:42 PM
Hi Folks,
(Apologies if this post appears twice. I registered, added the post, and then the site ate my user account. Thus deja vu for me at least)

I'm wondering if there's anyone who could help me find a solution to a problem I'm having with using TDirectDrawSurfaces.

I'm creating a game which makes heavy use of rotation functions to avoid making artwork. One of the things I need to do is run collission detection tests against graphics. I'm doing this by rendering rotated images to a background surface and then checking for a hit.

I've encountered two problems while trying to implement this:
- Surfaces do not work under hardware mode (From what I can gather, they get assigned correctly and you can render to them, but all draws to the surface after the first do not render)
- Reading from the surface pixel/pixels properties seems to cause a massive performance hit. (I.e. 9 checks per loop makes the engine start crawling) This has something to do with the surface being locked/unlocked.

I read in the latest readme.rtf on the unDelphiX site that any surface apart from the DXDraw surface will not work, but perhaps there's someone here who's managed to find a work around for the problem?

Speeeder
21-08-2006, 10:32 PM
Surely, I've made my game using TDirectDrawSurfaces, and it works perfectly, so you're mussing something up yourself.

Firstly, check if you have the latest version of UnDelphiX, with the places fixed what I've posted here: http://www.pascalgamedevelopment.com/forums/viewtopic.php?t=3265&start=15&sid=7cecc4e4beb794e283a4662506d7214e

On the main DXDraw, check if you have doDirectX7Mode, do3D, doFlip, doHardware all on. Othervise hardware mode won't be turned on.

When you're drawing, use the (main) DXDraw.Surface.blahblah functions to draw your graphic, not the DXDraw.Surface.Canvas.blahblah, as those are the simple GDI functions, so they're slow as bloody hell!

If you're doing hit check, you maybe should concider using hitboxes for example, just be sure to store every degree's sin to an array when your game starts and read the values from the array instead of generating them with the Delphi functions as they're slow as hell... (cos is sin(x+90), starting from 0 after 360... You can put in 450¬? if you're lazy for the check :P)

P.S. I'm sorry if I've said something stupid or what you already know!

Evil_Toaster
22-08-2006, 12:02 AM
Heya Speeeder, thanks for the reply.

I downloaded the latest version of unDelphiX a few days ago (1.07), I think surfaces were working in the previous version I had (Possibly 1.0.5). I'll have a look at your code changes and see if they help.

I reproduced the problem in the Prototype project in the main 'Dx' folder of unDelphiX:

I load an image into a TDXImageList:



Var
IL1 : TDXImageList;

...

IL1 := TDXImageList.Create(Self);
IL1.DXDraw := DXDraw;
IL1.Items.Add;
IL1.Items[0].Picture.LoadFromFile(ExtractFilePath( Application.ExeName)+'/Tmp/Stars1a.bmp');


I create a surface:



procedure TForm1.DXDrawInitialize(Sender: TObject);
begin
Dxtimer.Enabled := true;
Surface := TDirectDrawSurface.Create(DXDraw.DDraw);
Surface.SetSize(255,255);
end;


Then render in the main loop:



Surface.Fill($00FFFFFF);
Surface.Draw(Tmp,100,IL1.Items[0].PatternSurfaces[ 0]);

DXDraw.Surface.Draw(0,100,Surface);

// Using this doesn't work at all in hardware:
// IL1.Items[0].Draw(Surface,Tmp,100,0);





The Tmp variable is a byte incrementing from 0..255

If you run the project, the surface will render perfectly in software mode, and you'll see an image moving across it. If you press space to switch to hardware mode, the surface stops rendering.

So, it could be the way I'm using surfaces, it could be that the prototype project is wrong, or it could be those changes you mentioned.

At present, I'm implementing checking for mouse overs on game sprites. I'm already using square bounding boxes to limit the number of graphics I need to check against.

I calculate the relative position of the mouse and graphic, then render the graphic to a surface. I then check a single pixel on the surface to determine if there's a collission (I.e. Tip of mouse is on graphic). However, accessing the Pixels property seems to kill performance. I need to have pixel accurate detection though, is there a better way to access the pixels on a surface?

By the way, when you talk about hitboxes, do you mean rotated bounding boxes to check for collissions? (Not sure how to do those, but it would be nice to know...)

P.S. More correctly, cos=sin(x+PI/2), since most math functions work on radians. ;) I suspect degree's were invented by school teachers.

JSoftware
22-08-2006, 06:29 AM
P.S. More correctly, cos=sin(x+PI/2), since most math functions work on radians. ;) I suspect degree's were invented by school teachers.

Most likely sailors :P

"Change course 0.5PI starboard!!"

Traveler
22-08-2006, 07:43 AM
Are you constantly checking if your mouse is hovering above a sprite or only on mousedown?

Evil_Toaster
22-08-2006, 03:42 PM
P.S. More correctly, cos=sin(x+PI/2), since most math functions work on radians. ;) I suspect degree's were invented by school teachers.

Most likely sailors :P

"Change course 0.5PI starboard!!"

I think that kinda thing would have made pirates more stylish though.

Evil_Toaster
22-08-2006, 03:53 PM
Are you constantly checking if your mouse is hovering above a sprite or only on mousedown?

Constantly at 60fps. At present, I'm checking against at most 8 sprites.

In hardware mode, I can render hundreds of rotates objects directly to the main display... Either rendering to surfaces is slower, or it's accessing the pixels property. I can't really test at the moment though, since rendering to surfaces isn't working...

WILL
22-08-2006, 09:55 PM
P.S. More correctly, cos=sin(x+PI/2), since most math functions work on radians. ;) I suspect degree's were invented by school teachers.

Most likely sailors :P

"Change course 0.5PI starboard!!"

:lol: That'd be a first for me! :P

Evil_Toaster
23-08-2006, 02:30 AM
Ok, lets try a different tack.

Could someone please download and run this project?
http://etgames.q.org.za/tmp/Prototype.zip

The code used is the Prototype project provided with unDelphiX. The sum total of the code added is:



// in Public Variables
Surface : TDirectDrawSurface;
IL : TDXImageList; // Unused
Temp : Byte;
...

// in DXDraw Initialise
Surface := TDirectDrawSurface.Create(DXDraw.DDraw);
Surface.SetSize(256,256);
...

// in DXDraw Finalise
Surface.Free;
...

// in Timer Loop
Inc(Temp,10);
Surface.Fill(Temp);
DXDraw.Surface.Draw(0,100,Rect(0,0,Surface.Width,S urface.Height),Surface);


When I run this on my machine: In software mode, the surface cycles through shades of blue. If I switch to hardware mode, rendering freezes.

Please, I'd really appreciate some help in fixing this problem?

Traveler
23-08-2006, 07:13 AM
I've tried it and it appears to work pretty well for me. However, since you did not include your own exectuable I had to compile it myself with, I assume a different version of delphix, than you have. (Note that users without delphix problably wont test it at all)

In any case because I do not use undelphix, but the origional version of delphix, I had to change a couple things.
Beginscene and endscene are not know to my version and so are doDirectX7Mode, I removed them. Also, the draw function took an extra transparency parameter. After that it compiled without errors.

On my crappy pc (the one I use at work, radeon 7000), runs software mode at around 30 fps. Hardware mode does it a little better at ~400.

I did not see any odd things in your code, except mayby the part where you change the surface color. Since you're changing it at every frame it becomes very hard to see whats happening when using hardware mode.
It would be a typical thing to do timebased instead of framebased. But I doubt thats your problem anyway.

Hope that helps a bit.
In case you want to test the compiled exe, you can grab it here (http://www.gameprogrammer.net/zip/Evil_Toaster.zip)

Evil_Toaster
23-08-2006, 01:47 PM
Hi Traveler, thanks for having a look.

Are you using the original original DelphiX? (I.e. Hori's 2000.07.07 version?) As I remember, I stopped using it because pretty much all advanced draw functions killed performance if you rendered 10 or more sprites using them.

Your version works fine on my pc, with 126fps on software, and about 1600fps in hardware.

I think there's a bug in unDelphiX v1.0.7, or perhaps it doesn't like Delphi 6? *sigh*.. who knows. :/

The only purpose behind changing of the surface colour is so one can see if the surface is rendering. If it's flashing or you see screen tearing, great, it's working. If it's static, bad, it's broken. ;)

I've uploaded the project again including the exe:
http://etgames.q.org.za/tmp/Prototype2.zip

Traveler
23-08-2006, 03:50 PM
I believe its an even older version from october 1999. I've never used alpha blending so I really didn't have any problems when using a lot of sprites.

Your sample didn't work for me either. The exe didn't seem to freeze when using hardware mode, its just that the square didn't fade in/out anymore. Odd enough the fps was even lower than my sample. Around 320 fps using hardware mode.

In any case, from what you've written, I'd say there's something wrong with your undelphix version.

Evil_Toaster
23-08-2006, 06:56 PM
Ah well I'm pretty pathetic at graphics, so I prefer to get the computer to do as much work for me as possible. ;)

I rolled back to the previous version of unDelphiX that I was using. That fixed the surface rendering problem, but I was still having major speed hits when rendering to a surface. It seems that point I made in my original post was correct, that hardware acceleration is not available on any surface other than DXDraw:



TDirectDrawSurface.DrawRotate Function:
...

If AsSigned(D2D) Then Begin
If D2D.CanUseD2D Then Begin
If D2D.FDDraw.Surface = Self Then Begin
D2D.D2DRenderRotateDDS(Source,X,Y,Width,Height,Cen terX, CenterY,Angle,Transparent);
Exit;
End;
End;
End;

// Else render using software


The line "If D2D.FDDraw.Surface = Self Then Begin" is checking if the current object doing the rendering is the DXDraw object. If it isn't, then the graphic is drawn to the surface using software, which is ludicrously slow.

I've found a solution, but it's a bit of a hack. I'm now running two scenes per frame. In the first, I run all the collission detection using the DXDraw's surface, but never flip anything to the main display. Not exactly the ideal I was looking for, but it's much faster than software rendering to surfaces. ;)

Traveler
23-08-2006, 08:07 PM
Good to see you got things sorted out now...

In any case If all things fail, you might want to consider moving to the new opengl based wrapper thats being build by Andreaz and his team. (http://www.pascalgamedevelopment.com/forums/viewtopic.php?t=3345&highlight=) They appear quite active the last few weeks and are rapidly moving to a fully functional system for creating 2d (possibly 3d too?) based games.

Evil_Toaster
23-08-2006, 10:19 PM
Yeah, I'm quite relieved to have found a solution. ;) I'm busy making a game for a competition, the cut off date for which is a little over a month away. With all the other things I need to get in, fighting with technical issues like this is frustrating.

I saw that thread, looks like there's been a lot happening. I'll definately check it out at some point, the feature list looks great.

Speeeder
24-08-2006, 11:51 AM
Seems like I wasn't listening for some time :)

Well, the way it was meant to work with DirectDrawSurfaces is to allocate them in objects you store the pictures of in, loading when your program does the loading and freeing when it won't be used again (thus avoiding the many allocating, constructing and image loadings) and draw them directly on the main surface.

As for the "hit boxes", I did mean rotated bounding boxes, yes. And to check if they're inside or outside of them, you need to use the normal vectors of the edge vectors cast on the point, then you can count if the point is on which side of the original vector quite CPU efficiently I guess. At least this would be the way I'd do it, instead of pixel-checking ^^ However, for hit checking, you can as well draw the needed-to-check pictures on the main surface when the frame starts then just draw a fillrect on them. However I'm not sure it'd be much diff from your way :D

Evil_Toaster
27-08-2006, 08:46 PM
I assume you mean the functionality that's built into the TDXImagelist object? I use them extensively, but I was wanting to use surfaces for the following two reasons:
- Maintain pre-rendered versions of a group of images (Great for improving performance), or pre-rendered rotations/alpha/etc
- Using them for pixel accurate collission detection checks (Using the DXDraw surface works, but it's not as efficient or convenient)

Ah, ok then I probably do know how to implement those hit boxes, I just haven't done it before. ;) I'll go have a look through my 3D math references again sometime.

I've implemented the pixel checks in exactly the way you've described, the only difference is that I'm running them in a separate point in the program from the render function.

Anyhow, the result of this whole saga is that I've found two bugs within UnDelphiX 1.07, and two features I'd like to see implemented. I know you do have (or have had) some involvement with its development, so if there are plans for another version, it would be great if the following could be done:

Bug to Fix (The two bugs below are not present in the older version of UnDelphiX that I'm running. It's either v1.0.5 or v1.0.6)
- Surfaces fail to render in hardware mode (See that project I uploaded for an example)
- You cannot use more than one TDXImagelist class in your project, rendering fails. It's quite possible this bug is related to the first one.

Features
- Hardware rendering to surfaces
- DrawSkew functions (Basically, the ability to draw an image warped into a parallelogram/rhombus. Needs to be able to stretch):


|--------| /--------/
| | / /
| | --> / /
| | / /
|--------| /--------/

Speeeder
28-08-2006, 01:30 AM
uhm, as for the Surfaces failing to render in hardware mode, I'm absoletely sure that they work perfectly using TDirectDrawSurfaces. Using a TImageList... Well, I somehow doubt that it isn't working, I think you mused something up. More than one DXImageList - I have no idea, but I can't imagine why you'd really need two of them :)

As for what I mean, here's a little illustration:
http://img219.imageshack.us/img219/373/geometryet5.gif

I hope it's about understandable, that'd be the rotated rectangle. You know its corners and a dot you want to know if it's in it or outside of it. You can count which side of a rectangle-side it is on with the described way, you need to count it for 2-4 sides, whichever would be faster for the computer... (Guess it's probably the 4 sides)

If you wish to see if a line intersects the rectangle, that's even more easy. You count the equation for all rectangle edges and the line, count the variables from the two equation for the intersection coordinate, then compare it to the line's ending points.

If you wish to see if rect hits rect, then you need to follow about the same logic as with the line.

This algo wouldn't be much easy, but written well, I'm pretty sure it'd be much faster than per pixel checking, and it'd be a bit scalable, say, you wouldn't want to call it a hit if you want to make the target image look like it's in 3D, so it could as well hit it at its middle or so.

And now, let's see that uploaded project.......................

Speeeder
28-08-2006, 01:52 AM
aahhh, yupp... You're drawing the image transparently. The transparent colour will be the picture's upper-left pixel's colour. If you do not wish to draw with transparency, then in the drawing method set false to the appropriate parameter.

Evil_Toaster
28-08-2006, 09:54 AM
Thanks for the reference. I understand in theory what needs to be done, but I don't quite follow the maths. I follow that a, b is translating x1, y1 and x2, y2 into a vector, and -b,a or b,-a gives you a vector perpendicular to a, b. I assume creating the equations would mean something like:
x = by + a, or y = ax + b (Line on box)
x = ay - b, or y = -bx + a (Line Perpendicular to box line)

I'm not sure what is meant by "count their equations to get the intersection point" though? I have a feeling I should know, but my maths is a bit rusty. ;)

Uh, did you try this project?
http://etgames.q.org.za/tmp/Prototype2.zip

I'm not even drawing an image here, it's just a surface being plotted to the DXDraw surface. Switching to hardware mode causes rendering of the surface to fail. (Try the .exe I provided with the project) Drawing via TDXImagelists works fine (as long as I don't create more than one).

The reason for me having multiple image lists is a custom graphics storage format I created which allows me to address images from the context of sets of images. This was of course done before I found out that you can have multiple images per item in an image list, but since I built a custom image set creator program to easily put together my graphics sets, I'm still using my format. Under this format, having all the images for a game within a single image set loses me the ability to have sets of sets of images. (I.e. Unit > Walking Animation > <set of images>) Instead, everything would be mixed up in one long list of sets. Anyhow, it's not the end of the world or anything, but the functionality was working in previous versions and now it isn't.

Speeeder
28-08-2006, 11:15 AM
errrr, let's begin at the start... From a point on a line and a normal vector to it (a perpendicular vector), you can make an equation that's the line itself. I do not know the exact formula right now, and I'm kindof lazy to dig it up as I'm going in 10, but you can just prolly google it with something like... "line's equation formula" or something... You then get the equation for 2 lines. You can for example count the X (with a Y variable) from one of the equations, put it into the second, where you can then get the Y value. And then the X from using the Y's value you got. The X; Y you have is the intersection point of the two lines. From there, you can guess all.

In UnDelphiX, the hardware mode cannot process the transparent colour setting of an image, so instead it uses the upper-left corner by default, and it does so on ANY surface! Even if you've drawn it, not loaded it as a bitmap. As your Surface is 1 colour only, it's totally transparent. To draw it non-transparently, Draw(0, 0, Surfacename, !!!FALSE!!!); Voila, it's working well!

Lastly... If you wish to organize your pictures as so, I'd recommend you storing images in your own classes in your own structures in TDirectDrawSurface objects. Loading them from files or resources runtime when your program loads. (There, you can optimize memory usage too, by loading the only needed pictures) I'm using this method, and it's working juuuuuust nicely. Also, for a game, I think it's probably best if you do not put your images into the EXE file as resources, just leave them outside the program as whatever format (but prolly one that packs well) and loading them runtime. Phisically you can organize images into directorys, and logically into classes, it's cool ^^. One last thing you can do is put animations into ONE picture file that will contain x frames of the animation, and it'll just draw the picture cutting the correct frame out (this is probably a better solution in storage-space too). If you happen to use this method, note that in hardware, UnDelphiX will somehow fail to draw images longer in width than 2048 pixels when you cut a subpart out of it. Haven't tried on the height tho!

Evil_Toaster
02-09-2006, 12:14 AM
Cool, that makes sense, except what value to use for counting. Doesn't that mean I'd need to know either the interection point's X or Y value beforehand?

Ok, I follow that transparency colours are a problem when rendering images in hardware mode, but I don't think that you looked at the sample I put up? Please take a look at the code in this project (Also, try running the .exe):

http://etgames.q.org.za/tmp/Prototype2.zip

I am not using any images in this example, and the problem is not caused by transparencies. I am cycling a value through the shades of blue, then clearing a surface to that colour, then rendering the surface to the main DXDraw surface:



Inc&#40;Temp,10&#41;;
Surface.Fill&#40;Temp&#41;;
DXDraw.Surface.Draw&#40;0,100,Rect&#40;0,0,Surface.Width,S urface.Height&#41;,Surface&#41;;


In software mode: You see the surface cycling through shades of blue
In hardware mode: The surface gets "stuck" on the last colour that it was changed to in software mode.

So, that is what I mean by rendering failing is that any drawing to the surface in hardware simply doesn't happen. I get exactly the same behaviour if I try to render an image to the surface.

This functionality is definately working in the older version of unDelphiX that I am using, and no longer works in 1.0.7. (I've verified this on more than one computer)

Thanks for the suggestion regarding storing of images. I'll look into putting together something like that in the future. (No time to revise my image storage system at the moment)

Speeeder
02-09-2006, 11:13 AM
Okay, I'll try to write this down... AGAIN...

Wrong drawing code:

DXDraw.Surface.Draw&#40;0,100,Rect&#40;0,0,Surface.Width,S urface.Height&#41;,Surface&#41;;

Right drawing code:

DXDraw.Surface.Draw&#40;0,100,Rect&#40;0,0,Surface.Width,S urface.Height&#41;,Surface,False&#41;;

Logically right drawing code:

DXDraw.Surface.Draw&#40;0,100,Surface, False&#41;;

And as I've said before... In hardware mode, a SURFACE's transparent colour will be the top-left pixel's colour. You have a 1 colour surface, so the top-left pixel's colour is the same as all of the image. Thus it's totally transparent. With the upper drawing procedures it won't draw with transparency.

For the upper geometry, you need to know the points of the rectangle, and the point(s) of the object(s) you wish to see if they're intersecting the rectangle. The rectangle's points - you rotate those too.

Evil_Toaster
02-09-2006, 05:13 PM
Alright, look, I'm not looking for an endless war here. So here's my last attempt to explain what's happening:

Old Version in which surfaces render correctly:
http://etgames.q.org.za/tmp/unDelphiX/unDelphiX-Old.zip

New version in which surfaces no longer render correctly:
http://etgames.q.org.za/tmp/unDelphiX/unDelphiX-07a.zip

Updated prototype project which uses the drawing method you suggested:
http://etgames.q.org.za/tmp/Prototype3.zip

The problem I'm having has nothing to do with transparencies. In software, rendering works, in hardware, it doesn't. If you switch to hardware, the surface is still rendered to the DXDraw surface, you can see a blue surface, but any rendering TO the surface before plotting it to the DXDraw surface does not work.

Thus, in software, you can see the surface changing colour because drawing to the surface is working. In hardware, the surface stays one colour because drawing to the surface is failing.

Speeeder
02-09-2006, 11:50 PM
I see... My bad, sorry. I'm too sleepy to think now, but a quick and dirty solution could maybe be that you draw a fully blue surface on a second, black surface with alpha. Haven't tested that. But really, I don't think any drawing is needed for a per-pixel hit check...

ijcro
14-09-2006, 08:02 PM
Ahoy All!

Problem there overwritten in not problem.

How imagine works?

You use code:


Inc&#40;Temp,10&#41;;
Surface.Fill&#40;Temp&#41;;
DXDraw.Surface.Draw&#40;0,100,Rect&#40;0,0,Surface.Width,S urface.Height&#41;,Surface&#41;;


But instance of surface is constant, and it is passed into texture stack. And there is check – is image changed by instance? No and your changes no accepted! This can works in software mode as well, all images recently rebuild there, but in hardware no for save of time. You have to use other strategy for drawing like


Inc&#40;Temp,10&#41;;
Surface.Fill&#40;Temp&#41;;
DXDraw.ClearStack; &#123;there is force clear texture stack, but speed rapidly fall down&#125;
DXDraw.Surface.Draw&#40;0,100,Rect&#40;0,0,Surface.Width,S urface.Height&#41;,Surface&#41;;

And small hook is there. Function is supported in prepared version…coming soon 1.08…

I can release patch like 1.07b in short time.

ijcro
21-09-2006, 07:42 PM
Patch as 1.07b was release.
Regards