【问题标题】:How is SFML so fast?SFML 怎么这么快?
【发布时间】:2020-12-19 16:51:38
【问题描述】:

我需要在 C++ 中逐个像素地在窗口上绘制一些图形。为此,我创建了一个 SFML 窗口、精灵和纹理。我将所需的图形绘制到 uint8_t 数组中,然后用它更新纹理和精灵。这个过程大约需要 2500 我们。绘制两个填满整个窗口的三角形只需要 10 us。这种巨大的差异怎么可能?我已经尝试对逐像素绘图进行多线程处理,但仍然存在两个数量级的差异。我也尝试过使用点图绘制像素,但没有任何改进。我知道 SFML 在后台使用了一些 GPU 加速,但简单地循环并将值分配给像素数组已经花费了数百微秒。

有谁知道在窗口中分配像素值的更有效方法?

这是我用来比较三角形和逐像素绘制速度的代码示例:

#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;
}

【问题讨论】:

  • 在后一种情况下,您正在测量将渲染命令排入 GPU 所需的时间,而不是绘制时间。请注意,实际渲染时间应该仍然非常快,因为任何一半像样的 GPU 都应该能够在此类任务上轻松击败 CPU。
  • 绘图和着色都是在显卡上完成的。 SFML 将仅发送卡(图形驱动程序)说明。
  • 感谢您的回答。我不认为 GPU 渲染时间没有被测量,只是排队时间。你知道我有一种方法可以测量 gpu 绘制对象所需的时间吗?
  • 还请注意,您基本上几乎所有时间都在更新单个像素值。如果我用 memset 替换像素 [i]=255 循环,对我来说一切都会快 3 倍——如果可能的话,更新原地纹理会更有效。但是 YMMV。
  • @radioflash 我相信 memset 只有在所有像素都设置为相同值的情况下才能运行得更快。但是,我需要为每个像素计算单独的颜色。

标签: c++ sfml pixel


【解决方案1】:

使用图形卡在现代设备中绘制图形,绘制速度取决于您发送到图形内存的数据中有多少三角形。这就是为什么只画两个三角形很快。

正如您提到的多线程,如果您使用 OpenGL(我不记得 SFML 使用什么,但应该是一样的),您认为您正在绘制的基本上是向显卡发送命令和数据,所以这里的多线程是不是很有用,显卡有自己的线程来做这个。

如果您对显卡的工作原理感到好奇,this 教程是 你应该读的书。

附:当你编辑你的问题时,我猜2500us vs 10us的持续时间是因为你for循环创建了一个纹理(即使纹理是纯白色背景)(和for循环,你可能需要在for循环之后开始计数),并将纹理发送到显卡需要时间,而绘制三角形只发送几个点。尽管如此,我还是建议阅读教程,逐个像素地创建纹理,这可能会证明对 GPU 工作原理的错误理解。

【讨论】:

    猜你喜欢
    • 2011-06-02
    • 2012-03-18
    • 2014-05-09
    • 2016-03-19
    • 2012-09-19
    • 2010-09-13
    • 1970-01-01
    • 1970-01-01
    • 2017-04-30
    相关资源
    最近更新 更多