【问题标题】:CGD: Drawing on screen in chunks / per row concurrently in drawRectCGD:在drawRect中以块/每行同时在屏幕上绘图
【发布时间】:2014-02-19 15:06:50
【问题描述】:

几天前我想在晚上写一些愚蠢的东西,我想我会在模拟器上观看元素动画来放松一下。但事实证明,这两天让我很头疼。

背景:

我想我会在 320 X 480 的屏幕上的每个像素上随机绘制一个字母。 我将有一个包含 26 个字母的数组,并使用 arc4Random % 26 来提取一个随机字母。

首先我使用了UIGraphicsBeginImageInContextWithOptions 并将图像添加到图像视图中,该视图被添加为我的视图的子视图。

完全没问题。

但后来我意识到,为什么要等呢?让我们做一个 2 for 循环(外部:480 -> 内部:320) 一旦第一个内部循环完成,将该数组发送到 dispatch_async 并让它在 drawRect 中绘制,然后当下一次迭代完成时,将其发送到另一个 dispatch_async 等等,直到两个循环都完成。

我的痛苦由此开始/-之后......

原因:

for 循环太紧太快。因此,在我发送包含 320 个元素的数组后,我最终将 2 个 for 循环包装在 dispatch_async 中,并在内部 for 循环中放置了 sleep(1);

但后来我意识到:通过使用sleep(1),它超越了想要更快绘图的目的。事实上,这比根本不使用GCD 需要更长的时间。超过 25 秒。

所以,我继续尝试使用semaphore,并在将dispatch_async 中的数组发送到drawRect 之前只有3200 个可用插槽时创建一个;

问题:

比赛条件。我会得到意外的数组越界错误。错误不一致;有时它发生得更晚,有时更早。我认为这与未正确使用 CGD 有关。

所以我的问题是:

我凭直觉知道这可以通过GCD 进行微调和解决。但我就是不知道怎么做。我无计可施。

我真正想要的是能够更新屏幕的绘图,而不是等待整个东西被绘制出来。要么在屏幕上逐点显示一个点,要么一个接一个地显示,而不必调用setNeedsDisplay,但最好改为调用setNeedsDisplayinRect

我想我会剖析绘图元素的各个部分,并将每个部分发送到它自己的 dispatch_async,每个部分只负责绘制它的部分,并且由于同时运行,它会在屏幕上非常快速地渲染绘图。

但是,我又一次没有想法,最终充满挫败感。

for(int y = 0; y < 480; y++)
{
    for(int x = 0; x < 320; x++)
    {
        //get a random letter here and insert it into the array (myArray)
    }
    dispatch_async(myQueue, ^
    {
       dispatch_async(dispatch_async_get_main_queue(),^
       {
           [self.myView processRowDrawing:myArray];
       }
    }
    [myArray removeAllObjects];
    //as mentioned: I tried to put a sleep() here because the loop is faster the the drawing
}

- (void)processRowDrawing:(NSMutableArray*)array
{
    for(x = 0; x < array.count; x++)
    {
       //cell here `setNeedsDisplayInRect:` to draw a row
    }
}

至于信号量的版本,我只是简单的在create和signal中设置了一个大数来调用[self.myView processRowDrawing:myArray];,但是由于绘图比for循环慢,所以没有用。

是否有可能用GCD 以一种漂亮而优雅的方式解决这个问题,方法是同时分割绘图部分并将其发送到drawRect 并让这些部分一点一点地显示在屏幕上,而不是等待它整个?

逐行或逐格并看到它们被绘制?同样,我想在这里使用GCD

为此失眠了。请指教。

附:我已经尝试过使用CADisplayLink

【问题讨论】:

    标签: grand-central-dispatch


    【解决方案1】:

    这种情况不太可能通过并行处理变得更快。让我们探讨一下为什么...

    让我们在简单的单线程案例中分解这里正在完成的“工作”。您将 26 个字母中的 1 个绘制到 320 * 480 网格中的每个像素中(即 153,600 次)。这没有任何意义,但让我们开始吧。除非您没有提及其他内容,否则 CoreText 将缓存所有设置成本,以便在第一次渲染这些字母时渲染 26 个字形中的每一个。这意味着在第一次渲染后渲染每个字母的边际成本是恒定的。当您将最后一个字母渲染到最后一个像素时,您将一个完全渲染的 320 * 480 位图交给 UIKit 显示,然后就完成了。这个成本与这个讨论无关。我们称将单个字母渲染成一个像素的平均边际成本A。单线程案例可以花费的最短时间为 153,600 * A

    现在让我们讨论并行化此渲染所涉及的内容。假设将它分成两个 320 x 240 像素的图块。在这种情况下,您希望整个任务需要 153,600 * A / 2 秒。但是并行化这个工作流还有其他成本。就在我的脑海中:

    1. 安排/切换这些任务所涉及的时间
    2. 为每个并行背景渲染设置图形上下文所涉及的时间
    3. 将单独的图块组合成与视图对应的单个纹理所涉及的时间。

    我的猜测是那里的长杆将是#3。为了使并行化显示任何收益,这些额外成本总共必须小于 153,600 * A / 2 秒。仅划分为两个图块会产生绝对最小的开销。任何进一步的细分只会增加开销。如果将其分成两块并不快,那么肯定没有大于两块的块会更快。

    您似乎假设渲染字母的成本使并行性的开销相形见绌。我怀疑你错了。

    【讨论】:

    • 感谢您的回复。对,在一个像素中画一个字母很疯狂。我最初的意图是在屏幕上一个一个地画一些字母。更大的。但是后来我想画一个点,但是因为我已经做了一个随机的字母数组,我就跟着它去了。因此,假设我只想在 320 x 480 的屏幕上绘制一个点。是否可以同时逐行/或逐格绘制它们?假设我切出了 16 个网格,每个网格的尺寸为 60x120,我设置了 16 个并发队列,每个负责绘制其尺寸和(续)
    • 和..而不是调用setNeedsDisplay 调用setNeedsDisplayInRect 这样我就不必刷新整个屏幕,就好像我要绘制它们之一并刷新屏幕320x480 次?是的,你的计算是对的,但我认为拥有多个并发队列至少会“更快”,这样我就不必等到整个屏幕完成绘制后才能看到屏幕。这确实是我想做的有趣的事情,但最终我很想知道使用 GCD 是否会更好地实现。
    • 对没有成功感到沮丧。整个想法是通过drawRect 在屏幕上放一些东西,并想知道在屏幕上显示一些东西是否会“更快”还是更好,而不必等待整个事情完成。最令人沮丧的部分是在 for 循环中调用 setNeedsDisplay。很多时候,我们通过使用循环来创建东西并添加到数组中,但是能够在循环中立即调用 UI 更新而不需要在调用 UI 更新之前等待循环完成会很好。这就是我在做这件事时意识到的。
    • 因此问题。如果我的情况不能通过使用队列更好地实现。您能否引用我的一个示例案例,该示例使用 GCD 以尽可能快(且正确)的方式同时在循环中协调 UI 更新?提前致谢。
    • 你应该看看我对类似问题的回答:stackoverflow.com/a/20404359/438982
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-04
    • 2014-01-03
    • 1970-01-01
    • 1970-01-01
    • 2010-11-21
    相关资源
    最近更新 更多