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