CanvasGadget - faster double buffering

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

CanvasGadget - faster double buffering

Post by Danilo »

The CanvasGadget copies the image content on StopDrawing(), and preserves the old image.
The PB help example:

Code: Select all

  If OpenWindow(0, 0, 0, 220, 220, "CanvasGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    CanvasGadget(0, 10, 10, 200, 200)
    
    Repeat
      Event = WaitWindowEvent()
          
      If Event = #PB_Event_Gadget And EventGadget() = 0 
        If EventType() = #PB_EventType_LeftButtonDown Or (EventType() = #PB_EventType_MouseMove And GetGadgetAttribute(0, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton)
          If StartDrawing(CanvasOutput(0))
            x = GetGadgetAttribute(0, #PB_Canvas_MouseX)
            y = GetGadgetAttribute(0, #PB_Canvas_MouseY)
            Circle(x, y, 10, RGB(Random(255), Random(255), Random(255)))
            StopDrawing()
          EndIf
        EndIf
      EndIf    
      
    Until Event = #PB_Event_CloseWindow
  EndIf
Because the image content is preserved/copied, the double buffer switching in StopDrawing()
is very slow. The bigger the Canvas, the slower the copying/switching.

Maybe an optional command or flag could be introduced, so the buffers are just switched
without preserving/copying the content?
If I always draw new content onto the whole drawing area, I do not need the old content
to be preserved (custom gadgets and displays).

The double buffer switching should be much faster without copying/preserving on every StopDrawing() (1920x1200 Pixels for example).

Maybe a flag for CanvasOutput() could be added:

Code: Select all

If StartDrawing(CanvasOutput(0,#PB_Canvas_DoNotPreserveBuffer))
Or an attribute for the CanvasGadget to change the default behavior:

Code: Select all

SetGadgetAttribute(#Canvas,#PB_Canvas_CopyBackBuffer, #True ) ; DEFAULT
SetGadgetAttribute(#Canvas,#PB_Canvas_CopyBackBuffer, #False)
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: CanvasGadget - faster double buffering

Post by srod »

Not sure I follow this switching of buffers notion exactly. Does the canvas not simply maintain a single image buffer which is then blitted every time a paint event is received?

As I see it, when we draw to the canvas we simply draw to the image which is then blitted across. Actually, the user manual suggests that there is indeed an additional buffer since the description of #PB_Canvas_Image states that the imageID is invalidated when we simply alter the contents of the canvas. The following example seems to contradict this though :

Code: Select all

If OpenWindow(0, 0, 0, 220, 600, "CanvasGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    CanvasGadget(0, 10, 10, 200, 200)
    imageID = GetGadgetAttribute(0, #PB_Canvas_Image)
    ButtonGadget(1, 10, 300, 180, 20, "Copy canvas image...")
    ImageGadget(2, 10, 380, 200, 200, 0, #PB_Image_Border)
    Repeat
      Event = WaitWindowEvent()
      If Event = #PB_Event_Gadget
        If EventGadget() = 0 
          If EventType() = #PB_EventType_LeftButtonDown Or (EventType() = #PB_EventType_MouseMove And GetGadgetAttribute(0, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton)
            If StartDrawing(CanvasOutput(0))
              x = GetGadgetAttribute(0, #PB_Canvas_MouseX)
             y = GetGadgetAttribute(0, #PB_Canvas_MouseY)
              Circle(x, y, 10, RGB(Random(255), Random(255), Random(255)))
              StopDrawing()
            EndIf
          EndIf
        ElseIf EventGadget() = 1
          SetGadgetState(2, imageID)
        EndIf    
      EndIf
    Until Event = #PB_Event_CloseWindow
  EndIf
Note that the imageID is recorded immediately after the canvas is created and you can see by hitting the button that this imageID remains valid throughout the life of the canvas (unless we resize it). At least this is the case on Windows.

Are you saying that you wish for an option to draw directly to the canvas as opposed to the back image, i.e. an option to dispense with the image buffer? Useful only if we had better notification of paint events for individual controls otherwise you'll be repainting it more times than strictly required.
I may look like a mule, but I'm not a complete ass.
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: CanvasGadget - faster double buffering

Post by Danilo »

@srod:
CanvasOutput() -> Remarks -> Drawing on a CanvasGadget() is double-buffered.

The double-buffering is a good thing and makes the canvas display flicker-free, but because the content
is always preserved/copied, the double buffer switching is slow.
If I always redraw the whole canvas area, I do not need the old content preserved, because I paint over it anyway.

The real problem for me was, the CanvasGadget was very slow in a painting application
and it seems to be the copying/preserving of the old content on every double buffer switch (StopDrawing)
is the reason. I do not need the old content, I need speed.
I switched to WindowOutput() and to ImageGadget() for testing, and the drawing application was
much faster.

So I make a feature request to switch the 2 buffers without copying/preserving the content.
Just exchange the buffers and display the new front buffer, without preserving the buffer content.
For "real time displays" (redraw 1920x1200x32bit at 60 times a second) and feature rich drawing applications,
speed is very important. The CanvasGadget is much slower as using an ImageGadget, because the
Canvas preserves the old buffer on every StopDrawing().

If the user moves the Wacom tablet pen, I need to update the display (Canvas) every time, and if
the user moves the pen continuously, I need to update the CanvasGadget continuously.
Maybe this updating of a big area is required 60 or 100 times a second, drawing layers and brushes
to the CanvasGadget. The CanvasGadget double buffer switching in StopDrawing slows those things down
if it copies/preserves the old content every time, while I don't need this feature (I overpaint the whole area anyway).

It should be optional (SetGadgetAttribute for example) to change the double buffer switch behavior.

Nothing will change for old users. Users who change the switch behavior get a faster buffers switch
and an undefined (or old) content is in the drawing area (back buffer) - no problem if you redraw the whole area yourself anyway.
Last edited by Danilo on Sat Sep 01, 2012 12:14 pm, edited 1 time in total.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: CanvasGadget - faster double buffering

Post by srod »

The question then is whether PB is using 2 image buffers or just 1? Certainly 1 is enough to preserve the canvas gadget's contents and will reduce flicker etc. The only reason I can see for having 2 image buffers would be to allow one program thread to draw on one buffer whilst another was responding to a paint event etc. since any single image can be offered up to only one DC at a time.

Must admit that I find it hard to believe that 2 image buffers would be in use here, though of course that would explain the speed issues you are having.
I may look like a mule, but I'm not a complete ass.
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Re: CanvasGadget - faster double buffering

Post by freak »

There is only one image. The second 'buffer' is the window. When StopDrawing() is called, the image is drawn onto the window, that is all.

Note that the #PB_Canvas_DrawFocus actually involves a 2nd image to draw the focus rectangle without destroying the content. But if you don't use that flag there is only one image buffer.

> redraw 1920x1200x32bit at 60 times a second

For something like that, maybe a windows screen is better?
quidquid Latine dictum sit altum videtur
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: CanvasGadget - faster double buffering

Post by Danilo »

freak wrote:There is only one image. The second 'buffer' is the window. When StopDrawing() is called, the image is drawn onto the window, that is all.

Note that the #PB_Canvas_DrawFocus actually involves a 2nd image to draw the focus rectangle without destroying the content. But if you don't use that flag there is only one image buffer.
OK, thanks for making this clear. Using WindowOutput or ImageOutput+SetGadgetState with an ImageGadget
was still much faster here. Don't know why, I thought it is the double buffer copying.
freak wrote:> redraw 1920x1200x32bit at 60 times a second

For something like that, maybe a windows screen is better?
Yes, I already tested some lowlevel DirectX 9 stuff, where I can display 100 alpha layers
one on each other with different blending modes and with rotation (very expensive if done on the CPU)
of the drawing area in real-time. Much more work, though. DX9Canvas. ;)


This feature request is considered null and void.
Joris
Addict
Addict
Posts: 890
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: CanvasGadget - faster double buffering

Post by Joris »

For the same reason as above : more speed with canvas drawings and without having to change existing codes.

Maybe I don't understand the given answers above good enough and has my question still sense :
Wouldn't it then still be possible to add a flag to StartDrawing like StartDrawing(..., #Reverse_Double_Buffer) ?
#Reverse_Double_Buffer would make drawing on the Canvas directly visible and only when StopDrawing() is called the last drawings become buffered.
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
Post Reply