Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance Discussion #26

Open
Nathan-MV opened this issue Mar 5, 2024 · 5 comments
Open

Performance Discussion #26

Nathan-MV opened this issue Mar 5, 2024 · 5 comments

Comments

@Nathan-MV
Copy link

Nathan-MV commented Mar 5, 2024

I wonder what's going on here as i doubt it's entirely Ruby's fault, LiteRGSS2 (LiteCGSS is the backend with SFML2 and LiteRGSS2 are the Ruby bindings) can draw around 50k at a stable 60 fps (or is SFML2 just faster than Raylib/SDL2 and i didn't know? xD (SDL2 seems to draw 10k at a stable 60))
https://gitlab.com/NuriYuri/litecgss
https://gitlab.com/pokemonsdk/litergss2

Benchmark:
https://www.mediafire.com/file/zi7d3qwc0nk93dd/litergss2_-_bench.7z/file

@awfulcooking
Copy link

mruby can handle ~50,000 Ruby method calls per frame at 60fps on low/mid range devices.

MRI can do around 4x that.

nb. Calling a block has the same overhead as calling a method.

So with:

array = [1] * 10000

This loop costs 10000 more method calls:

array.each do |el|
  # do nothing
end

Than:

n = array.size
i = 0
while i < n
  el = array[i]
  i += 1
  # do nothing
end

Instance variables are free to access, relative to calling a method.

As a result it's much more efficient to ask game objects to handle themselves:

e.g.

class Tile
  attr_accessor %i(x y w h vx vy color)
  def tick
    if @vx != 0
      @x += @vx
    end
    if @vy != 0
      @y += @vy
    end
    DrawRectangle(@x, @y, @w, @h, @color)
  end
end

def tick()
  n = world.size
  i = 0
  while i < n
    thing = world[i]
    thing.tick
    i += 1
  end
end

This is the fastest way I know to update and draw many game objects in mRuby.

I bet the difference between LiteRGSS2, Taylor and SDL in your tests comes down to method call overhead in the implementation of their Ruby APIs.

Once they call into C, the difference is probably negligible in comparison!

@Nathan-MV
Copy link
Author

Nathan-MV commented Mar 6, 2024

I had tested SDL2 using mkxp-z and it gave 10k but using RGU which is using SDL3 it gives 50 fps with 20k both of them using Angle to Vulkan, as they are used to do the same thing, aka being a RGSS1/2/3 player for RPG Maker XP/VX/VXA i used the same benchmark for both (LiteRGSS is a little different (It was initially made for RGSS1(RPG Maker XP) but then it was designed solely for PokemonSDK and changed things too much on both ends) so it needs some heavy modifications and instead of being an executable it is a shared library so i put the ruby binary pokemonsdk uses with it in the download)

class SpriteGenerator
  DEFAULT_SPRITE_COUNT = 20_000
  SCREEN_WIDTH = 640
  SCREEN_HEIGHT = 480
  SPRITE_SIZE = 32

  def initialize
    @sprite_count = DEFAULT_SPRITE_COUNT
    @viewport = Viewport.new(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)
    @bitmap = nil
  end

  def run
    initialize_sprites
    loop do
      Graphics.update
    end
  rescue StandardError
    puts "\nEND\n"
  end

  private

  def initialize_sprites
    puts "Sprite Count: #{@sprite_count}"

    @sprites = @sprite_count.times.map do |i|
      initialize_bitmap if (i % 100).zero?
      initialize_sprite
    end
  end

  def initialize_bitmap
    @bitmap = Bitmap.new(320, 320)
    @bitmap.fill_rect(0, 0, 320, 320, random_color)
  end

  def initialize_sprite
    sprite = Sprite.new(@viewport)
    sprite.bitmap = @bitmap
    sprite.x = rand(SCREEN_WIDTH) - SPRITE_SIZE
    sprite.y = rand(SCREEN_HEIGHT) - SPRITE_SIZE
    set_sprite_src_rect(sprite)
    sprite
  end

  def set_sprite_src_rect(sprite)
    j = @sprite_count % 100
    sprite.src_rect.set(32 * (j / 10), 32 * (j % 10), SPRITE_SIZE, SPRITE_SIZE)
  end

  def random_color
    Color.new(rand(256), rand(256), rand(256), 32)
  end
end

SpriteGenerator.new.run

@HellRok
Copy link
Owner

HellRok commented Mar 9, 2024

I've been busy with the refactor at the moment but performance is something I've kept a little bit of an eye on. I'm probably going to have a quick pass over the code base once I've finished the refactor to see what I can do about performance.

With the refactor there is a lot less ruby -> ruby -> c++, it's now more ruby -> c++, I'm hoping this will help mitigate a little bit of the performance as I too feel it could be better.

@Nathan-MV
Copy link
Author

Ray said he tends to ignore code that could be more performatic for code that are simpler to understand for the sake of his students

@Nathan-MV
Copy link
Author

It appears that creating your own Vector2 class in Ruby increases benchmark performance by 2x
https://github.com/vaiorabbit/raylib-bindings/blob/main/examples/textures_bunnymark_optimized.rb

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants