【问题标题】:OpenGL GLPaint threaded renderingOpenGL GLPaint 线程渲染
【发布时间】:2014-04-29 23:46:36
【问题描述】:

我目前正在使用一个基于 Apple 的 GLPaint 示例的库,用于在 Open GL 中的屏幕上绘图。目前,每当画布保存和恢复会话时,都会绘制线条(可以看到进度),如果要渲染很多点,则需要相当长的时间。有没有办法让它并行或更快地渲染?

这是我正在使用的绘图代码:

CGPoint start = step.start;
CGPoint end = step.end;

// Convert touch point from UIView referential to OpenGL one (upside-down flip)
CGRect bounds = [self bounds];
start.y = bounds.size.height - start.y;
end.y = bounds.size.height - end.y;

static GLfloat*     vertexBuffer = NULL;
static NSUInteger   vertexMax = 64;
NSUInteger          vertexCount = 0,
count,
i;

[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

// Convert locations from Points to Pixels
CGFloat scale = self.contentScaleFactor;
start.x *= scale;
start.y *= scale;
end.x *= scale;
end.y *= scale;

// Allocate vertex array buffer
if(vertexBuffer == NULL)
    vertexBuffer = malloc(vertexMax * 2 * sizeof(GLfloat));

// Add points to the buffer so there are drawing points every X pixels
count = MAX(ceilf(sqrtf((end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y)) / kBrushPixelStep), 1);
for(i = 0; i < count; ++i) {
    if(vertexCount == vertexMax) {
        vertexMax = 2 * vertexMax;
        vertexBuffer = realloc(vertexBuffer, vertexMax * 2 * sizeof(GLfloat));
    }

    vertexBuffer[2 * vertexCount + 0] = start.x + (end.x - start.x) * ((GLfloat)i / (GLfloat)count);
    vertexBuffer[2 * vertexCount + 1] = start.y + (end.y - start.y) * ((GLfloat)i / (GLfloat)count);
    vertexCount += 1;
}

// Render the vertex array
glVertexPointer(2, GL_FLOAT, 0, vertexBuffer);
glDrawArrays(GL_POINTS, 0, (int)vertexCount);

// Display the buffer
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];

【问题讨论】:

  • 我们在谈论多少分?我解释代码的方式是,它需要两个屏幕位置(可能来自触摸输入),并在每个kBrushPixelStep 像素之间绘制一个点。积分应该不会这么多吧?还是你重复调用我们看到的代码,startend 的值不同?
  • @RetoKoradi 代码被重复调用,一个包含许多步骤(每个都有开始和结束坐标)的数组调用每个步骤的函数

标签: ios objective-c opengl-es opengl-es-2.0


【解决方案1】:

OpenGL 不是多线程的。您必须从单个线程提交 OpenGL 命令。

你有几个选择:

  1. 您可以将代码分解为使用并发来构建您发送到 OpenGL 的数据,然后在数据全部可用后将其提交给 OpenGL API。

  2. 您可以重构它以使用着色器进行计算。这将计算从 CPU 推到 GPU 上,GPU 针对并行操作进行了高度优化。

您上面的代码在 for 循环中使用 realloc 重复重新分配缓冲区。这是非常低效的,因为内存分配是现代操作系统上最慢的基于 RAM 的操作之一。您应该重构代码以预先计算内存缓冲区的最终大小,然后以最终大小分配缓冲区一次,并且根本不使用 realloc。这应该可以让您毫不费力地提高数倍的速度。

浏览您的代码,重构您的 for 循环以将顶点计算分解为块并将这些块提交给 GCD 进行并发处理应该一点也不难。诀窍是将任务分解为足够大的工作单元以从并行处理中受益(在设置任务以在后台队列中运行时存在一定的开销。您希望在每个工作单元中做足够的工作以让这些开销值得。)

【讨论】:

  • 发布的代码不会在每次通过循环时重新分配。每次将其填充到当前大小时,它都会分配两倍的大小。这是动态调整缓冲区大小的一种非常有效的方法。当然如果能提前计算出大小就更好了。
  • 我在发表评论后就意识到了这一点,但留下了它,因为重新分配根本是缓慢且不必要的。正如我所说,内存分配是最慢的基于内存的操作之一。
【解决方案2】:

我相信上面 cmets 中的对话框揭示了您的性能问题的主要部分。除非我完全误解了,您的代码的高级结构目前看起来像这样:

loop over steps
    calculate list of points from start/end points
    render list of points
    present the renderbuffer
end loop

只有在所有步骤都被渲染后才呈现渲染缓冲区应该会更快:

loop over steps
    generate list of points from start/end points
    draw list of points
end loop
present the renderbuffer

更好的是,为每个步骤生成一个顶点缓冲区对象(又名 VBO)作为创建它的一部分,并将该步骤的点的坐标存储在缓冲区中。那么你的绘制逻辑就变成了:

loop over steps
    bind VBO for step
    draw content of VBO
end loop
present the renderbuffer

【讨论】:

    猜你喜欢
    • 2012-06-21
    • 1970-01-01
    • 1970-01-01
    • 2011-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多