make all circle drawing high-precision and faster too
This PR adds a new drawing mode and shaders for drawing circles directly and uses that in draw_circle instead of the previous draw_poly approach. This makes it faster in all cases!
It uses the great implementation from #521 as a foundation and adds a bugfix and an update to the current shader APIs to it.
This PR supersedes #939 and #940.
There are still two things to do before this can be merged:
- test the Metal version of the shader
- ~#945 must be resolved independently of this PR~
Performance
Here is my benchmarking code to test both variants:
use macroquad::prelude::*;
use std::time::{Duration, Instant};
pub fn draw_circle_new_timed(x: f32, y: f32, r: f32, color: Color, sum_time: &mut Duration) {
let t0 = Instant::now();
draw_circle(x, y, r, color);
*sum_time += t0.elapsed();
}
pub fn draw_circle_old_timed(x: f32, y: f32, r: f32, color: Color, sum_time: &mut Duration) {
let t0 = Instant::now();
draw_poly(x, y, 20, r, 0., color);
*sum_time += t0.elapsed();
}
#[macroquad::main("BasicShapes")]
async fn main() {
let mut old_sum_dur = Duration::ZERO;
let mut new_sum_dur = Duration::ZERO;
let mut n = 0;
loop {
clear_background(LIGHTGRAY);
/*draw_circle_timed(
screen_width() * 0.5,
screen_height() * 0.5,
200.,
BLUE,
&mut sum_dur,
);*/
for _ in 0..1000 {
let x = quad_rand::gen_range(0.0, screen_width());
let y = quad_rand::gen_range(0.0, screen_height());
draw_circle_old_timed(x, y, 1., BLUE, &mut old_sum_dur);
let x = quad_rand::gen_range(0.0, screen_width());
let y = quad_rand::gen_range(0.0, screen_height());
draw_circle_new_timed(x, y, 1., GREEN, &mut new_sum_dur);
n += 1;
}
println!("old avg time: {:?}, n {}", old_sum_dur / n, n);
println!("new avg time: {:?}, n {}", new_sum_dur / n, n);
draw_text("PRETTY CIRCLE", 20.0, 20.0, 30.0, DARKGRAY);
next_frame().await
}
}
Results on my laptop are for circles of radius 1:
old avg time: 1.812µs, n 74000
new avg time: 1.357µs, n 74000
When dealing with larger circles (replace 0..1000 with 0..5 and radius 1. with 100.), the new versions speed advantage remains or is even clearer (in addition to the circle being round and not polygonal):
old avg time: 2.474µs, n 480
new avg time: 1.834µs, n 480
Image: Old version
Image: New version
Any help with the metal shader would be most appreciated, since I have no experience with them and no way to test them either.
Judging by the implementation, you draw a circle as a texture on a rectangular surface of 2 triangles. Perhaps you should not be categorical and change the initial implementation (using a polygon) to a new one. However, you can always add this as a new function so as not to break compatibility, because some users could rely in their shaders on the fact that a circle is a set of triangles. Example of names: draw_texture_circle or draw_smooth_circle