首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >第一次图形-在软件渲染中性能不佳

第一次图形-在软件渲染中性能不佳
EN

Code Review用户
提问于 2014-07-01 15:41:18
回答 1查看 2K关注 0票数 5

我正在编写一个实用的音频插件,专门用于“全屏”(窗口可以自由调整大小)音频的可视化,比如光谱图、示波器和矢量显微镜。对于那些不知道的人,向量仪是一个坐标系,其中音频流中的每个样本都映射坐标(左是x,右通道是y)。下面是程序执行的可视化图像:

然而,我正经历着令人烦恼的性能,像1280x1024这样的对话框可以根据具体情况消耗100%的一个核心。

我目前希望我的渲染代码很糟糕,并且性能可以提高,因此我要求对下面的代码进行检查。这是我第一次写图形代码,所以请原谅我。到目前为止,我已经避开了大多数糟糕的过早优化(我也可以度量大多数没有做多少事情),并试图保持代码的良好模块化。

我已经包含了代码的相关部分,我希望它是清楚的发生了什么。这是绘画功能:

代码语言:javascript
复制
void CVectorScope::paint(Graphics & g)
{
    auto clockStart = cpl::Misc::ClockCounter();

    // erase previous content
    waveFormGraphics->fillAll(Colours::black);

    // .. draw graph here

    // render waveform of all audio samples in the buffer, interconnecting each new pair with a line to the previous
    renderGeneric(numSamples,
                // this is the 'fetch' lambda, that returns a sample from the audio buffer
                [&](std::size_t channel, std::size_t sample)
                {
                    return audioData[channel].directAccess(sample);
                },
                // this is the line-drawing lambda
                [](Image::BitmapData & data, int x, int y, int ox, int oy, float sampleFade)
                {
                    bDrawLine<float>(x, y, ox, oy,
                                    // the line drawing lambda uses following lambda to color pixels
                                    [&](int xx, int yy)
                                    {
                                        // get the pixel pointer to the second channel (green)
                                        auto p1 = (data.getPixelPointer(xx, yy) + 1);
                                        // color the pixel up to half of the remaining bits
                                        *p1 = *p1 + ((0xFF - *p1) >> 1) * sampleFade;
                                    }
                    );
                }
    );


    auto renderStop = cpl::Misc::ClockCounter();

    // 'transferring' software image to screen buffer?
    g.drawImageAt(waveForm, 0, 0);

    auto cyclesNow = cpl::Misc::ClockCounter();

    // .. print diagnostics below

}

下面是bDrawLine函数:

代码语言:javascript
复制
/*
http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
plots lines fast, but unaliased
*/
template <typename Ty, class Plot>
void inline bDrawLine(Ty x0, Ty y0, Ty x1, Ty y1, Plot plot)
{
    Ty dx = fastabs(x1 - x0);
    Ty dy = fastabs(y1 - y0);
    Ty sx, sy, err, e2;
    if (x0 < x1)
        sx = 1;
    else
        sx = -1;
    if (y0 < y1)
        sy = 1;
    else
        sy = -1;
    err = dx - dy;

    while (true)
    {
        plot(x0, y0);
        if (x0 == x1 && y0 == y1)
            break;
        e2 = err * 2;
        if (e2 > -dy)
        {
            err = err - dy;
            x0 = x0 + sx;
        }
        if (e2 < dx)
        {
            err = err + dx;
            y0 = y0 + sy;
        }
    }
}

使通用:

代码语言:javascript
复制
template<class Fetcher, class Plot>
void CVectorScope::renderGeneric(std::size_t numSamples, Fetcher fetch, Plot plot)
{
    float xn, yn, sleft, sright;
    int x, y;
    auto middleHeight = displaySize.getY() / 2 + displaySize.getHeight() / 2 - 2;
    auto middleWidth = displaySize.getX() / 2 + displaySize.getWidth() / 2 - 2;
    // stack reference to the software buffer image we draw on
    Image::BitmapData data(waveForm, Image::BitmapData::ReadWriteMode::writeOnly);
    float sampleFade = 1.0 / numSamples;

    // main loop, iterate over each sample and plot them
    for (int i = 0; i < numSamples; ++i)
    {
        // fetch sample
        sleft = fetch(0, i);
        sright = fetch(1, i);

        // rotate matrix
        xn = sleft * cosrol - sright * sinrol;
        yn = sleft * sinrol + sright * cosrol;

        sleft = xn;
        sright = yn;
        // hardclip
        if (fastabs(sright) > 1)
            continue;
        if (fastabs(sleft) > 1)
            continue;
        x = sleft * middleWidth + middleWidth;
        y = sright * middleHeight + middleHeight;

        plot(data, x, y, ox, oy, sampleFade * i);

        ox = x; oy = y;
    }
}

为了概述发生了什么以及我们正在谈论的数据量,下面是一系列事件:

  • 每个帧都会调用paint(),大约为60 for (变量)。
  • renderGeneric()循环整个音频缓冲区并绘制它。这可能长达1秒,对于一个44100采样来说,这意味着它必须绘制任意长度的44100条线,每秒钟60次。

直到现在,我只在OSX上测试了它,但是在Windows上性能更差。在OSX上,我在Windows上使用Xcode + LLVM和MSVC++ 12编译它。查看程序集,MSVC没有内联bDrawLine()调用(不过,所有lambdas都是内联的)。此外,MSVC不发出任何SIMD指令(或者说它发出的指令很好,但没有任何不是单标量的指令)。LLVM嵌入了所有的东西。

我使用rdtsc (包装在ClockCounter()中)来度量所花费的时间,它概述了在呈现和写入之间总共花费的时间。

下面是对1100x700窗口中某个声音的一些测量:

  1. Windows,软件上下文x64:
    • cpu时间: 113% (渲染73%,传输27%)

  2. Windows,openGL上下文(使用JUCE) x64:
    • cpu时间: 200% (渲染42%,传输58%)

  3. OSX,软件上下文x64:
    • cpu时间: 77% (渲染40%,传输60%)

  4. openGL上下文(使用JUCE),x64:
    • cpu时间: 51% (渲染73%,传输27%)

其中计算cpu时间的方法如下:

代码语言:javascript
复制
cputime = (totalClocksSpent * (1000.0 / refreshRate)) / (processorSpeedInGHZ * 1000 * 1000) * 100;

这显然不是一个精确的度量,但它概述了绘画功能使用的时间(在一个核心上)。注意,openGL上下文在单独的线程中运行。有趣的是,windows上的openGL传输性能要差得多。

目前,我使用JUCE处理图形,但我很快意识到使用软件上下文上的API函数编写代码并不能获得很好的性能,所以我重写了代码在软件位图(waveForm,其中waveformGraphics是写到waveForm映像的上下文)上呈现,后者在paint()的末尾被写到窗口的图形上下文中。

我对以下问题的答案/建议很感兴趣:

  1. 我是否达到了计算机性能的上限?(注:这是一个全新的顶级Macbook Pro) --我应该期待什么样的表现?我真没想到它会吃掉超过几个百分比的CPU,毕竟它不过是黑色背景上的几行而已。
  2. 如何减少仅用于传输所呈现的图像所花费的时间?真的吗?复制一个图像需要这么多时间吗?虽然看看它,传输1100x700像素60次每秒产生46兆字节/秒.那可真是太多了。
  3. 你会如何处理这种情况?全屏应用程序如何在不消耗一半cpu的情况下传输这么多数据?注意,1100x700甚至没有那么高的分辨率。
  4. 我在位图上画的方法怎么样?这样做还行吗?或者我应该考虑使用openGL或其他什么工具来完成渲染(我也没有经验!)
  5. 我怎样才能说服MSVC将电话内联?(注意,__forceinlinebDrawLine()没有影响)--我怀疑这是演出的很大一部分。
  6. 如何翻译Bresenham的线算法,它最优吗?还有关于代码的速度和质量的一般注释吗?
  7. 我会实现什么尝试使用SIMD本质/并行特性/循环展开吗?
EN

回答 1

Code Review用户

发布于 2014-07-08 20:21:12

以下是一些建议:

双缓冲

在图形处理器处理另一个缓冲区时,绘制到一个缓冲区(位图等)中。当图形处理器完成后,切换缓冲区。根据需要使用更多的缓冲区以适应速度差异。

使用线程

你可以用三个线程。线程1读取数据并存储到数据缓冲区中。线程2处理来自数据缓冲区的数据并写入图像缓冲区。线程3将图像缓冲区提供给图形处理器。

不要经常画画,画画是一项昂贵的工作。考虑一些需要执行的操作,如裁剪。相反,将多次绘制到缓冲区中,然后绘制到缓冲区中。例如,在缓冲区中执行4次绘制操作,然后绘制缓冲区。你刚刚节省了3次昂贵的油漆操作。

使用图书馆

使用一个图形库来检测您的图形硬件并利用尽可能多的硬件功能。一些GPU具有BitBlit功能,它可以在不使用CPU的情况下将内存中的矩形区域复制到显示器上。(占用CPU的时间)。

票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/55802

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档