循环中单个像素的渲染速度

时间:2015-12-17 16:29:24

标签: ios objective-c loops draw

我正在努力将单个像素绘制到UIView以创建分形图像。我的问题是我的渲染速度。我目前正在运行这个循环260,000次,但想渲染更多像素。实际上,在iPad Mini上运行大约需要5秒钟。

之前我使用的是UIBezierPath,但这甚至有点慢(约7秒)。我一直在查看NSBitMap内容,但我不确定这是否会加速它或如何实现它。

我还在考虑尝试将环路中的像素存储到数组中,然后在循环后将它们全部绘制在一起。尽管如此,我不太确定存储然后从数组中检索像素的最佳过程是什么。

任何有关加快此过程的帮助都会很棒。

for (int i = 0; i < 260000; i++) {

    float RN = drand48();

    for (int i = 1; i < numBuckets; i++) {
        if (RN < bucket[i]) {
            col = i;
            CGContextSetFillColor(context, CGColorGetComponents([UIColor colorWithRed:(colorSelector[i][0]) green:(colorSelector[i][1]) blue:(colorSelector[i][2]) alpha:(1)].CGColor));
            break;
        }
    }

    xT = myTextFieldArray[1][1][col]*x1 + myTextFieldArray[1][2][col]*y1 + myTextFieldArray[1][5][col];
    yT = myTextFieldArray[1][3][col]*x1 + myTextFieldArray[1][4][col]*y1 + myTextFieldArray[1][6][col];

    x1 = xT;
    y1 = yT;
    if (i > 10000) {
        CGContextFillRect(context, CGRectMake(xOrigin+(xT-xMin)*sizeScalar,yOrigin-(yT-yMin)*sizeScalar,.5,.5));

    }
    else if (i < 10000) {
        if (x1 < xMin) {
            xMin = x1;
        }
        else if (x1 > xMax) {
            xMax = x1;
        }
        if (y1 < yMin) {
            yMin = y1;
        }
        else if (y1 > yMax) {
            yMax = y1;
        }
    }
    else if (i == 10000) {
        if (xMax - xMin > yMax - yMin) {
            sizeScalar = 960/(xMax - xMin);
            yOrigin=1000-(1000-sizeScalar*(yMax-yMin))/2;
        }
        else {
            sizeScalar = 960/(yMax - yMin);
            xOrigin=(1000-sizeScalar*(xMax-xMin))/2;
        }
    }
}

修改

我创建了一个多维数组来存储UIColors,所以我可以使用位图来绘制我的图像。它明显更快,但我现在的颜色不合适。

这是我将UIColors存储到数组中的地方:

int xPixel = xOrigin+(xT-xMin)*sizeScalar;
int yPixel = yOrigin-(yT-yMin)*sizeScalar;
pixelArray[1000-yPixel][xPixel] = customColors[col];

这是我的绘画内容:

CGDataProviderRef provider = CGDataProviderCreateWithData(nil, pixelArray, 1000000, nil);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

CGImageRef image = CGImageCreate(1000,
                                 1000,
                                 8,
                                 32,
                                 4000,
                                 colorSpace,
                                 kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipLast,
                                 provider,
                                 nil, //No decode
                                 NO,  //No interpolation
                                 kCGRenderingIntentDefault); // Default rendering

CGContextDrawImage(context, self.bounds, image);

不仅颜色不是它们应该是的颜色,而且每次渲染图像时,颜色都与前一次完全不同。我一直在用颜色测试不同的东西,但我仍然不知道为什么颜色是错误的,而且我更加困惑他们如何不断变化。

3 个答案:

答案 0 :(得分:6)

每像素绘图 - 每个像素的复杂计算,如分形渲染 - 是您可以要求计算机执行的最困难的事情之一。这里的每个其他答案都涉及其困难的一个方面,但这并不是全部。 (幸运的是,这种渲染也是现代硬件优化的东西,如果你知道要问什么。我会做到这一点。)

@jcaron和@JustinMeiners都注意到CoreGraphics中的矢量绘图操作(甚至是矩形填充)会对基于CPU的光栅化造成损失。操纵位图数据的缓冲区会更快,但速度要快得多。

将缓冲区放到屏幕上也需要时间,特别是如果您不得不经历创建位图图像缓冲区然后在CG上下文中绘制它们的过程 - 这会做很多顺序在CPU上绘制工作和大量内存带宽工作来复制该缓冲区。所以@JustinMeiners是正确的,直接访问GPU纹理内存将是一个很大的帮助。

但是,如果您仍在使用CPU代码填充缓冲区,那么您仍会受到两项费用的限制(最好,如果您天真地这样做会更糟):

  • 渲染每个像素的连续工作
  • 渲染时从纹理内存到帧缓冲区的内存传输成本

@ JustinMeiners&#39;答案对他的用例很有用 - 图像序列是预渲染的,因此他确切地知道每个像素将是什么,他只需要将其转换为纹理存储器。但是您的用例需要大量的每像素计算。

幸运的是,每像素计算是GPU的设计目标!欢迎来到像素着色器的世界。对于屏幕上的每个像素,您可以运行一个独立的计算来确定该点与分形集之间的关系,从而确定绘制它的颜色。可以同时为多个像素并行运行该计算,并且输出直接进入屏幕,因此没有内存开销将位图转储到帧缓冲区。

在iOS上使用像素着色器的一种简单方法是SpriteKit - 它可以为您处理大部分必要的OpenGL / Metal设置,因此您需要编写的是GLSL中的每像素算法(实际上,它是在金属支持的设备上自动转换为金属着色器语言的GLSL)。 Here's a good tutorial on thathere's another on OpenGL ES pixel shaders for iOS in general

答案 1 :(得分:2)

如果你想最大化渲染速度,我建议使用位图渲染。矢量栅格化速度要慢得多,CGContext绘图并不适合高性能实时渲染。

我遇到了类似的技术挑战,发现CVOpenGLESTextureCacheRef是最快的。纹理缓存允许您将位图直接上载到图形内存中以便快速渲染。渲染使用OpenGL,但因为它只是2D全屏图像 - 你真的不需要了解OpenGL才能使用它。

你可以看到我在这里看到的一个使用纹理缓存的例子:https://github.com/justinmeiners/image-sequence-streaming

我原来的问题与此有关: How to directly update pixels - with CGImage and direct CGDataProvider

我的项目从文件中呈现位图,所以它有点不同,但您可以查看ISSequenceView.m,了解如何使用纹理缓存并设置OpenGL进行此类渲染。

您的渲染程序可能类似于:

1. Draw to buffer (raw bytes)
2. Lock texture cache
3. Copy buffer to texture cache
4. Unlock texture cache.
5. Draw fullscreen quad with texture 

答案 2 :(得分:2)

如果你真的想单独更改许多不同的像素,最好的选择可能是分配一块内存(大小为width * height * bytes per pixel),直接在内存中进行更改,然后将整个内容转换为使用CGBitmapContextCreateWithData

立即创建位图

可能有更快的方法(见贾斯汀的答案)。