Drawing thread not getting enough time from scheduler?

Need some assistance with animation again… When I run the drawing
code in a separate thread, it’s slow as heck. I think it may be
because the drawing thread isn’t getting enough time from the thread
scheduler.

I was able to reproduce the problem in my little sample program simply
by increasing the number of lines it draws per update. I’m lucky to
get one frame every three seconds with this. If I “thread.join” or
“thread.priority += 1” the speed greatly increases, but then the GUI
becomes unresponsive. (Similar problems with threads have been
discussed previously on the wxRuby mailing list.)

require ‘rubygems’
require ‘wx’

class MyApp < Wx::App

def on_init

#Animate every 33 milliseconds.
  t = Wx::Timer.new(self, 55)
  evt_timer(55) { Thread.pass }
  t.start(33)

  #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])

  #Animate.
  thread = Thread.new do
    300.times 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 lines.
      30.times do |j|
        x = i + j
        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(x, 0, x+100, 100)
        end
      end
      #Update screen.
      update_window(window, buffer)
    end
  end

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

I ran it through ruby-prof… Am I right in thinking these results
mean the drawing thread is getting a fraction of the time that the
main loop is?

Thread ID: 59174940
Total: 0.01

%self total self wait child calls name
1250.00 30.45 0.13 2.02 28.31 11 Integer#times-1
160.00 2.86 0.02 2.84 0.00 3391
Wxruby2::Colour#initialize
160.00 0.02 0.02 0.00 0.00 3378
Wxruby2::DC#draw_line
0.00 0.05 0.00 0.05 0.00 11
Wxruby2::Window#paint

Thread ID: 42518340
Total: 32.594

%self total self wait child calls name
96.59 31.72 31.48 0.00 0.24 1
Wxruby2::App#main_loop
2.01 0.77 0.66 0.00 0.11 42
Kernel#gem_original_require-1
0.19 0.06 0.06 0.00 0.00 28 Class::Dir#[]

My actual program (a game) is going to be doing even more drawing
operations than this, so I need to boost the speed wherever I can.
Any advice would be most appreciated!

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

Hello Jay,

Well, one optimization that I can see just by skimming the code, is that
your creating a DC Buffer each time you loop to draw a line. The
grabbing
of a DC weither it’s an On-Screen Control, or an Off Screen Bitmap.

The better way to do this, is to go this route here:

–Code–
buffer.draw do |surface|
surface.pen = Wx::Pen.new(
Wx::Colour.new(128, 255, 128),
3
)
30.times do |j|
x = i + j
surface.pen.cap = Wx::CAP_ROUND
surface.draw_line(x, 0, x+100, 100)
end
end
–end code–

If you would also notice, I’m not constantly re-creating the Wx::Pen
either. Since it’s created once, and your using the same colour, and
size
for the Pen, there’s no reason to be re-creating it for each and every
line
you draw.

Whenever it comes to drawing, the main things you need to keep in mind,
as
far as performance is concerned, is that everytime you create a DC, it
takes
the Underlying OS a bit to load up the DC, the same to release the DC.
Also, you will want to create your primary pens first, that your going
to
use most often, and avoid trying to create them everytime an iteration
of a
loop occurs, as this also takes time away from which you can do stuff
with.

I believe, that with these simple modifications, you would easily get up
to
50~ FPS on the game.

Let me know how it goes,

Mario S.