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

然而,我正经历着令人烦恼的性能,像1280x1024这样的对话框可以根据具体情况消耗100%的一个核心。
我目前希望我的渲染代码很糟糕,并且性能可以提高,因此我要求对下面的代码进行检查。这是我第一次写图形代码,所以请原谅我。到目前为止,我已经避开了大多数糟糕的过早优化(我也可以度量大多数没有做多少事情),并试图保持代码的良好模块化。
我已经包含了代码的相关部分,我希望它是清楚的发生了什么。这是绘画功能:
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函数:
/*
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;
}
}
}使通用:
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窗口中某个声音的一些测量:
其中计算cpu时间的方法如下:
cputime = (totalClocksSpent * (1000.0 / refreshRate)) / (processorSpeedInGHZ * 1000 * 1000) * 100;这显然不是一个精确的度量,但它概述了绘画功能使用的时间(在一个核心上)。注意,openGL上下文在单独的线程中运行。有趣的是,windows上的openGL传输性能要差得多。
目前,我使用JUCE处理图形,但我很快意识到使用软件上下文上的API函数编写代码并不能获得很好的性能,所以我重写了代码在软件位图(waveForm,其中waveformGraphics是写到waveForm映像的上下文)上呈现,后者在paint()的末尾被写到窗口的图形上下文中。
我对以下问题的答案/建议很感兴趣:
__forceinline对bDrawLine()没有影响)--我怀疑这是演出的很大一部分。发布于 2014-07-08 20:21:12
以下是一些建议:
双缓冲
在图形处理器处理另一个缓冲区时,绘制到一个缓冲区(位图等)中。当图形处理器完成后,切换缓冲区。根据需要使用更多的缓冲区以适应速度差异。
使用线程
你可以用三个线程。线程1读取数据并存储到数据缓冲区中。线程2处理来自数据缓冲区的数据并写入图像缓冲区。线程3将图像缓冲区提供给图形处理器。
不要经常画画,画画是一项昂贵的工作。考虑一些需要执行的操作,如裁剪。相反,将多次绘制到缓冲区中,然后绘制到缓冲区中。例如,在缓冲区中执行4次绘制操作,然后绘制缓冲区。你刚刚节省了3次昂贵的油漆操作。
使用图书馆
使用一个图形库来检测您的图形硬件并利用尽可能多的硬件功能。一些GPU具有BitBlit功能,它可以在不使用CPU的情况下将内存中的矩形区域复制到显示器上。(占用CPU的时间)。
https://codereview.stackexchange.com/questions/55802
复制相似问题