[A5] ideas on how to handle timers/user events/expose events in a widget class.
axilmar

hi all.

In my gui library, I'd like to allow widgets to start timers, put user events in an event queue, and put custom expose events in the associated display's event queue.

In order to allow widgets to start timers, widgets must have an ALLEGRO_EVENT_QUEUE* member in order to register the timer's event source to the queue.

In order to allow widgets to send user events, widgets must have an ALLEGRO_EVENT_SOURCE* member in order to use the function 'al_emit_user_event'.

In order to allow widgets to put custom expose events, widgets must have an ALLEGRO_DISPLAY* member (according to this thread).

Now my question is this: how to avoid having all these members in my widgets? is there a way in Allegro 5 to have a single pointer to <something> in a widget and do all the above? if so, what is this <something>? a queue? an event source?

How do you handle relevant cases in your GUI libraries?

Thomas Fjellstrom

I didn't like passing allegro events directly to my object based framework. Instead I have a bunch of methods that handle different events. The event objects themselves do take an ALLEGRO_EVENT to populate itself though.

Matthew Leverton
axilmar said:

is there a way in Allegro 5 to have a single pointer to <something> in a widget and do all the above? if so, what is this <something>? a queue? an event source?

No, I'm not really sure how you expect that it would work in C. Or maybe I don't even understand what you're asking.

I haven't done much with Allegro events in my GUI. I don't use them internally because I use my own function-based event system. However, externally, I will probably give the programmer an option to use Allegro events.

Regarding a display pointer, I don't associate widgets directly with displays. If I really needed to know what display a widget was on, I'd have to travel up the parent nodes until I got to the root object, and then check which display owned it by a display/root pair I maintain.

For example, on a mouse move event, I cycle through each root widget owned by the display. Each widget asks itself "what widget is at X,Y" recursively until it finds its top most widget. So I always know what the display is without explicitly storing it per widget.

But I haven't implemented any optimization. If I ever get to that point, I may rethink a few things.

axilmar

Matthew, how does your GUI library handle redrawing? do you redraw the whole GUI at each frame update?

If I wanted to use your GUI not for a game, but for an application, I would not like to have my GUI being redrawn at every frame. So, in order to achieve this, I would have to manage expose events. But, in order to send expose events from widgets, I need to have a pointer to a display in each widget (or an association between a widget and a display, like the one you describe).

Now, normally, this isn't a problem. But, when I want to handle timers in widgets, each widget should also have a pointer to the event queue that will be associated with the timer.

Finally, if I want my widget to send events to an event queue, each widget must have a pointer to an event source.

Let me repeat the above in short form:

  • if I want my widgets to send expose events, each widget must have access to an ALLEGRO_DISPLAY pointer.


  • if I want my widgets to handle timer events, each widget must have access to an ALLEGRO_EVENT_QUEUE pointer, so as that the timer that is created by the widget is registered to the event queue.


  • if i want my widgets to send user events, each widget must have access to an ALLEGRO_EVENT_SOURCE pointer.

I want to avoid all the above. I want to avoid this:

#SelectExpand
1class Widget { 2public: 3 ALLEGRO_DISPLAY *getDisplay() const; 4 5 void setDisplay(ALLEGRO_DISPLAY *d); 6 7 ALLEGRO_EVENT_QUEUE *getEventQueue() const; 8 9 void setEventQueue(ALLEGRO_EVENT_QUEUE *q); 10 11 ALLEGRO_EVENT_SOURCE *getEventSource() const; 12 13 void setEventSource(ALLEGRO_EVENT_SOURCE *s); 14 15private: 16 ALLEGRO_DISPLAY *m_display; 17 ALLEGRO_EVENT_QUEUE *m_queue; 18 ALLEGRO_EVENT_SOURCE *m_event_source; 19};

So, my question is how do you handle this problem.

By the way, the above problem leads me to think that the Allegro 5 event system is not designed correctly. The core of the problem is al_emit_user_event. What is wrong with it is that user events can only be emitted on an event source. The right approach would be to put events in the event queue. If A5 was like that, I would only need this:

class Widget {
public:
     ALLEGRO_EVENT_QUEUE *getEventQueue() const;
     void setEventQueue(ALLEGRO_EVENT_QUEUE *q);

private:
    ALLEGRO_EVENT_QUEUE *m_queue;
};

But perhaps I am wrong in this; perhaps there is a way to do what I ask, and that's why I am asking for insight on this.

Matthew Leverton

What if you want to send events to multiple queues? That's why Allegro is designed the way it is, so I wouldn't call it incorrect. Essentially, Allegro is providing the convenience layer to the owner of the event source, such that it simply has to emit an event into its source and not worry if anybody is listening.

However, I don't think it would be hard to add al_push_event(ALLEGRO_EVENT_QUEUE *queue, ALLEGRO_EVENT *event); to A5's internals as an alternative way to populate a queue.

I redraw the entire GUI double buffered, without optimization, on every frame. This is mostly because I'm too lazy to do anything else, as opposed to any master plan. I cannot imagine that my GUI would ever end up being useful for high speed, full screen games. It is primarily designed for tool building and not real time games, so I don't really care.

axilmar

What if you want to send events to multiple queues?

Is there a use case for such a feature?

Quote:

such that it simply has to emit an event into its source

So widgets need to be aware of one or more event sources, if they want to send events themselves.

But widgets also need to know of a queue to register timers to, and I think the Allegro 5's event source abstraction breaks at this point, at least for the GUI.

Quote:

I cannot imagine that my GUI would ever end up being useful for high speed, full screen games. It is primarily designed for tool building

Actually, it is the tools that need the expose optimization, not games. Full screen games will need to redraw the whole GUI at every frame anyway.

Matthew Leverton
axilmar said:

Is there a use case for such a feature?

I have made use of multiple things listening to the same event source on Allegro, if that's what you mean. And with HTML/JS, I do it a lot.

Could you simply pass an event source to the widget? i.e., void Widget::setEventSource(ALLEGRO_EVENT_SOURCE *s); If it is 1:1 with an event queue, it's the same thing, from the widget's perspective. If you build your source like:

then your widget can push into the event source and your listener can just use the queue.

I don't see why you think it breaks down. You need an extra object somewhere, of course, but conceptually it isn't a limitation.

axilmar

Widgets also need to know the queue, because they need to create timers. Knowing the event source is not enough.

Furthermore, there is also the case of expose events: the widget needs to know the display's event source as well.

Matthew Leverton
axilmar said:

Widgets also need to know the queue, because they need to create timers. Knowing the event source is not enough.

You can reverse responsibilities:
al_register_event_source(widget->getTimerEventSource(), queue);

But to be honest, I'm not really sure who is creating the timer and who is listening...

Quote:

Furthermore, there is also the case of expose events: the widget needs to know the display's event source as well.

Well, really you just need the display pointer. And that's true no matter how you want to implement it.

Anyway, see if this patch helps:


Index: include/allegro5/events.h
===================================================================
--- include/allegro5/events.h	(revision 15029)
+++ include/allegro5/events.h	(working copy)
@@ -260,6 +260,8 @@
 AL_FUNC(bool, al_wait_for_event_until, (ALLEGRO_EVENT_QUEUE *queue,
                                         ALLEGRO_EVENT *ret_event,
                                         ALLEGRO_TIMEOUT *timeout));
+AL_FUNC(void, al_push_event, (ALLEGRO_EVENT_QUEUE *queue,
+                              const ALLEGRO_EVENT *event));
 
 #ifdef __cplusplus
    }
Index: src/events.c
===================================================================
--- src/events.c	(revision 15029)
+++ src/events.c	(working copy)
@@ -565,8 +565,11 @@
    _al_mutex_unlock(&queue->mutex);
 }
 
+void al_push_event(ALLEGRO_EVENT_QUEUE *queue, const ALLEGRO_EVENT *event)
+{
+   _al_event_queue_push_event(queue, event);
+}
 
-
 /* contains_event_of_source:
  *  Return true iff the event queue contains an event from the given source.
  *  The queue must be locked.
Index: examples/ex_user_events.c
===================================================================
--- examples/ex_user_events.c	(revision 15029)
+++ examples/ex_user_events.c	(working copy)
@@ -65,6 +65,13 @@
    al_init_user_event_source(&user_src);
 
    queue = al_create_event_queue();
+
+   user_event.user.type = 666;
+   al_push_event(queue, &user_event);
+   if (al_get_next_event(queue, &event)) {
+      printf("Got an event of type %d\n", event.type);
+   }
+
    al_register_event_source(queue, &user_src);
    al_register_event_source(queue, al_get_timer_event_source(timer));

It adds al_push_event(*queue, *event). Note that it makes a copy of the event, so normal usage would be to declare an ALLEGRO_EVENT on the stack and pass a reference to that.

Peter Wang wrote all the event stuff, so you should probably bring it up on the development forum here if you want to see something like that added. I'm not 100% sure the patch above is correct.

Personally, I would be in favor of having such a function if only for the sake of completeness.

axilmar

Thanks a lot Matthew.

But to be honest, I'm not really sure who is creating the timer and who is listening...

A timer is required when a widget needs to perform a timely action such as:

  • animating a text cursor.

  • scrolling a widget's contents when the mouse is at the edges of the widget.

So, it is a widget that creates the timer, and it is also the widget that responds to the timer events.

EDIT:

After a little bit of thinking, I will implement the above by:

  • using a custom event for expose.

  • creating an internal event source object which will be registered with the queue used by the main loop.

For example:

rootWidget->connect(mainQueue);

A function which puts an event in a queue is not really required. All that is required is a custom event source which is bound to a queue.

When a widget needs to create a timer, it will register the timer with the queue given above.

When a widget needs to emit an expose event, a custom expose event will be emitted to the internal event source registered to the queue given above.

This event source can also be used to put any event to that queue.

Edgar Reynaldo

I don't think this is the best way to go about it. Why not make a global timer used by the entire GUI? Set it to the monitor's refresh rate and forget it. Have the user pass the delta time into a widget's update function after reading the timer events. The user will most likely create a timer at the refresh rate of the monitor anyway, and then they can just pass the delta time in.

class WidgetBase {
public :
   virtual void Update(double dt) {}
};


if (event.type == ALLEGRO_EVENT_TIMER) {
   gui.Update(SECONDS_PER_TICK);
}

That is so much simpler than making every widget own a timer, and event queue.

As for your expose events, it looks like Matthew's patch should take care of that for you. Wouldn't the display have to be using double buffering to make that work for you though? If it used page flipping, you would be forced to fully redraw everything every frame anyway. My A4 GUI uses a BITMAP buffer for each WidgetHandler, and it uses dirty rectangles, only updating the widgets that need to be redrawn. So it blits its buffer every time you call display, but doesn't necessarily redraw every widget.

Matthew Leverton

I guess I have implemented timers for my blinking cursor, but I think I just hacked it internally. Generally, I think I would do something like Edgar is proposing.

axilmar

Why not make a global timer used by the entire GUI?

1) Because Widgets shouldn't have update() functions. Remember, you spoke of clean and organized APIs in another thread.

2) Because I wouldn't want to dispatch the timer event to every widget in a widget tree. I'd like to dispatch it to the widget that created the timer.

3) Because different widgets would have different timer periods, often incompatible between them.

4) because I don't like the user of my library to not get blinking cursors if he/she doesn't handle the timer event in his/her event queue.

Quote:

As for your expose events, it looks like Matthew's patch should take care of that for you.

Well, if it becomes official Allegro 5 code, then maybe I'll use it. But, as I have thought about it, it is not really needed.

Quote:

Wouldn't the display have to be using double buffering to make that work for you though? If it used page flipping, you would be forced to fully redraw everything every frame anyway.

I don't know what A5 currently does internally, but expose events work as they are. So, my code will look like this:

switch (event.type) {
    case ALLEGRO_EVENT_DISPLAY_EXPOSE:
    case MYGUILIB_EVENT_DISPLAY_EXPOSE:
        ...; 
}

Quote:

My A4 GUI uses a BITMAP buffer for each WidgetHandler, and it uses dirty rectangles, only updating the widgets that need to be redrawn. So it blits its buffer every time you call display, but doesn't necessarily redraw every widget.

1) There is no need to use bitmaps in A4 when using a dirty rectangle system. I've done GUIs in A4 with dirty rectangles that simply told widgets to redraw the dirty rectangles.

2) I am not planning to use a dirty rectangle system, it's not required. All I want is to redraw a specific part of the GUI and not the whole GUI.

Here is how I decided to do it: A class GUI will hold the global context of a GUI, including display and queue. Then, the root widget will have to be initialized with this GUI class, like this:

GUI gui(display, queue);
Widget root;
gui.setRoot(root);

The GUI class will contain lots of other globals that a GUI needs.

Edgar Reynaldo
axilmar said:

1) Because Widgets shouldn't have update() functions. Remember, you spoke of clean and organized APIs in another thread.

If you want animated widgets, they should have an update function. Having a default base class update function that does nothing adds zero overhead. It is a perfectly clean and organized solution to have only the widgets that need delta time do something with it. If your user is too lazy / brain dead to use a timer and pass SECONDS_PER_TICK to a top level gui widget, that's really pathetic and they deserve for animations not to work.

axilmar said:

2) Because I wouldn't want to dispatch the timer event to every widget in a widget tree. I'd like to dispatch it to the widget that created the timer.

How do you plan to distinguish which widgets get which events? Isn't it better to let them decide whether they want to deal with an event?

axilmar said:

3) Because different widgets would have different timer periods, often incompatible between them.

Are you really going to need widgets that have timers faster than the refresh rate of the monitor?. I imagine widgets like that would be pretty rare and in that case then they should have their own timer built in. You would still need a way to detect widgets like this and have them check their inputs more often than the rest of the widgets.

axilmar said:

4) because I don't like the user of my library to not get blinking cursors if he/she doesn't handle the timer event in his/her event queue.

So instead of asking the user to do one tiny little thing, you would make your library more complicated and bloated by making all of your widgets have their own timers?

axilmar said:

1) There is no need to use bitmaps in A4 when using a dirty rectangle system. I've done GUIs in A4 with dirty rectangles that simply told widgets to redraw the dirty rectangles.

In A4, buffers are quite useful, and prevent the need for an entire panel of widgets to be redrawn at once. Buffers also allow the user to redraw the entire gui at once if part of the screen has been changed.

axilmar said:

2) I am not planning to use a dirty rectangle system, it's not required. All I want is to redraw a specific part of the GUI and not the whole GUI.

What are you going to do if A5 is using page flipping internally? The screen will get out of sync with what it is supposed to be displaying.

axilmar

If you want animated widgets

Animation can be achieved on a per widget basis. There is no need for a global system, because only one widget will be animated at a time.

Quote:

Having a default base class update function that does nothing adds zero overhead.

The update mechanism adds overhead. Having a timer, i.e. a thread, wake up 60 frames per second, i.e. every 16 milliseconds, when all I want is a blinking cursor every one second.

Quote:

It is a perfectly clean and organized solution to have only the widgets that need delta time do something with it.

It is not clean to have a function in the root class that only a few widgets will use.

Quote:

If your user is too lazy / brain dead to use a timer and pass SECONDS_PER_TICK to a top level gui widget, that's really pathetic and they deserve for animations not to work.

The user shouldn't have to initialize a timer in order for his GUI to work. It's better to automate the process.

Quote:

How do you plan to distinguish which widgets get which events? Isn't it better to let them decide whether they want to deal with an event?

Widgets that create timers will register themselves to their GUI object in order to receive timer events.

Quote:

Are you really going to need widgets that have timers faster than the refresh rate of the monitor?.

Not faster, slower. But when I need a blinking cursor with speed = 1000 milliseconds, 1000 is not divided accurately with 60.

Quote:

So instead of asking the user to do one tiny little thing, you would make your library more complicated and bloated by making all of your widgets have their own timers?

I am against making the user do anything except the absolutely necessary required to get his gui going. If that means making the library more complicated for me, then so be it. It will be easier for the user though.

Quote:

In A4, buffers are quite useful, and prevent the need for an entire panel of widgets to be redrawn at once.

Buffering is useless when widgets need to be redrawn. Why waste resources when the screen needs to be updated? drawing onto a buffer and then blitting the buffer to the screen spends resources that wouldn't be spent if the drawing happened directly to the destination bitmap (and then buffers were switched). And if you use memory bitmaps, you lose accelerated drawing.

Quote:

Buffers also allow the user to redraw the entire gui at once if part of the screen has been changed.

The same effect can be achieved by double buffering.

Quote:

What are you going to do if A5 is using page flipping internally? The screen will get out of sync with what it is supposed to be displaying.

Not at all. The event EXPOSE contains the area that is to be updated, and so I will redraw the appropriate widgets inside that area, as needed.

Edgar Reynaldo
axilmar said:

Buffering is useless when widgets need to be redrawn. Why waste resources when the screen needs to be updated? drawing onto a buffer and then blitting the buffer to the screen spends resources that wouldn't be spent if the drawing happened directly to the destination bitmap (and then buffers were switched). And if you use memory bitmaps, you lose accelerated drawing.

If you draw directly to the screen, you will have flickering images whenever an object needs to draw over itself or have it's background redrawn. You can use memory or video bitmaps if you like, I never said the user was forced to use one or another.

axilmar said:

Edgar said:

Buffers also allow the user to redraw the entire gui at once if part of the screen has been changed.

The same effect can be achieved by double buffering.

Why would you want to redraw every single widget when you could just blit a stored copy of the gui panel into place as necessary?

axilmar said:

Not at all. The event EXPOSE contains the area that is to be updated, and so I will redraw the appropriate widgets inside that area, as needed.

The EXPOSE event may tell you what part of the screen is dirty, but if A5 is using page flipping and you update one page and flip, that update is lost on the second page.

axilmar said:

The update mechanism adds overhead. Having a timer, i.e. a thread, wake up 60 frames per second, i.e. every 16 milliseconds, when all I want is a blinking cursor every one second.

Premature optimization anyone? The overhead added by an update function is completely negligible. If your timer is running at 60Hz, then you will never be more than 1/60 of a second behind the actual time you wanted. No human can even tell the difference between 60/60 and 61/60 of a second.

axilmar said:

It is not clean to have a function in the root class that only a few widgets will use.

Of course it is. Only the widgets that actually use the update function will ever redefine it. The rest of the widgets don't have to do a single thing with it.

I really just disagree with you on this. This all works perfectly fine in my GUI, and adding in 5-10 lines of timer code is not too much to ask a user to do if they want animations.

Thomas Fjellstrom
axilmar said:

Animation can be achieved on a per widget basis. There is no need for a global system, because only one widget will be animated at a time.

Why make the limitation of one animation at a time? Sure you don't need a global animation system, but Qt manages it with Animation classes that you tie to various properties on a widget, and the Animation class handles the animation (which I assume registers a timer event for itself).

axilmar

If you draw directly to the screen, you will have flickering images whenever an object needs to draw over itself or have it's background redrawn.

You also have flickering if you use per-widget bitmaps - the drawing operation then becomes the blitting of the widget bitmap to the screen.

Buffering widgets does not work like that (i.e. one bitmap per widget). It works with a background bitmap that all widgets are drawn onto, and then the final result is displayed, either via page flipping or blitting.

Quote:

Why would you want to redraw every single widget when you could just blit a stored copy of the gui panel into place as necessary?

Because bitmaps take a lot of memory, and you will usually have to redraw the bitmap anyway.

Quote:

The EXPOSE event may tell you what part of the screen is dirty, but if A5 is using page flipping and you update one page and flip, that update is lost on the second page.

Perhaps. I haven't seen it though, in my current demos. Which means that A5 doesn't use page flipping, it just blits the back buffer to the front buffer.

Quote:

Premature optimization anyone? The overhead added by an update function is completely negligible. If your timer is running at 60Hz, then you will never be more than 1/60 of a second behind the actual time you wanted. No human can even tell the difference between 60/60 and 61/60 of a second.

It's noticeable, because there are other delays too: the thread that implements the timer, the putting of event in the queue, the waking up of the thread etc. These may amount to more than 20 milliseconds, especially on Windows where the default timer accuracy is 10 milliseconds.

The main problem though is that a thread waking up every 16 milliseconds. It's a resource hog.

Quote:

Of course it is. Only the widgets that actually use the update function will ever redefine it. The rest of the widgets don't have to do a single thing with it.

But the whole widget tree will have to be notified every 16 milliseconds of the timer. Isn't that a performance issue?

Quote:

I really just disagree with you on this. This all works perfectly fine in my GUI, and adding in 5-10 lines of timer code is not too much to ask a user to do if they want animations.

Well, it may work for you, but if you try to do a simulation where the simulation state is updated every 100 milliseconds, and you have a thread waking up every 16 milliseconds, you will notice many hickups in the simulation thread.

I want to avoid any possible performance issues in my gui, since I'd like to use it in my simulations. Perhaps your needs are different, but even if they are, I don't see how saving up resources is a bad thing.

Why make the limitation of one animation at a time?

I will not make such a limitation. I was just referring to the most common use cases for timers on a gui, i.e. blinking cursors and automated scrolling views.

EDIT:

According to al_flip_display documentation:

Quote:

Pointers to the special back and front buffer bitmaps remain valid and retain their semantics as back and front buffers respectively, although their contents may have changed.

So the expose event works because the back and front buffers remain back and front buffers after a flip.

Thomas Fjellstrom
axilmar said:

Because bitmaps take a lot of memory, and you will usually have to redraw the bitmap anyway.

Nope. Qt4 manages to speed up a lot of stuff using a back buffer for every widget. Turns out that much of the time, most widgets don't change much. And redrawing every complex widget every frame is a massive chunk of overhead. Primitives, especially ones with fancy features (Qt supports a wide range of Vector like drawing features in its primitive drawing api) are slower than you think they are. Even in Allegro 5 primitives are rather slow. Most of it is overhead in the graphics driver.

Using native windows for widgets also speeds things up, so it stopped doing that by default. They do use the platform's native drawing routines when possible to draw those widgets though, so themes tend to work.

axilmar

Nope. Qt4 manages to speed up a lot of stuff using a back buffer for every widget.

I doubt Qt4 has one bitmap per widget. Most probably, it does some kind of caching, depending on the video card memory size and on the main memory size.

Using bitmaps for caching is ok, as long as the bitmaps are in video memory, or the cache is small. Otherwise, it is a huge drain in resources.

Imagine a desktop of 1650 x 1280 in 32-bit color, with 10 windows open full screen, and one of the browsers having 20 tabs. Not an unusual setup, right?

If each window and each browser tab has one bitmap, we are talking about 30 bitmaps, which will consume 30 x 1650 x 1280 x 4 = 253440000 bytes, i.e. 241 MB.

And, in the above example, I am not counting bitmaps of controls.

Thomas Fjellstrom

If a web browser doesn't have a backbuffer for each tab, switching tabs would be hella slow.

axilmar said:

If each window and each browser tab has one bitmap, we are talking about 30 bitmaps, which will consume 30 x 1650 x 1280 x 4 = 253440000 bytes, i.e. 241 MB.

Guess how much memory firefox and chrome use? many hundreds of MB. Even several GB if you let it get that far. I've seen both chrome and firefox use more than 2 GB.

append: though I think the default for Qt4 is to have one backbuffer for the top level window, and the widgets then render to a chunk of that. But each widget has the option of having its own back buffer IIRC.

Matthew Leverton

You cannot do all graphical effects without a buffer. My GUI is a mess regarding this right now, but my goal is to support both buffered (as it does now) and unbuffered (sub-bitmaps or transformations) widgets at the programmer's own discretion.

Peter Wang
axilmar said:

So the expose event works because the back and front buffers remain back and front buffers after a flip

No. As clarified in the more recent version of the docs:

Pointers to the special back and front buffer bitmaps remain valid and retain their semantics as back and front buffers respectively, although their contents may have changed.

(We took out the al_get_frontbuffer() function a while ago, so there is no such thing any more.)

Thomas Fjellstrom

In case you want an example of that "Painter" stuff, I implemented a simple Painter/PaintEngine setup in my Canva5 library. Its rather simple and doesn't do any caching (yet). But adding a cached version of objects would likely speed up the rendering of simple objects significantly (especially if the pixmaps were cached in atlases). The overhead of calling things like al_draw_circle hundreds of times is enormous.

Edgar Reynaldo
axilmar said:

You also have flickering if you use per-widget bitmaps - the drawing operation then becomes the blitting of the widget bitmap to the screen.

Buffering widgets does not work like that (i.e. one bitmap per widget). It works with a background bitmap that all widgets are drawn onto, and then the final result is displayed, either via page flipping or blitting.

That's the kind of buffering I was talking about - my WidgetHandler (gui / panel / window) class has two bitmaps, one background bitmap and one buffer bitmap. The background is redrawn as necessary and then the necessary widgets are drawn, but almost never all of them are redrawn at once. Having a buffer allows the user to change as much of the back buffer as he wants to without having to make every widget redraw itself all over again. Why redraw more than once when you don't have to?

axilmar said:

Because bitmaps take a lot of memory, and you will usually have to redraw the bitmap anyway.
...
If each window and each browser tab has one bitmap, we are talking about 30 bitmaps, which will consume 30 x 1650 x 1280 x 4 = 253440000 bytes, i.e. 241 MB.

And with 2GB being the standard normal amount of memory in a system these days, that's still only about 12% of the systems memory, which isn't that much.

axilmar said:

But the whole widget tree will have to be notified every 16 milliseconds of the timer. Isn't that a performance issue?

1,000 widgets * 60 updates per second = 60,000 function calls per second. That's not all that much for today's processors to handle. And 1,000 widgets is kind of ridiculous anyway, more like one or two hundred. Don't optimize until you identify a bottleneck.

Oscar Giner
axilmar said:

If you draw directly to the screen, you will have flickering images whenever an object needs to draw over itself or have it's background redrawn.

You also have flickering if you use per-widget bitmaps - the drawing operation then becomes the blitting of the widget bitmap to the screen.

You don't get flicker if you implement it property. It's actually how Windows (pre-Aero) works. My old GUI works this way, too (it's a convenient method because it's very efficient when widgets rarely change).

(Append: note that I wouldn't use this method on a modern GUI. It's in general more limited, and if you want that most widgets have cool animations and effects (I'm talking about a GUI designed for use in games), then you just want to render all the widgets every frame).

axilmar said:

Pointers to the special back and front buffer bitmaps remain valid and retain their semantics as back and front buffers respectively, although their contents may have changed.

So the expose event works because the back and front buffers remain back and front buffers after a flip.

As the docs say, the contents may have changed (and the surfaces this bitmaps "point" to may change). Easy test: force Allegro to use the OpenGL driver, and enable "Triple buffering" in your GPU settings (this setting, on both nVidia and ATI, only affects OpenGL). For DirectX, there are 3rd party programs that allow to change the update method (forcing triple buffering for example, or increase the number of back buffers).

axilmar

If a web browser doesn't have a backbuffer for each tab, switching tabs would be hella slow.

Not really. Drawing part of a web page is not that slow.

Quote:

Guess how much memory firefox and chrome use? many hundreds of MB. Even several GB if you let it get that far. I've seen both chrome and firefox use more than 2 GB.

That's allocated memory chunks used by various plugins, Javascript, etc. Not bitmaps.

Quote:

append: though I think the default for Qt4 is to have one backbuffer for the top level window, and the widgets then render to a chunk of that. But each widget has the option of having its own back buffer IIRC.

Exactly. Only selected Qt4 widgets have back buffers enabled by default, in order to minimize memory use.

You cannot do all graphical effects without a buffer.

What kinds of graphical effects do you have in mind?

Pointers to the special back and front buffer bitmaps remain valid and retain their semantics as back and front buffers respectively, although their contents may have changed.

That complicates using Expose events for a GUI. I'd have to think a solution.

That's the kind of buffering I was talking about - my WidgetHandler (gui / panel / window) class has two bitmaps, one background bitmap and one buffer bitmap. The background is redrawn as necessary and then the necessary widgets are drawn, but almost never all of them are redrawn at once. Having a buffer allows the user to change as much of the back buffer as he wants to without having to make every widget redraw itself all over again. Why redraw more than once when you don't have to?

Good waste of resources, Reynaldo :-).

Quote:

And with 2GB being the standard normal amount of memory in a system these days, that's still only about 12% of the systems memory, which isn't that much.

Considering all the other things that run in a system, it is.

Quote:

1,000 widgets * 60 updates per second = 60,000 function calls per second. That's not all that much for today's processors to handle. And 1,000 widgets is kind of ridiculous anyway, more like one or two hundred. Don't optimize until you identify a bottleneck.

You are master in wasting resources :-).

You don't get flicker if you implement it property.

You do get flickering, although less if you draw stuff quickly. If you blit bitmaps directly on the screen, then you get flickering.

Quote:

It's actually how Windows (pre-Aero) works.

No, it is not. Aero works like this: the top-level windows have a texture inside the video ram, and all drawing of those windows and of children windows goes into that texture. Then Windows renders this texture on the screen, using two buffers.

It's not like Reynaldo's system that has two bitmaps per widget.

Oscar Giner
axilmar said:

You do get flickering, although less if you draw stuff quickly. If you blit bitmaps directly on the screen, then you get flickering.

Of course you don't. You basically draw the widget in a buffer, and blit that buffer to the screen (overwriting the previously drawn widget). Where do you see the flicker in this? (if you want a real example: download my tetris clone, go to new game, and see if you see any flicker in the GUI there)

Quote:

No, it is not. Aero works like this: the top-level windows have a texture inside the video ram, and all drawing of those windows and of children windows goes into that texture. Then Windows renders this texture on the screen, using two buffers.

I said pre-Aero. Believe me I've worked with this, and in Windows you need to double buffer your widgets or you get flickering when they're redrawn (again, to be clear, this is true only if not using Aero). (As for Qt: while widgets may not be double buffered, Qt windows are always double buffered so you can resize windows without getting flickering. This is why there's no need for widgets to be double buffered other than as a cache for not having to redraw complex widgets).

Append:

Quote:

That's allocated memory chunks used by various plugins, Javascript, etc. Not bitmaps.

I don't know about Firefox since I don't use it, but Opera certainly stores each tab in its own bitmap. This allows for instant tab switching no matter how complex a page is, even on an ancient computer (as long as it has enough RAM).

axilmar

Of course you don't. You basically draw the widget in a buffer, and blit that buffer to the screen (overwriting the previously drawn widget). Where do you see the flicker in this? (if you want a real example: download my tetris clone, go to new game, and see if you see any flicker in the GUI there)

I was talking about the bitmaps of widgets, in the context of one bitmap per widget, as per Reynaldo's implementation. If you draw all the widgets in a buffer, then you don't get flickering.

Quote:

Believe me I've worked with this, and in Windows you need to double buffer your widgets or you get flickering when they're redrawn (again, to be clear, this is true only if not using Aero). (As for Qt: while widgets may not be double buffered, Qt windows are always double buffered so you can resize windows without getting flickering. This is why there's no need for widgets to be double buffered other than as a cache for not having to redraw complex widgets).

You're right. I've implemented a custom widget library for a project under Win32. The default drawing on an Window HDC is to draw directly in the VRAM, clipped by the HDC's update region.

Quote:

I don't know about Firefox since I don't use it,

There was an article on Reddit a few moons ago about the problem of Firefox memory usage, with links to Firefox's developers describing how memory allocation is done in Firefox. They spoke of memory compartments, and how a single reference to that compartment keeps the compartment from being deallocated.

Here is a relevant link.

Quote:

but Opera certainly stores each tab in its own bitmap

How do you know this? any links?

Oscar Giner
axilmar said:

How do you know this? any links?

Because even on an old Pentium 166 (the first computer I used Opera with), a complex page that maybe scrolled at 2-5fps (this was common with pages with a static background and transparent/translucent things on the front), was instantly shown when I clicked on its tab (it's obvious it was not being re-rendered).

Matthew Leverton
axilmar said:

What kinds of graphical effects do you have in mind?

For instance:

  • A window has a background image

  • A button in that window is drawn with 50% transparency over that background image

  • The entire result is drawn at 25% transparency

So a pixel in the button is calculated by:

(0.5 * button + 0.5 * window background) * 0.75 + (desktop * 0.25)

In this case, if you buffer the window, it's extremely simple. (No need to buffer the button.)

Say you want to drag a window and have it wobble around (see Compiz's wobbly windows plugin). Again, if you buffer the window, you can easily distort the buffer.

I think for most practical use cases, if the window is buffered and all of its widgets are sub bitmaps of that, then you'll be fine. But there could still be cases where that's not sufficient, which is why I optionally support buffering per widget.

Edgar Reynaldo
axilmar said:

It's not like Reynaldo's system that has two bitmaps per widget.

I don't know where you got that idea from, but it's not true. I said the WidgetHandler class has two bitmaps, not every single widget. It's basically a window / panel / gui class and it handles all the mundane tasks like hover/focus/update/input/display.

axilmar said:

I was talking about the bitmaps of widgets, in the context of one bitmap per widget, as per Reynaldo's implementation.

Again, that's not correct.

axilmar

Because even on an old Pentium 166 (the first computer I used Opera with), a complex page that maybe scrolled at 2-5fps (this was common with pages with a static background and transparent/translucent things on the front), was instantly shown when I clicked on its tab (it's obvious it was not being re-rendered).

On machines with similar specs, clicking on the Windows task bar to bring another Netscape or Internet Explorer in the foreground resulted in the background window being rendered almost instantly.

So, on what basis do you claim Opera cached its page rendering on a bitmap? perhaps your eyes were too slow to see the redraw, or Opera painted the page on a background bitmap first, and then discarded the bitmap.

Say you want to drag a window and have it wobble around (see Compiz's wobbly windows plugin). Again, if you buffer the window, you can easily distort the buffer.

You can always render it in a bitmap when it's about to be dragged. Why cache the output? X-Windows may need the caching in order to avoid network delays, but an Allegro-based GUI wouldn't need to do that, because creating the back buffer, drawing the widget tree is very fast (unless you draw something really complex, that is).

I don't know where you got that idea from, but it's not true. I said the WidgetHandler class has two bitmaps, not every single widget. It's basically a window / panel / gui class and it handles all the mundane tasks like hover/focus/update/input/display.

You didn't explain that you only have one WidgetHandler instance per GUI. I thought each widget has its own WidgetHandler. The name 'WidgetHandler' suggests each widget has its own handler.

Oscar Giner
axilmar said:

perhaps your eyes were too slow to see the redraw

if a bitmap wasn't stored, it would be a 200 ms (talking about the example I mentioned of a page that scrolled at 3-5fps) delay between I click on a tab and I see the contents. That's a very noticeable delay.

Quote:

or Opera painted the page on a background bitmap first, and then discarded the bitmap.

Wouldn't that mean that it would need to keep a bitmap of each tab? So if I change to a new tab I can instantly see the page, while in the background it re-renders it to show me an up to date version?

axilmar

if a bitmap wasn't stored, it would be a 200 ms (talking about the example I mentioned of a page that scrolled at 3-5fps) delay between I click on a tab and I see the contents. That's a very noticeable delay.

Perhaps it was a 50 ms delay. Or a 100 ms delay. Or a 500 ms delay. You cannot tell how many milliseconds the delay is, simply by clicking on the tab.

I don't doubt you, I just want a little proof for your claims. That you saw it is not proof, because we humans cannot tell the exact delay in milliseconds in such cases.

Quote:

Wouldn't that mean that it would need to keep a bitmap of each tab?

Nope, it would simply mean that Opera used a background buffer to draw its page.

I also doubt the rendering of such a page would need more than 10 milliseconds. If you use Allegro 4 on such an old computer, open a video mode of 1024x768 and then proceed to draw a background and then some transparent text and bitmaps on it; it will hardly take a few milliseconds. Do it with Win32 that perhaps uses hardware acceleration (accelerated 2d was the norm back then), and it would take much less.

Oscar Giner

I already told you that scrolling was very slow (no more than 5 fps), almost unusable. Antialiased text rendering is very slow, as are translucent blits. Opera was specially slow rendering this kind of pages, too. IE for example was pretty fast.

You Pentium was awesomely fast, where did you bought it?. Because for me even the Allegro Grabber help viewer was laggy (just a bit).

If you want a more recent prove, take this page: http://bitcoincharts.com/charts/mtgoxUSD#kgthUSDzrg2zvztgSzm1g10zm2g25 The graph there takes a good fraction of a second to render on my computer. Tabing in-out is instant, when restoring/maximizing the window you see a noticeable delay in the graph updating (the size changes so it needs to be redrawn).

Edgar Reynaldo
axilmar said:

You didn't explain that you only have one WidgetHandler instance per GUI. I thought each widget has its own WidgetHandler. The name 'WidgetHandler' suggests each widget has its own handler.

HandlerOfWidgets then? WidgetManager? That was the best name I could come up with.

Speaking of slow scrolling, there are some web pages that scroll like molasses. If they were buffered, that wouldn't happen.

axilmar

I already told you that scrolling was very slow (no more than 5 fps)

That does not mean drawing was slow. It may have been slow due to other reasons.

Quote:

Tabing in-out is instant, when restoring/maximizing the window you see a noticeable delay in the graph updating (the size changes so it needs to be redrawn).

Perhaps the graph itself is buffered and not the page. Perhaps the conversion of graph's data to an image is slow. Perhaps the fetching of data for the graph is slow. When I resize the page, all the elements are instantly redrawn except the graph.

HandlerOfWidgets then? WidgetManager? That was the best name I could come up with.

I once had in my GUI library:

typedef int (*WidgetHandler)(Widget *, int msg, void *data);

How about 'GUI'?

Quote:

Speaking of slow scrolling, there are some web pages that scroll like molasses. If they were buffered, that wouldn't happen.

It depends. If the whole page was buffered, then yes. But it would take a huge amount of memory. Imagine a bitmap 100 pages high, for example.

The slowness may be due to any number of parameters, including slow rendering.

Oscar Giner

Ok, believe what you want. I'm not going to do the research for you.

Thread #608514. Printed from Allegro.cc