Skip to content

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

Open
cyrgani wants to merge 2 commits into
not-fl3:masterfrom
cyrgani:better-circles-v2
Open

make all circle drawing high-precision and faster too#943
cyrgani wants to merge 2 commits into
not-fl3:masterfrom
cyrgani:better-circles-v2

Conversation

@cyrgani

@cyrgani cyrgani commented Apr 8, 2025

Copy link
Copy Markdown
Contributor

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

cyrgani commented Apr 8, 2025

Copy link
Copy Markdown
Contributor Author

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
Copy Markdown
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