Tearing in my buffered animation test script

OK, I have a basic blit demo working (thanks to Alex F. for his
reply, which I finally saw).

However, there’s a great deal of “tearing” on the screen - flickering
grey lines in the black background. It looks like the blit isn’t
always complete when the screen refreshes.

Can anyone look at this and tell me what I might be doing wrong? Any
help would be most appreciated!

-Jay McGavren
http://jay.mcgavren.com/zyps

require ‘rubygems’
require ‘wx’

class MyApp < Wx::App

def on_init

#Containing frame.
frame = Wx::Frame.new(nil, :size => [300, 300])
frame.show

#Offscreen drawing buffer.
buffer = Wx::Bitmap.new(300, 300)

#Displays drawing.
window = Wx::Window.new(frame, :size => [300, 300])
window.evt_paint do |event|
  window.paint do |dc|
    #Copy the buffer to the viewable window.
    buffer.draw do |buffer_dc|
      dc.blit(0, 0, 300, 300, buffer_dc, 0, 0)
    end
  end
end

#Animate.
(1..40).each do |i|
  #Clear screen.
  buffer.draw do |surface|
    surface.pen = Wx::Pen.new(Wx::Colour.new(0, 0, 0), 0)
    surface.brush = Wx::BLACK_BRUSH
    surface.draw_rectangle(0, 0, 300, 300)
  end
  #Draw line.
  buffer.draw do |surface|
    surface.pen = Wx::Pen.new(
      Wx::Colour.new(128, 255, 128),
      3
    )
    surface.pen.cap = Wx::CAP_ROUND
    surface.draw_line(i, 0, i+100, 100)
  end
  #Update screen.
  window.refresh
  window.update
  sleep 0.1
end

end

end

app = MyApp.new
app.main_loop

On 11/30/07, Jay McGavren [email protected] wrote:

-Jay McGavren
http://jay.mcgavren.com/zyps

Hey Jay,

I’ve actually looked over the demo, and tested it out on my computer.
According to what I see here, there should be no problems with it. And
I’ve
tested it on Linux, and I have no flickering, or tears in it. I would
suggest, for the final “blit” operation, that instead of waiting for a
Paint
Event, you switch to using DC#draw_bitmap() instead of using DC#blit()
This will take a lot of the computation off of wxRuby, which it must do
to
ensure that everything is within bounds, and there is not a programmer
error
(Which is often raised by a Message box error, or a segfault).
Especially
considering that your drawing the entire buffer that you have offscreen.

Two other things.
1.) If your doing the actual painting when evt_paint() is called, then
you
do not need to create a ClientDC (Which is what window.paint does)
2.) You can skip evt_paint() and window.refresh/window.update all
together,
and just call window.paint, to paint to the window, when your finished
with
the painting on the Buffer.

Just a couple of suggestions for ya, which should take care of the
flicker
and tearing.

Mario S.

On 12/1/07, Mario S. [email protected] wrote:

Mario S.

Just a quick follow up. What I meant to say, was that you need to use
one,
or the other, as your basically creating two DC’s within a single event,
which would be causing the flicker. So, you need to either create a
Thread
to draw it, or you need to take out the paint and draw within the
evt_paint() proc. Sorry if that didn’t make sense earlier. :wink:

Hi Jay!

I have tested your application, and indeed there is great deal of
flicker as expected. You are not making any particular errors in your
code; the problem occurs because the drawing, or more precisely, the
copying of graphics (blit) is not synchronized with the vertical sync
signal your screen receives from your graphics card. (I’m speaking in
terms of analog signal CRT, but actually this applies to any kind of
digital screen as well, as far the application is concerned)

The usual way to deal with this is:

  1. Draw all the graphics onto an backbuffer (any surface that does not
    appear on the screen yet)

  2. Await the V-blank (the vertical sync signal)

  3. Copy the contents of this backbuffer onto the screen (the
    frontbuffer), or simply swap the buffers if your API supports it

  4. repeat from 1

What you are currently doing is similar but lacks the point (2). wxRuby
aims to support this on a lower level (i think). There is a method
called “paint_buffered” (in stead of “paint”) that provides a
BufferedDC-object instead of ClientDC or DC. Drawing to the BufferedDC
is then not supposed to be visible first after: 1. the actual drawing
you are making on it is finally completed ie. the block provided to
“buffered_paint” finishes, and 2. the V-blank occurs.

Sadly enough, this feature does not seem to be working properly with
wxruby 1.9.5. I have currently similar problem - have not yet decided
whether to devise another method to signal V-blank to Ruby, or to accept
the moderate flicker in my application.

Best regards,

Paul W Florczykowski

Hi Paul

Paul w Florczykowski wrote:

  1. Draw all the graphics onto an backbuffer (any surface that does not
    appear on the screen yet)

  2. Await the V-blank (the vertical sync signal)

  3. Copy the contents of this backbuffer onto the screen (the
    frontbuffer), or simply swap the buffers if your API supports it

  4. repeat from 1

I didn’t know about (2), thanks for this info on buffering.

whether to devise another method to signal V-blank to Ruby, or to accept
the moderate flicker in my application.

wxRuby 1.9.5 ‘paint_buffered’ method does what you describe, lacking
point (2). It doesn’t do anything more low-level. It’s implemented in
pure Ruby,

http://wxruby.rubyforge.org/svn/trunk/wxruby2/lib/wx/classes/window.rb

but it’s a straight conversion of the C++ code that wxWidgets uses to
provide BufferedDC:

http://svn.wxwidgets.org/viewvc/wx/wxWidgets/tags/WX_2_8_7/include/wx/dcbuffer.h?revision=52270&view=markup

If you find any advice on how we can improve this by synchronising with
the V-blank please post this, as this is beyond my expertise.

thanks
alex