jeroenjanssens / raylibr

R package that wraps Raylib, a simple and easy-to-use library to enjoy videogames programming
https://jeroenjanssens.github.io/raylibr/
Other
44 stars 6 forks source link

A matter of performance or "The tale of the turtle and the rabbit" #8

Closed Gladius1 closed 1 year ago

Gladius1 commented 2 years ago

Well, the performance of raylibr isn't great. I rewrote the bunnymark example (https://github.com/raysan5/raylib/blob/master/examples/textures/textures_bunnymark.c) and using the render_texture function raylibr can draw about 1000 bunnies on my machine (before FPS drops below 30).

A simple fix is to do the rendering on the c++ side. So I wrote a simple function render_texture_vec which takes a texture and two vectors of X- and Y-positions respectively.

void draw_texture_vec(Texture texture, DoubleVector pos_x, DoubleVector pos_y) { for (int i = 0; i < pos_x.length(); i++){ DrawTexture(texture, pos_x[i], pos_y[i], WHITE); } return;}

Using this function raylibr can draw about 150000 bunnies before the FPS drop below 30.

So I propose that a "vectorized" version of at least the texture-drawing functions should be added. Unfortunately I lack the skill to do more than this (believe me I tried several days...).


Slightly OT. Pure c++ gets me about 600000 bunnies. Rust about 400000. Go about 170000. R about 150000 (but with microstutter from garbage collector). Python about 12000.

My machine is a 8 year old I5 3.2 Ghz, DDR3 Ram and a NVIDIA 1050 GTX? GPU. I would be very much interested if newer machines get better peformance. Care to test out? https://github.com/Gladius1/raylibr/tree/bunnymark

jeroenjanssens commented 1 year ago

Thank you for the suggestion @Gladius1. All draw_* and image_draw_* functions are now vectorized. In order to leave the interface the same (and not generate additional R functions), the R function decides if one of its arguments has more than one element or row:

draw_cube <- function(position, width, height, length, color) {
  if (!is_vector_3(position) && !is_mat(position, is_vector_3)) abort(paste0('`position` must be a numeric vector of length 3 or a numeric matrix of width 3, not ', friendly_typeof(position), '.'), call = NULL)
  if (!is_float(width) && !is_vec(width, is_float)) abort(paste0('`width` must be a number or a vector of numbers, not ', friendly_typeof(width), '.'), call = NULL)
  if (!is_float(height) && !is_vec(height, is_float)) abort(paste0('`height` must be a number or a vector of numbers, not ', friendly_typeof(height), '.'), call = NULL)
  if (!is_float(length) && !is_vec(length, is_float)) abort(paste0('`length` must be a number or a vector of numbers, not ', friendly_typeof(length), '.'), call = NULL)
  if (!is_color(color) && !is_vec(color, is_color)) abort(paste0('`color` must be a color or a list of colors, not ', friendly_typeof(color), '.'), call = NULL)

  lens <- c(nrow(position), length(width), length(height), length(length), length(color))
  if (any(lens > 1)) {
    max_len <- max(lens)
    if (lens[1] < max_len) position <- rep(position, length.out = max_len)
    if (lens[2] < max_len) width <- rep(width, length.out = max_len)
    if (lens[3] < max_len) height <- rep(height, length.out = max_len)
    if (lens[4] < max_len) length <- rep(length, length.out = max_len)
    if (lens[5] < max_len) color <- rep(unlist(list(color)), length.out = max_len)
    draw_cube_vectorized_(position, width, height, length, color)
  } else {
    draw_cube_(position, width, height, length, color)
  }
}

If that's the case, it will ensure all vectorized arguments have the same length and call a vectorized version of the C++ function:

void draw_cube_vectorized_(NumericMatrix position, NumericVector width, NumericVector height, NumericVector length, List color) {
  for (int i = 0; i < position.nrow(); i++) {
    DrawCube(Vector3{ as<float>(wrap(position(i, 0))), as<float>(wrap(position(i, 1))), as<float>(wrap(position(i, 2))) }, width[i], height[i], length[i], color[i]);
  }
}

Otherwise, it will call the regular version:

void draw_cube_(Vector3 position, float width, float height, float length, Color color) {
  return DrawCube(position, width, height, length, color);
}

I have updated the cubes and balls demos to leverage the new vectorized drawing functions. They now run buttery smooth!

Gladius1 commented 1 year ago

Awesome!That is a very clever solution.