Comment SFML est-il si rapide?

Aug 31 2020

J'ai besoin de dessiner des graphiques en C ++, pixel par pixel sur une fenêtre. Pour ce faire, je crée une fenêtre SFML, un sprite et une texture. Je dessine mes graphiques souhaités dans un tableau uint8_t, puis je mets à jour la texture et le sprite avec. Ce processus prend environ 2500 nous . Dessiner deux triangles qui remplissent toute la fenêtre ne prend que 10 us . Comment cette énorme différence est-elle possible? J'ai essayé le multithreading du dessin pixel par pixel, mais la différence de deux ordres de grandeur demeure. J'ai également essayé de dessiner les pixels à l'aide d'une carte de points, sans amélioration. Je comprends que SFML utilise une accélération GPU en arrière-plan, mais le simple fait de boucler et d'attribuer les valeurs au tableau de pixels prend déjà des centaines de microsecondes.

Quelqu'un connaît-il un moyen plus efficace d'attribuer les valeurs de pixels dans une fenêtre?

Voici un exemple du code que j'utilise pour comparer la vitesse du triangle et du dessin pixel par pixel:

#include <SFML/Graphics.hpp>
#include <chrono>
using namespace std::chrono;
#include <iostream>
#include<cmath>

uint8_t* pixels;

int main(int, char const**)
{
    const unsigned int width=1200;
    const unsigned int height=1200;
    sf::RenderWindow window(sf::VideoMode(width, height), "MA: Rasterization Test");
    pixels = new uint8_t[width*height*4];
    sf::Texture pixels_texture;
    pixels_texture.create(width, height);
    sf::Sprite pixels_sprite(pixels_texture);
    sf::Clock clock;
    sf::VertexArray triangle(sf::Triangles, 3);

    triangle[0].position = sf::Vector2f(0, height);
    triangle[1].position = sf::Vector2f(width, height);
    triangle[2].position = sf::Vector2f(width/2, height-std::sqrt(std::pow(width,2)-std::pow(width/2,2)));
    triangle[0].color = sf::Color::Red;
    triangle[1].color = sf::Color::Blue;
    triangle[2].color = sf::Color::Green;
    
    
    while (window.isOpen()){
        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed) {
                window.close();
            }
            if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) {
                window.close();
            }
        }

        window.clear(sf::Color(255,255,255,255));

        // Pixel-by-pixel
        int us = duration_cast< microseconds >(system_clock::now().time_since_epoch()).count();
        for(int i=0;i!=width*height*4;++i){
            pixels[i]=255;
        }
        pixels_texture.update(pixels);
        window.draw(pixels_sprite);
        int duration=duration_cast< microseconds >(system_clock::now().time_since_epoch()).count()-us;
        std::cout<<"Background: "<<duration<<" us\n";

        // Triangle
        us = duration_cast< microseconds >(system_clock::now().time_since_epoch()).count();
        window.draw(triangle);
         duration=duration_cast< microseconds >(system_clock::now().time_since_epoch()).count()-us;
        std::cout<<"Triangle: "<<duration<<" us\n";

        window.display();
    }
    return EXIT_SUCCESS;
}

Réponses

5 Noname Aug 31 2020 at 22:00

Dessin graphique dans les appareils modernes utilisant des cartes graphiques, et la vitesse de dessin dépend du nombre de triangles dans les données que vous avez envoyées à la mémoire graphique. C'est pourquoi le simple fait de dessiner deux triangles est rapide.

Comme vous l'avez mentionné à propos du multithreading, si vous utilisez OpenGL (je ne me souviens pas de ce que SFML utilise, mais cela devrait être le même), ce que vous pensez dessiner est essentiellement d'envoyer des commandes et des données à des cartes graphiques, donc le multithreading ici n'est pas très utile , la carte graphique a son propre fil pour ce faire.

Si vous êtes curieux de savoir comment fonctionne la carte graphique, ce tutoriel est le livre que vous devriez lire.

PS Lorsque vous éditez votre question, je suppose que la durée 2500us vs 10us est due au fait que vous créez une texture pour la boucle for (même si la texture est un fond blanc pur) (et la boucle for, vous devez probablement commencer à compter après la boucle for) , et envoyer la texture à la carte graphique ont besoin de temps, tandis que dessiner le triangle n'envoie que plusieurs points. Pourtant, je suggère de lire le tutoriel, de créer une texture pixel par pixel prouver potentiellement le manque de compréhension du fonctionnement du GPU.