Como o SFML é tão rápido?
Eu preciso desenhar alguns gráficos em c ++, pixel por pixel em uma janela. Para fazer isso, crio uma janela SFML, sprite e textura. Eu desenho meus gráficos desejados em um array uint8_t e, em seguida, atualizo a textura e o sprite com ele. Este processo leva cerca de 2500 nós . Desenhar dois triângulos que preencham toda a janela leva apenas 10 us . Como essa enorme diferença é possível? Eu tentei multithreading o desenho pixel a pixel, mas a diferença de duas ordens de magnitude permanece. Também tentei desenhar os pixels usando um mapa de pontos, sem melhorias. Eu entendo que o SFML usa alguma aceleração de GPU em segundo plano, mas simplesmente fazer um loop e atribuir os valores à matriz de pixels já leva centenas de microssegundos.
Alguém conhece uma maneira mais eficaz de atribuir os valores de pixels em uma janela?
Aqui está um exemplo do código que estou usando para comparar a velocidade do desenho de triângulo e pixel a 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;
}
Respostas
O desenho gráfico em dispositivos modernos usando placas gráficas, e a velocidade do desenho depende de quantos triângulos nos dados você enviou para a memória gráfica. É por isso que desenhar dois triângulos é rápido.
Como você mencionou sobre multithreading, se você usa OpenGL (não me lembro o que SFML usa, mas deveria ser o mesmo), o que você pensa que está desenhando é basicamente enviar comandos e dados para placas gráficas, então multithreading aqui não é muito útil , a placa gráfica tem seu próprio thread para fazer isso.
Se você está curioso sobre como a placa gráfica funciona, este tutorial é o livro que você deve ler.
PS Conforme você edita sua pergunta, acho que a duração 2500us vs 10us é porque você for loop cria uma textura (mesmo se a textura for um fundo branco puro) (e o loop for, você provavelmente precisará começar a contar após o loop for) , e enviar textura para a placa gráfica precisa de tempo, enquanto o triângulo de desenho envia apenas vários pontos. Ainda assim, sugiro que você leia o tutorial, criar uma textura pixel por pixel potencialmente provar a falta de compreensão de como funciona a GPU.