Closed Gladius1 closed 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!
Awesome!That is a very clever solution.
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