[wxruby-users] Drawing thread not getting enough time from scheduler?

Jay McGavren jay at mcgavren.com
Thu Jan 10 11:14:50 EST 2008


Alex Fenton wrote:
> Jay McGavren wrote:
> > It did speed things up a bit, but it still runs many
> times faster (55
> > seconds vs. 5 seconds) if I join the animation thread.
> >
> ...
> > Any idea how I can get the thread scheduler to devote a
> bit more time
> > to animation, without completely freezing the GUI?
> Just had a chance to try your code. For me on OS X it
> doesn't work at all if thread.join is called, but runs
> pretty smoothly (about 8s total) otherwise. At some level
> you'll also be caught by the granularity of Ruby's thread
> time slices (10ms IIRC), and garbage collection, which is
> taking up to 9ms tracking wxRuby objects alone in your
> example.

OK, so avoid excessive object creation...  Oh, yes, and I forgot
(again) to mention I'm on Windows, as are my main target users.
Interesting that the thread handling seems to be so much better on
OSX.

> I see a couple of further potential optimisations in your
> code which improve smoothness for me:
>
> * Take the calls to surface.pen= and surface.pen.cap= out
> of the 30.times loop.
> * See whether the drawing could be more efficiently done
> with a single draw_polygon (which acccepts an Array of
> Wx::Points)

It only happens to look like a polygon - my actual game needs to draw
individual lines (in varying colors and line widths).  But that's OK,
because your below suggestion allowed me to draw everything at decent
speed...

> Another untested possibility to improve perceived
> smoothness might be to drive the animation from a
> Wx::Timer. Have it run at regular intervals, calling
> window.refresh to invalidate the window, then copy from the
> bitmap inside an evt_paint handler.

This was the key.  It's drawing 300 lines in under 33 milliseconds
now, even on my laptop.  The close button and window dragging respond
immediately.

  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|
        update_window(window, buffer)
      end

      #Initialize drawing loop counter.
      @i = 0

      #Animate periodically.
      timer_id = Wx::ID_HIGHEST + 1
      t = Wx::Timer.new(self, timer_id)
      evt_timer(timer_id) {animate(window, buffer)}
      t.start(33)

    end

    def animate(window, buffer)
      green_pen = Wx::Pen.new(Wx::Colour.new(128, 255, 128), 3)
      black_pen = Wx::Pen.new(Wx::Colour.new(0, 0, 0), 0)
      buffer.draw do |surface|
        #Clear screen.
        surface.pen = black_pen
        surface.brush = Wx::BLACK_BRUSH
        surface.draw_rectangle(0, 0, 300, 300)
        #Draw lines.
        surface.pen = green_pen
        surface.pen.cap = Wx::CAP_ROUND
        300.times do |j|
          x = @i + j
          surface.draw_line(x, 0, x+100, 100)
        end
      end
      #Update screen.
      update_window(window, buffer)
      @i += 1
      @i = 0 if @i > 300
    end

    def update_window(window, buffer)
      window.paint do |dc|
        #Copy the buffer to the viewable window.
        dc.draw_bitmap(buffer, 0, 0, false)
      end
    end

  end

  app = MyApp.new
  app.main_loop

Now to see if I can get similar results from the game itself.  Thanks
to everyone for the assistance!

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


More information about the wxruby-users mailing list