在辅助线程上调用[EAGLContext presentRenderBuffer:]是否安全?

时间:2016-01-21 11:07:20

标签: ios multithreading opengl-es

我有(多个)UIViews类型为CAEAGLLayer的图层,并且能够在辅助线程上连接到这些图层的渲染缓冲区上调用[EAGLContext presentRenderBuffer:],而不会一种图形故障。

我原本希望看到至少一些撕裂,因为在主线程上更新了合成这些UIViews的其他UI。 CAEAGLLayer(我kEAGLDrawablePropertyRetainedBacking设置为NO)在幕后做了一些双重缓冲吗?

我只是想明白为什么会这样有效......

示例:

BView是一个UIView子类,拥有一个帧缓冲区,其渲染缓冲区存储分配给其OpenGLES层,位于共享EAGLContext中:

@implementation BView

-(id) initWithFrame:(CGRect)frame context:(EAGLContext*)context
{
    self = [super initWithFrame:frame];

    // Configure layer
    CAEAGLLayer* eaglLayer = (CAEAGLLayer*)self.layer;
    eaglLayer.opaque = YES;
    eaglLayer.drawableProperties = @{ kEAGLDrawablePropertyRetainedBacking : [NSNumber numberWithBool:NO], kEAGLDrawablePropertyColorFormat : kEAGLColorFormatSRGBA8 };

    // Create framebuffer with renderbuffer attached to layer    
    [EAGLContext setCurrentContext:context];
    glGenFramebuffers( 1, &FrameBuffer );
    glBindFramebuffer( GL_FRAMEBUFFER, FrameBuffer );    
    glGenRenderbuffers( 1, &RenderBuffer );
    glBindRenderbuffer( GL_RENDERBUFFER, RenderBuffer );
    [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(id<EAGLDrawable>)self.layer];    
    glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, RenderBuffer );

    return self;
}

+(Class) layerClass
{
    return [CAEAGLLayer class];
}`

UIViewController在初始时在主线程上添加BView实例:

BView* view = [[BView alloc] initWithFrame:(CGRect){ 0.0, 0.0, 75.0, 75.0 } context:Context];
[self.view addSubView:view];

在辅助线程上,渲染到BView中的帧缓冲区并呈现它;在这种情况下,它定期从视频AVCaptureDevice回调:

-(void) captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection
{    
    [EAGLContext setCurrentContext:bPipe->Context.GlContext];

    // Render into framebuffer ...

    // Present renderbuffer
    glBindRenderbuffer( GL_RENDERBUFFER, BViewsRenderBuffer );
    [Context presentRenderbuffer:GL_RENDERBUFFER];
}

1 个答案:

答案 0 :(得分:0)

以前不行。如果缓冲区是在除主线程之外的任何缓冲区上呈现的,那么更新视图时会出现几个问题。这似乎已经有一段时间了,但是你自己承担风险就像你一样实施它。以后的版本可能会遇到问题以及一些旧版本可能仍然存在(不是说你需要支持一些旧的操作系统版本)。

关于内部如何运作,Apple总是有点关闭,但我们可能会猜到很多东西。由于iOS似乎是唯一使用主缓冲区作为FBO(帧缓冲区对象)的平台,我希望主帧缓冲区无法进行开发,并且当您呈现渲染缓冲区时,主FBO实际上会重绘为主帧缓冲区。我最后一次检查呈现缓冲区的方法将阻止当前线程,并且似乎仅限于屏幕刷新率(大多数情况下为60FPS),这意味着仍然存在一些锁定机制。应该进行一些额外的测试,但是我希望有一些缓冲池需要重新绘制到主缓冲区,在那里池中只有一个唯一的缓冲区id可以存在,或者调用线程被阻塞。这将导致对当前渲染缓冲区的第一次调用根本不会被阻塞,但是如果先前的缓冲区尚未被重绘,则每个顺序都将被删除。

如果这是真的那么是,在某些时候必须进行双缓冲,因为您可能会立即继续绘制到缓冲区。由于渲染缓冲区在帧上具有相同的id,因此可能无法交换(据我所知),但可以将其重绘/复制到另一个缓冲区(很可能是纹理),可以在任何给定时间动态完成。在此过程中,当您第一次显示缓冲区时,您将缓冲区复制到将被锁定的纹理。当屏幕刷新时,纹理将被收集和解锁。因此,如果此纹理被锁定,您的演示文稿调用将阻止该线程,否则它将继续平滑。很难说这是双缓冲。它有2个缓冲区,但它仍然可以使用锁定机制。

我希望你能理解为什么会这样。它与在单独的线程上运行的单独共享上下文中加载大型数据结构时使用的过程几乎相同。

不幸的是,大部分仍然是猜测。