GUI library for D

Nick Sabalausky a at a.a
Wed Apr 6 00:18:57 PDT 2011


"Adam D. Ruppe" <destructionator at gmail.com> wrote in message 
news:ingldk$1r9$1 at digitalmars.com...
> Nick Sabalausky wrote:
>> The width is statically-known, so the compiler can optimize the
>> index calculation from multiplication to shifts when appropriate.
>
> One concern I have here is will it be an incompatible type with
> dynamic widths? i.e. an image loaded from a file? (I'll be hopefully
> cleaning up my png and bmp loaders in the near future to use with
> this.)
>
> Maybe it could use an interface to smooth that over.
>

Anything that provides an abstraction for manipulating individual pixels 
needs to be *ultra* fast, zero overhead. So I don't think it would be a good 
idea to let any vtable overhead get into there. In fact, even the overhead 
of a function call is a big problem and the pixel-access routines really 
need to be inlined. I *think* with the class being final, calls to the 
pixel-access routines can have their vtable indirection optimized away and 
*should* be inline-able (if not, then they really should be turned into 
string mixins to force the inlining - although maybe not if it's *just* for 
toying around). But my understanding is that making the functions part of an 
interface will force the vtable overhead and definitely inhibit inlining.

My thought is this: Functions which take an Image should be templated on the 
image's type. Then, the image types can work the way ranges work: by using 
what's essentially compile-time duck-typing. Except, I hate the laxness of a 
duck's structural typing, so all the image types would include a static 
dummy variable named something like "_this_is_a_type_Image_", and the 
isImage template would check for that.

Then, some sort of adapter or converter could be created to go back and 
forth between static-sized and dynamic-sized images for any cases where you 
might really need to skip the templating (possibly at the cost of some 
speed, though). And that should take care of everything.

Actually, that pretty much describes the gist of an article I'm planning to 
write for that D article competition. Of course, if you see any fundamental 
problems with this, feel free to let me know :)

>
> But, I'm getting off the point. Indexed images can be played with
> later.
>

I'm nostalgic for palette-cycling effects :) VGA was great. (And I get 
downright excited over EGA.)

>> A major improvement, of course, would be to replace the GDI
>> stuff with Direct3D 7 or 8 (there's no real need to require
>> anything beyond those versions).
>
> Now, I don't want to get too fancy.. especially since I've never
> used directx, not even for hello world, and I don't think
> performance will be too big of a deal.
>

GDI is notoriously bad for performance. It's good enough for desktop 
applications, and it's fine if you're just going to draw something once and 
display it. But for anything that's continuously drawing new frames GDI has 
never really cut it. That's what DirectX was made for in the first place. 
(Actually, that's what WinG was made for but even that *still* wasn't quite 
good enough and it was quickly replaced by the "Game SDK" which was renamed 
to DirectX at version 2. Now I feel old.) Although that said, I did once 
make a pretty nifty GDI PacMan in C#: 
http://www.semitwist.com/download/GDIPac.zip

I have used DirectX, although it's been awhile (way back in my C/C++ days), 
and it was back when DirectDraw was still relevant. Direct3D is now the way 
to do high-performance 2D and 3D (unless you're requiring a new enough 
version of DirectX that you'd have access to Direct2D, but I don't remember 
which version introduced Direct2D.) Of course, OpenGL can be used too, but 
AIUI the average-Joe's Windows computer isn't likely to have reasonably good 
OpenGL drivers (unless my info's just out-of-date).

> But, I did do something similar with my D1 game lib. It uses
> SDL for most its stuff, but I found that to be dreadfully slow
> with anything beyond the simple, so I changed the output to
> use OpenGL, set up to do 2d output. Only difference client code
> wise was I had was I had to stop using putPixel - it was even
> slower!

Yea, any individual-pixel-access function is going to be overly slow unless 
it's completely inlined and highly optimized. And even then, things like 
line drawing, fills and image blitting are still better off skipping the 
individual-pixel-access routines (unless optimizing compilers have gotten 
far better than I'd even thought).

If you haven't already, any of the old books by Andre' LaMothe or Michael 
Abrash are fantastic resources for high-performance graphics processing in 
software. Oldies, but goodies.

But anyway, I don't know exactly how you were going about it, but the way to 
do this in OpenGL or DirectX would be to do all your drawing to a texture 
buffer (using the same pixel-format as the current screen mode), and attach 
that texture to couple of hardware-drawn triangles arranged in a rectangle. 
Then optionally wait for the VSync, and then render (or maybe better yet, 
render to a back buffer, wait for VSync, and then flip).

> But sprite rotation and blitting, lines, shapes, all that
> stuff was sped up by huge, huge amounts.
>
> And I got free alpha blending and gradients! It was a huge
> benefit, ultimately very worth it.
>

Yup. Hardware acceleration (ie, OpenGL or DirectX) is totally the way to go 
whenever possible.

>
> So yeah, I think I would like to do something similar here too,
> but since I have no experience with D3D it'll have to be low
> on my own priority list. But I like the idea!
>

I'd love to do some of this. It's been FAR too long since I've even touched 
anything remotely game-related. Maybe I'll have to make time for it. We'll 
see.

>
> In DOS, the pattern I used was something like this:
>
> // initialize the program and the screen
>
> while(still_going) {
>    if(kbhit()) {
>       char c = getch();
>       // handle c
>    }
>
>    // draw your frame
>
>    wait_for_next_frame();
> }
>
> // clean up
>

You were using getch() in a game? Much better to hook the keyboard interrupt 
handler, watch the scan codes, and update an array or bitfield containing 
the current up/down state of each key. Then the game code just checks the 
key status array. Much more responsive that way, and you get control over 
key-up/key-down.

>
>
> My D1 game code did something similar, but the loop was replaced
> by a virtual class method and the wait was done by a system timer.
> (And something too cool: instead of
> checking the keyboard, it checked a joystick or keyboard, pretending
> it always had a Playstation controller.
>
> if(buttonWasPressed(triangle, 1)) // the 1 is player #1
>   // react
> if(buttonIsDown(square))
>   // react
>
> Then, if you had a controller plugged into the computer, it'd
> just work, or you could be boring and use the keyboard, with
> the keys mapped to the playstation names.
>

I prefer to use a more general key mapping system: On one side, you have 
every key and button your game recognizes. On the other side you have things 
like "jump", "walk left", "walk right", "run", "context-sensitive action 
button", etc. Then you just connect the dots between the two sides. Makes 
user-configurability very easy. I suppose that's very similar to yours 
though, just using game-specific names instead of "triangle"/"square"/etc.

BTW, this is something I'd use DirectInput for. The Win32 event system is 
about as suitable for game input as the GDI is for game graphics.

> One of the IMO best parts was that the controller interface was
> also network transparent, so any game that stuck to it could
> be played online too. The way that'd work is the server sends
> out state to everyone, random number seeds, etc. Then, all
> controls were lagged a frame or two. If you pressed a button, it'd
> transmit your action to the other players along with a timestamp.
>
> Frame 20, you hit circle. It sends down the network "on frame 22,
> send the event that player #2 pressed circle". Then, since everyone
> has the same program and the same initial state, everyone sees
> the same thing, without the game itself needing to know anything
> about the network.
>
> Of course, the lag could be slightly annoying, but meh, didn't
> bother me. Besides, I can't complain too much about free networking!
>

That's interesting. Not the way to do Halo obviously, but for simple stuff I 
can see how that would be nice and convenient.

>
> Man, I wish I had more time to make games.
>

I'd say "ditto", but that'd be one hell of an understatement. I used to be a 
huge regular ("Abscissa") over on the xgames3d forums (now xgamestation.com) 
even way back before it moved over to hardware. Heck, I even have one of the 
first usernames assigned there :) God I miss game dev. Don't want to join 
one of the established companies though. Don't like many of the directions 
the industry's been going the last ten years or so. And then dealing with 
crunches and studio closings, all just to make the next hollywood-wannabe 
story/cutscene-fest or military shooter #5421 or some IP-tie-in...forgeddit.

>
> Wow, I'm off topic again. Anyway, for the event loop here, I'm
> thinking something similar to how std.concurrency does it might
> be a good approach: a bunch of delegates, matched to events by
> their signature. Simple signatures could be accepted as well
> as fancier ones.
>
> // need to make a simple window separately here so it is persistent
> auto win = new DrawableWindow(width, height);
>
> // still use an image the same way. Alternatively, DrawableWindow
> // could implement the same interface as Image, probably double
> // buffering internally anyway, so pretty much same implementation.
> auto image = new Image(width, height);
>
> window.eventLoop(
>   50, // first parameter is the timeout. can be 0 to only react
>       // to events, but putting one in gives you an easy to use
>       // frame pulse for drawing
>   (int keyPressed) { // might be a struct KeyEvent rather than int
>        // react to the key
>   },
>   (int x, int y, int mouseButton) {
>        // react to a mouse click
>   },
>   () { // no params means your timeout happened, time to draw
>        // draw your frame...
>
>        image.display(win); // this overload uses the existing
>                            // window and returns immediately
>                            // rather than making and waiting
>   }
>   // might also offer a platform specific msg, wParam, lParam
>   // so you could in theory do all of Win32 based on this same
>   // framework
> );
>
>
>
> This way, you could whip something up right there and have a
> simple little game all inside a couple dozen lines of main()
> without needing any monster libraries in your project.
>
> Wow, I think I really like this...

Interesting. I've had a lot of thoughts about game loops flying around in 
the back of my mind too, but talking about any of it will have to wait for 
another time, gotta get back to some other stuff right now...





More information about the Digitalmars-d mailing list