Rendimiento con draw_circle y draw_fcircle

Started by Arcontus, December 01, 2017, 05:23:39 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Arcontus

Hola chicos,
estoy desarrollando un videojuego y me encuentro que tengo una mala performance al utilizar la funcion fcircle.Para debugar mis sospechas, he creado el siguiente código:import "mod_key"
import "mod_proc"
import "mod_video"
import "mod_text"

import "mod_map"
import "mod_math"
import "mod_rand"
import "mod_draw"


PROCESS RadioAlcance1()        //187 FPS
PRIVATE
    int tamano, radio, salir;
END
BEGIN
    radio = 400;
    tamano = radio*2;
    graph = new_map(tamano+1, tamano+1, 32);
    drawing_map(0,graph);
    //Dibujamos un circulo relleno
    drawing_color(rgba(255,255,255,175));
    draw_fcircle(radio, radio, radio);
    //Dibujamos un circulo un pixel mas pequeño con el alpha al mínimo.
    drawing_color(rgba(0,0,0,0));
    draw_fcircle(radio, radio, radio - 1);
    z = -10;
    while(!(key(_esc)))
        x = rand(400, 600);
        y = rand(400, 600);
        FRAME;
    END
OnExit:
    map_unload(0,graph);
END

PROCESS RadioAlcance2()   //429 FPS, ¡Pero si es el mismo grafico que en RadioAlcance1! WTF???
PRIVATE
    int tamano, radio;
END
BEGIN
    radio = 400;
    tamano = radio*2;
    graph = new_map(tamano+1, tamano+1, 32);
    drawing_map(0,graph);
    //Dibujamos un circulo sin relleno
    drawing_color(rgba(255,255,255,175));
    draw_circle(radio, radio, radio);

    z = -10;
    while(!(key(_esc)))
        x = rand(400, 600);
        y = rand(400, 600);
        FRAME;
    END
OnExit:
    map_unload(0,graph);
END

PROCESS RadioAlcance3()  //187 FPS ¡¡Anda, lo mismo que el primero!!
PRIVATE
    int tamano, radio;
END
BEGIN
    radio = 400;
    tamano = radio*2;
    graph = new_map(tamano+1, tamano+1, 32);
    drawing_map(0,graph);
    //Dibujamos un circulo relleno
    drawing_color(rgba(255,255,255,175));
    draw_fcircle(radio, radio, radio);
    z = -10;
    while(!(key(_esc)))
        x = rand(400, 600);
        y = rand(400, 600);
        FRAME;
    END
OnExit:
    map_unload(0,graph);
END

PROCESS RadioAlcance4()  //187 FPS ¡¡Anda, lo mismo que el primero!!
PRIVATE
    int tamano, radio;
END
BEGIN
    radio = 400;
    tamano = radio*2;
    graph = new_map(tamano+1, tamano+1, 32);
    drawing_map(0,graph);
    //Dibujamos un circulo relleno
    drawing_color(rgba(255,255,255,255));
    draw_fcircle(radio, radio, radio);

    z = -10;
    while(!(key(_esc)))
        x = rand(400, 600);
        y = rand(400, 600);
        FRAME;
    END
OnExit:
    map_unload(0,graph);
END

Process Main()
Begin

    set_mode(1000,900,32);
    set_fps(0,0);
    write(0,10,5,0, "FPS: ");
    write_int (0,50,5,0, &FPS);

    graph = new_map(1000, 900, 32);
    drawing_map(0,graph);
    x = 1000/2;
    y = 900/2;
    drawing_color(rgba(60,60,20,255));
    draw_box(0, 0, 1000,900);

    RadioAlcance4(); //<<-- cambiad aqui por RadioAlcance1-4() para probar.
    while(!(key(_esc)))
        FRAME;
    END
END

He creado cuatro funciones RadioAlcance1(), RadioAlcance2(), RadioAlcance3() y RadioAlcance4(). Tanto RadioAlcance1() como RadioAlcance2() crean un mapa con un circulo vacio de 1 pixel de grosor, sin embargo el impacto que tiene en la performance es brutal, pasando de 429 FPS a tan solo 187 FPS. Ambos mapas tienen el mismo tamaño y ambos tienen un color traslucido.
Por otro lado, RadioAlcance3() y RadioAlcance4() ambos crean un circulo relleno, en el primer caso es traslucido, en el segundo es color solido. Ambos tienen el mismo performance en contra de mi intuición. Daba por hecho que el que tenia traslucidez tendria peor rendimiento que el color solido por tener que teñir el color de fondo.

¿No es esto un poco raro?
Me gustaría entender que está sucediendo para poder mejorar el rendimiento del juego, ya que me baja 30fps al utilizar draw_fcircle(), independientemente de si tiene alpha o no y esos 30fps en mi caso son demasiado. Y porque draw_circle() tiene un rendimiento tan descomunalmente superior.

Un saludo!
5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

Drumpi

Por lo que yo veo, y con todo el sentido del mundo (a menos que sea una errata) es que tu RadioAlcance2 es el único que usa circle en lugar de fcircle.
Si la cosa no ha cambiado (y no me consta) las funciones draw no sólo pintan figuras geométricas, sino que generan un "objeto"; es como un mini-proceso, con un gráfico asociado, tienen su propio ID y puedes usar move_draw y delete_draw sin alterar el mapa sobre el que están dibujados.
Por eso, cuando se usan, tiene que volverse a dibujar en cada frame sobre el mapa en el que están, lo cual afecta al rendimiento, y en tu caso, al usar circle, este tiene que pintar menos pixels que fcircle al no estar relleno.
Lo de que la transparencia no afecte al resultado puede deberse a cómo se manejan los gráficos en ese modo, que siempre se ha dicho que consume muchos más recursos que a 16 bits.

Pero creo recordar que se mencionó algo sobre que los "draw_*" son objetos sólo si se dibujan sobre el fondo, y no sobre otros mapas, con lo cual mi explicación carece de sentido, pero no lo veo claro, porque entonces las funciones move_draw y delete_draw podrían usarse con unos draw_* (los que se dibujan en el fondo) y con otros no (los que se dibujan sobre un mapa).

Eso que nos lo aclare Splinter, que se ha leido el código ^^U
Hala, como con 1001 procesos sólo va a 9 FPS, vamos a meterle 32 veces más, a ver si revienta.
(Drumpi epic moment)

Arcontus

Yo tengo otra teoría, quizás el vaciarlo del fcircle con fcircle a rgba(0,0,0,0) no deja los pixels a "NULL" o sin info y por ello, pese a no tener que dibujarlos los tiene que calcular. Eso explicaría por que tarda lo mismo con el circulo sin rellenar de fcircle que con el circulo relleno y con independencia de si tiene traslucidez o es solido, lo cual, de ser cierto me lleva a la siguiente pregunta: ¿Cual es la manera de poner los pixeles a "NULL" para optimizar un map?
5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

Drumpi

No existe eso de "pixeles a null". Todos los pixels tienen un valor, independientemente de la profundidad de color. Otra cosa es cómo se traten.
En 16 bits, sólo el 0 absoluto era tratado diferente por el renderer, siendo el pixel de transparencia, igual que pasaba en 8 bits y en 1 bit. Podías tener algún consumo extra si usabas alguna paleta, es decir, un mapa de 8bits en modo 16 bits, o algún blendop para darles transparencia, invertir colores o crear algún efecto chulo, o incluso usar los flags aditivos o sustractivos.
En 32 hay que añadir al problema de manejar más colores y por tanto más información, que cada pixel tiene info de transparencia propio. Creo que el color 0 se sigue tratando de forma diferente, pero ya no por el renderer, sino sólo por collision.
Hala, como con 1001 procesos sólo va a 9 FPS, vamos a meterle 32 veces más, a ver si revienta.
(Drumpi epic moment)

Arcontus

Gracias Drumpi por la expicación, tiene sentido lo que comentas de que todos los pixeles se procesan indistintamente del color que posean.
Lo cual me lleva a la pregunta original:

Dado un mapa del mismo tamaño ¿Porque narices tarda mas fcircle que circle? ...si total, todos los pixeles van a procesarse.

¿Puede ser que haya algo que no funcione como debiera en la funcion fcircle?
5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

Drumpi

Yo me remito a mi planteamiento original. A menos que SplinterGu (o alguien que se haya leido el código) lo desmienta, yo apuesto por la cantidad de píxeles de más que genera draw_circle frente a draw_fcircle y su posterior procesamiento como objeto individual sobre un mapa.
Hala, como con 1001 procesos sólo va a 9 FPS, vamos a meterle 32 veces más, a ver si revienta.
(Drumpi epic moment)

SplinterGU

Quote from: Arcontus on December 02, 2017, 09:00:32 AM
Yo tengo otra teoría, quizás el vaciarlo del fcircle con fcircle a rgba(0,0,0,0) no deja los pixels a "NULL" o sin info y por ello, pese a no tener que dibujarlos los tiene que calcular. Eso explicaría por que tarda lo mismo con el circulo sin rellenar de fcircle que con el circulo relleno y con independencia de si tiene traslucidez o es solido, lo cual, de ser cierto me lleva a la siguiente pregunta: ¿Cual es la manera de poner los pixeles a "NULL" para optimizar un map?

por aca vienen los tiros... es correcta en parte tu teoria... lo que sucede es que el draw_* con rgba(0,0,0,0) no rellena con ese color, sino que hace el calculo de alpha con respecto al color de fondo, o sea, que todos los pixeles del circulo interior, resultan con un color ~rgba(175,175,175,0), en realidad 173 (segun test)... la unica que se me ocurre para limpiar seria que hagas 2 fcircle interior... asi...


PROCESS RadioAlcance1()
PRIVATE
    int tamano, radio;
END
BEGIN
    radio = 400;
    tamano = radio*2;
    graph = new_map(tamano+1, tamano+1, 32);
    drawing_map(0,graph);
    //Dibujamos un circulo relleno
    drawing_color(rgba(255,255,255,175));
    draw_fcircle(radio, radio, radio);
    //Dibujamos un circulo un pixel mas pequeño con el alpha al mínimo.
    drawing_color(rgba(0,0,0,255));
    draw_fcircle(radio, radio, radio - 1);

    drawing_color(rgba(0,0,0,0));
    draw_fcircle(radio, radio, radio - 1);

    z = -10;
    while(!(key(_esc)))
        x = rand(400, 600);
        y = rand(400, 600);
        FRAME;
    END
OnExit:
    map_unload(0,graph);
END


lo que hace esto, es con el primer rgba 0,0,0,255, al ser el alpha 255, el circulo interior se dibuja sin considerar el alpha, y quedan pixels con rgba 0,0,0,255.
ahora, con el 2do 0,0,0,0, como los pixles ya estan a 0, al hacer el calculo del alpha sobre el grafico que ya existe, queda un color rgba de 0,0,0,0.

se que es un poco confuso, pero es asi...

igualmente vas a notar que no llega a los mismos fps que la funcion 2, esto es porque el hacer un circulo de radio -1 no hace que la linea radio, tenga exactamente 1 pixel de ancho, los algoritmos de dibujado de circulo no son tan precisos... en mis pruebas la funcion 2 da unos 470-480fps y la 1 da unos 450fps... bastante cerca...

espero se haya entendido que el tema son los pixels con alphas, si bien tienen un alpha a 0, los componentes rgb resultan a otro valor (el resultante de la mezcla del fondo y el color del nuevo circulo)
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Arcontus

#7
Ostras! funciona tal y como dice Splinter! ha subido de 180 a 432fps simplemente añadiendo las dos lineas:
drawing_color(rgba(0,0,0,255));
draw_fcircle(radio, radio, radio - 1);

He implementado esto en mi juego y pasa de consumir 40fps el radio de alcance a solo 5!

Thx, karma++.
5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

Drumpi

No te acostarás sin aprender una cosa más :)
Hala, como con 1001 procesos sólo va a 9 FPS, vamos a meterle 32 veces más, a ver si revienta.
(Drumpi epic moment)

SplinterGU

Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Arcontus

Sería de agradecer y hasta cierto punto lógico que Bennu hiciera internamente los cambios necesarios para que cuando el programador ponga "drawing_color(rgba(0,0,0,0))" obtenga esos pixeles, sin tener que hacer "drawing_color(rgba(0,0,0,255))" previamente, ya que quien no lo sepa (o se olvide) tendrá una bajada de rendimiento "gratis" y considerable en su proyecto.

De hecho drawing_color(0,0,0,0), no hace lo que debería hacer si nos ceñimos a la descripcion de la funcion: "Tells Bennu to draw the coming drawings in a certain color."

Sugerencia: ¿Es posible este cambio como candidato para una nueva release?

Gracias y un saludo!
5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

SplinterGU

tendria que pensarlo, porque se que es confuso, pero depende de como funcionan los alphas.

no se, tendria que pensarlo, quizas si se puede suponer que cuando es 0 absoluto, significa borrar... y en los otros casos seria mezclar.

lo tengo que pensar con tiempo...
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Drumpi

Oye, ¿Y si en lugar de usar las funciones de 32bits se usan las de 16bits para "borrar" o para pintar sin tener en cuenta el canal alpha? ¿Se podría ganar rendimiento o por hacer las conversiones a 32bits sería peor solución?
Hala, como con 1001 procesos sólo va a 9 FPS, vamos a meterle 32 veces más, a ver si revienta.
(Drumpi epic moment)

SplinterGU

Quote from: Drumpi on December 20, 2017, 11:50:04 PM
Oye, ¿Y si en lugar de usar las funciones de 32bits se usan las de 16bits para "borrar" o para pintar sin tener en cuenta el canal alpha? ¿Se podría ganar rendimiento o por hacer las conversiones a 32bits sería peor solución?

buena pregunta
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

FreeYourMind