gpt4 book ai didi

ios - 如何从iOS上的Grand Central Dispatch Queue异步绘制到GLKit的OpenGL ES上下文

转载 作者:行者123 更新时间:2023-12-01 22:59:49 26 4
gpt4 key购买 nike

我试图将冗长的OpenGL绘制操作移到GCD队列中,以便在GPU磨削的同时完成其他工作。与在我的应用程序中添加真正的线程相比,我更喜欢使用GCD进行此操作。从字面上看,我想做的就是

  • 在glDrawArrays()调用上不阻塞,因此当GL渲染变得非常慢时,UI的其余部分可以保持响应。
  • 当我们无论如何都没有完成它们时,请放下glDrawArrays()调用(不要建立只会不断增长的帧队列)

  • 在苹果网站上,文档说:

    GCD和NSOperationQueue对象可以在自己选择的线程上执行任务。他们可以创建专门用于该任务的线程,或者可以重用现有线程。但是无论哪种情况,您都不能保证哪个线程执行任务。对于OpenGL ES应用程序,这意味着:


  • 每个任务必须在执行任何OpenGL ES命令之前设置上下文。
  • 访问同一上下文的两个任务可能永远不会同时执行。
  • 每个任务都应在退出之前清除线程的上下文。


  • 听起来很简单。

    为了简单起见,我首先从新的Apple模板的骨干版本开始,该版本出现在“OpenGL ES”游戏的“新项目”对话框中。实例化它,编译并运行时,您应该看到两个多维数据集在灰色字段上旋转。

    在该代码中,我添加了一个GCD队列。从 ViewController.m的接口部分开始:
    dispatch_queue_t openGLESDrawQueue;

    然后在 ViewController viewDidLoad中设置它们:
    openGLESDrawQueue = dispatch_queue_create("GLDRAWINGQUEUE", NULL);

    最后,我对CADisplayLink最终触发的 drawInRect方法进行了非常小的更改:
    - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
    {
    void (^glDrawBlock)(void) = ^{
    [EAGLContext setCurrentContext:self.context];
    glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glBindVertexArrayOES(_vertexArray);

    // Render the object with GLKit
    [self.effect prepareToDraw];

    glDrawArrays(GL_TRIANGLES, 0, 36);

    // Render the object again with ES2
    glUseProgram(_program);

    glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0, _modelViewProjectionMatrix.m);
    glUniformMatrix3fv(uniforms[UNIFORM_NORMAL_MATRIX], 1, 0, _normalMatrix.m);

    glDrawArrays(GL_TRIANGLES, 0, 36);
    };
    dispatch_async(openGLESDrawQueue, glDrawBlock);
    }

    这是行不通的。图纸变得疯狂。不过,使用 dispatch_sync()与相同的块进行绘制可以正常工作。

    让我们仔细检查一下苹果的列表:
  • 每个任务必须在执行任何OpenGL ES命令之前设置上下文。
  • 好的。我正在设置上下文。它的Objective-C对象指针的生存期比该块要长,因此应该将其关闭。另外,我可以在调试器中检查它们,它们很好。另外,当我从dispatch_sync绘制时,它也可以工作。因此,这似乎不是问题。
  • 访问同一上下文的两个任务可能永远不会同时执行。
  • 设置GL上下文后,唯一访问该上下文的代码就是此方法中的代码,而该方法又位于此块中。由于这是一个串行队列,因此无论如何一次都只能绘制一个实例。此外,如果我添加了synchronized(self.context){}块,它不会解决任何问题。另外,在其他绘图速度很慢的代码中,我添加了一个信号量,以在前一个尚未完成并且丢弃帧很好时(根据它吐出的NSLog()消息)跳过向队列添加块的信号,但是没有修复工程图。但是,有些我看不到的GLKit代码有可能以无法从主线程理解的方式操纵上下文。这是我目前评分第二高的理论,尽管事实是sync()不会改变问题,并且OpenGL Profiler不会显示任何线程冲突。
  • 每个任务都应在退出之前清除线程的上下文。
  • 我不清楚这是什么意思。 GCD线程的上下文?没关系。我们不会在队列的上下文中添加任何内容,因此没有需要清理的内容。我们要绘制的EAGLContext?我不知道我们还能做什么。当然不是完全清除它,只会删除所有内容。另外,Sunset Lake's Molecules中有一些代码如下所示:

  • 码:
    dispatch_async(openGLESContextQueue, ^{
    [EAGLContext setCurrentContext:context];
    GLfloat currentModelViewMatrix[9];
    [self convert3DTransform:&currentCalculatedMatrix to3x3Matrix:currentModelViewMatrix];
    CATransform3D inverseMatrix = CATransform3DInvert(currentCalculatedMatrix);
    GLfloat inverseModelViewMatrix[9];
    [self convert3DTransform:&inverseMatrix to3x3Matrix:inverseModelViewMatrix];

    GLfloat currentTranslation[3];
    currentTranslation[0] = accumulatedModelTranslation[0];
    currentTranslation[1] = accumulatedModelTranslation[1];
    currentTranslation[2] = accumulatedModelTranslation[2];

    GLfloat currentScaleFactor = currentModelScaleFactor;

    [self precalculateAOLookupTextureForInverseMatrix:inverseModelViewMatrix];
    [self renderDepthTextureForModelViewMatrix:currentModelViewMatrix translation:currentTranslation scale:currentScaleFactor];
    [self renderRaytracedSceneForModelViewMatrix:currentModelViewMatrix inverseMatrix:inverseModelViewMatrix translation:currentTranslation scale:currentScaleFactor];

    const GLenum discards[] = {GL_DEPTH_ATTACHMENT};
    glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards);

    [self presentRenderBuffer];

    dispatch_semaphore_signal(frameRenderingSemaphore);
    });

    这段代码有效,而且我看不到任何其他清理方法。我无法弄清楚这段代码与我的代码有何不同。有所不同的是,看起来与GL上下文相关的所有事情实际上都是在同一GCD调度队列中完成的。但是,当我这样编写代码时,它无法解决任何问题。

    最后的不同是该代码似乎没有使用GLKit。上面的代码(以及我实际上感兴趣的代码)确实使用GLKit。

    在这一点上,我对这个问题有三种理论:
    1.关于块,GCD和OpenGL ES之间的交互,我犯了一个概念性错误。
    2. GLKit的 GLKViewControllerGLKView在对 EAGLContext的调用之间对 drawInRect进行了一些绘制或操作。在处理我的 drawInRect块时,发生了这种情况,使事情变得混乱。
    3.我所依赖的 - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect方法是事实。我认为这种方法是:“嘿,您自动配置了一个 CADisplayLink,每当它想要一个框架时,它都会命中此方法。不管您想要做什么。我的意思是,在这里的普通代码中,您只需发出glDrawArrays命令。这不像是我传回包含要在屏幕上显示的内容的帧缓冲区对象或CGImageRef。我正在发布GL命令。但是,这可能是错误的。也许您只是不能推迟绘图为了测试这一理论,我将所有绘制代码​​移入了一种称为 drawStuff的方法中,然后将 drawRect方法的主体替换为:
    [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(drawStuff) userInfo:nil repeats:NO];

    该应用程序启动,显示视图颜色为 glClear十秒钟,然后正常绘制。因此,该理论看起来也不是太强。

    a similar question posted here有一个答案,该答案被接受并接受:

    调度块中的代码无法正常工作。到执行时,该帧的所有OpenGL状态都将早已被销毁。如果您要在该代码块中调用glGetError(),我相信它会告诉您相同的信息。您需要确保所有绘画代码均在该glkView方法中完成,以使OpenGL状态有效。执行该分派时,实际上是在使该绘图代码的执行脱离该方法的范围。

    我不明白为什么这应该是真的。但:
  • 我只关闭对块中将要超出该块寿命的东西的引用,这些东西就像来自封闭对象范围的Objective-C指针一样。
  • 我可以在调试器中检查它们,它们看起来不错。
  • 我在每个GL操作后都插入了一个getGLError()调用,它从不返回零。
  • 从带有dispatch_sync的块进行绘制即可。
  • 我尝试过思考,在drawInRect方法中的什么地方,我将代码块保存到ivar中,然后设置了一个NSTimer来调用drawStuff。在drawStuff中,我只是调用该块。画得很好。

  • NSTimer情况是异步绘制的,但是它不涉及从另一个线程进行绘制,因为AFAIK NSTimer调用只是在设置线程的运行循环中安排的。因此,它与线程有关。

    有人可以告诉我我在这里想念的东西吗?

    最佳答案

    这是行不通的,因为正如borrrden所说,GLKit在presentRenderbuffer:完成后立即调用- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect

    这在使用计时器的情况下有效,因为在绘制周期开始时在主线程上调用了drawStuff方法。您的- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect有效地什么也不做,只是将其安排在另外10秒内再次在主线程上发生,然后在drawInRect:方法的结尾呈现先前计划的绘图调用。除了将绘图延迟10秒之外,此操作无济于事,一切仍在主线程上进行。

    如果您想采用渲染主线程的方法,则GLKit并不是一个很好的选择。您将需要使用运行循环设置自己的线程,将CADisplayLink挂接到该运行循环,然后从此队列进行渲染。 GLKViewController被配置为为此使用主运行循环,并且将始终在每个帧的末尾呈现渲染缓冲区,这将导致您在其他线程上所做的任何操作都遭受破坏。

    根据您的GL需求,您可能会发现在主线程上执行所有GL事务并在主线程上执行“其他事务”会更简单。

    关于ios - 如何从iOS上的Grand Central Dispatch Queue异步绘制到GLKit的OpenGL ES上下文,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19850809/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com