Skip to content

make all circle drawing high-precision and faster too #943

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

cyrgani
Copy link
Contributor

@cyrgani cyrgani commented Apr 8, 2025

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:

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

Image: New version

image

@cyrgani
Copy link
Contributor Author

cyrgani commented Apr 8, 2025

Any help with the metal shader would be most appreciated, since I have no experience with them and no way to test them either.

@cyrgani cyrgani force-pushed the better-circles-v2 branch from b8c927a to 7fe344e Compare April 9, 2025 20:43
@KurlykovDanila
Copy link
Contributor

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

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

Successfully merging this pull request may close these issues.

2 participants