OpenGL ES 代码在 Android 中工作,但 glAttachShader 不在 iOS 中工作

OpenGL ES code working in Android but not glAttachShader not working on iOS

我有一些在 Android 上运行良好的 OpenGL ES C 代码。代码质量很差(随机错误代码,使用全局变量等),因为我只是想在重构和清理之前起床 运行。但是,它适用于 Android 并正确显示红色三角形。

但相同的代码不适用于 iOS。我在调用 glAttachShader 时得到 GL_INVALID_VALUE (我在下面评论了相关行),即使之前的每个调用都成功了。

代码如下:

#include "gl_render.h"
#include "gl_wrapper.h"
#include <math.h>

GLuint globalProgramObject;
int surfaceWidth;
int surfaceHeight;

GLuint LoadShader ( GLenum type, const char *shaderSrc )
{
   GLuint shader;
   GLint compiled;
   GLenum error;

   shader = glCreateShader ( type );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -50;
   }

   glShaderSource ( shader, 1, &shaderSrc, NULL );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -51;
   }

   glCompileShader ( shader );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -52;
   }

   glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -53;
   }

   if ( !compiled )
   {
      GLint infoLen = 0;

      glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );

      error = glGetError();
      if (error != GL_NO_ERROR) {
         return -54;
      }

      if ( infoLen > 1 )
      {
         char *infoLog = malloc ( sizeof ( char ) * infoLen );

         glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );

         error = glGetError();
         if (error != GL_NO_ERROR) {
            return -55;
         }

         free ( infoLog );
      }

      glDeleteShader ( shader );

      error = glGetError();
      if (error != GL_NO_ERROR) {
         return -56;
      }

      return -2;
   }

   return shader;

}

int on_surface_created(int width, int height) {
   surfaceWidth = width;
   surfaceHeight = height;

   char vShaderStr[] =
      "#version 300 es                          \n"
      "layout(location = 0) in vec4 vPosition;  \n"
      "void main()                              \n"
      "{                                        \n"
      "   gl_Position = vPosition;              \n"
      "}                                        \n";

   char fShaderStr[] =
      "#version 300 es                              \n"
      "precision mediump float;                     \n"
      "out vec4 fragColor;                          \n"
      "void main()                                  \n"
      "{                                            \n"
      "   fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );  \n"
      "}                                            \n";

   GLuint vertexShader;
   GLuint fragmentShader;
   GLuint programObject;
   GLint linked;
   GLenum error;

   vertexShader = LoadShader ( GL_VERTEX_SHADER, vShaderStr );

   if (vertexShader <= 0) {
      return -155;
   }

   fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, fShaderStr );

   if (fragmentShader <= 0) {
      return -156;
   }

   programObject = glCreateProgram ( );

   if (programObject == 0) {
      return -157;
   }

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -57;
   }

   // this is failing with GL_INVALID_VALUE
   glAttachShader ( programObject, vertexShader );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -58;
   }

   glAttachShader ( programObject, fragmentShader );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -59;
   }

   glLinkProgram ( programObject );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -60;
   }

   glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -61;
   }

   if ( !linked )
   {
      GLint infoLen = 0;

      glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );

      error = glGetError();
      if (error != GL_NO_ERROR) {
         return -62;
      }

      if ( infoLen > 1 )
      {
         char *infoLog = malloc ( sizeof ( char ) * infoLen );

         glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );

         error = glGetError();
         if (error != GL_NO_ERROR) {
            return -63;
         }

         free ( infoLog );
      }

      glDeleteProgram ( programObject );

      error = glGetError();
      if (error != GL_NO_ERROR) {
         return -64;
      }

      return -2;
   }

   glClearColor ( 1.0f, 1.0f, 1.0f, 0.0f );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -65;
   }

   globalProgramObject = programObject;

   return 0;
}

int on_draw_frame() {
   GLenum error;

   GLfloat vVertices[] = {  0.0f,  0.5f, 0.0f,
                            -0.5f, -0.5f, 0.0f,
                            0.5f, -0.5f, 0.0f
                         };

   glViewport ( 0, 0, surfaceWidth, surfaceHeight );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -66;
   }

   glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -67;
   }

   glUseProgram ( globalProgramObject );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -68;
   }

   glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -69;
   }

   glEnableVertexAttribArray ( 0 );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -70;
   }

   glDrawArrays ( GL_TRIANGLES, 0, 3 );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -71;
   }

   glDisableVertexAttribArray ( 0 );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -72;
   }

   return 0;
}

对于额外的上下文,这里是 Objective C 我用来托管 OpenGL worker 的代码:

#import "OpenGLRender.h"
#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES2/gl.h>
#import <OpenGLES/ES2/glext.h>

@interface OpenGLRender()
@property (strong, nonatomic) EAGLContext *context;
@property (strong, nonatomic) id<OpenGLRenderWorker> worker;
@property (copy, nonatomic) void(^onNewFrame)(void);

@property (nonatomic) GLuint frameBuffer;
@property (nonatomic) GLuint depthBuffer;
@property (nonatomic) CVPixelBufferRef target;
@property (nonatomic) CVOpenGLESTextureCacheRef textureCache;
@property (nonatomic) CVOpenGLESTextureRef texture;
@property (nonatomic) CGSize renderSize;
@property (nonatomic) BOOL running;
@end

@implementation OpenGLRender

- (instancetype)initWithSize:(CGSize)renderSize
                      worker:(id<OpenGLRenderWorker>)worker
                  onNewFrame:(void(^)(void))onNewFrame {
    self = [super init];
    if (self){
        self.renderSize = renderSize;
        self.running = YES;
        self.onNewFrame = onNewFrame;
        self.worker = worker;

        NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
        thread.name = @"OpenGLRender";
        [thread start];
    }
    return self;
}

- (void)run {
    [self initGL];
    int result = [_worker onCreateWithWidth: self.renderSize.width andHeight: self.renderSize.height];

    if (result != 0) {
        NSLog(@"Failed to call onCreateWithWidth %f AndHeight %f: %d", self.renderSize.width, self.renderSize.height, result);
    }

    while (_running) {
        CFTimeInterval loopStart = CACurrentMediaTime();
        result = [_worker onDraw];

        if (result != 0) {
            NSLog(@"Failed to call onDraw: %d", result);
        } else {
            glFlush();
            dispatch_async(dispatch_get_main_queue(), self.onNewFrame);
        }

        CFTimeInterval waitDelta = 0.016 - (CACurrentMediaTime() - loopStart);
        if (waitDelta > 0) {
            [NSThread sleepForTimeInterval:waitDelta];
        }
    }
    [self deinitGL];
}

#pragma mark - Public

- (void)dispose {
    _running = NO;
}

#pragma mark - FlutterTexture

- (CVPixelBufferRef _Nullable)copyPixelBuffer {
    CVBufferRetain(_target);
    return _target;
}

#pragma mark - Private

- (void)initGL {
    _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];

    [EAGLContext setCurrentContext:_context];
    [self createCVBufferWithSize:_renderSize withRenderTarget:&_target withTextureOut:&_texture];

    glBindTexture(CVOpenGLESTextureGetTarget(_texture), CVOpenGLESTextureGetName(_texture));

    glTexImage2D(GL_TEXTURE_2D,
                 0, GL_RGBA,
                 _renderSize.width, _renderSize.height,
                 0, GL_RGBA,
                 GL_UNSIGNED_BYTE, NULL);

    glGenRenderbuffers(1, &_depthBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, _depthBuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _renderSize.width, _renderSize.height);

    glGenFramebuffers(1, &_frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, CVOpenGLESTextureGetName(_texture), 0);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthBuffer);

    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
    } else {
        NSLog(@"Successfully initialized GL");
    }
}

- (void)createCVBufferWithSize:(CGSize)size
              withRenderTarget:(CVPixelBufferRef *)target
                withTextureOut:(CVOpenGLESTextureRef *)texture {

    CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, _context, NULL, &_textureCache);

    if (err) return;

    CFDictionaryRef empty;
    CFMutableDictionaryRef attrs;
    empty = CFDictionaryCreate(kCFAllocatorDefault,
                               NULL,
                               NULL,
                               0,
                               &kCFTypeDictionaryKeyCallBacks,
                               &kCFTypeDictionaryValueCallBacks);

    attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 1,
                                      &kCFTypeDictionaryKeyCallBacks,
                                      &kCFTypeDictionaryValueCallBacks);

    CFDictionarySetValue(attrs, kCVPixelBufferIOSurfacePropertiesKey, empty);
    CVPixelBufferCreate(kCFAllocatorDefault, size.width, size.height,
                        kCVPixelFormatType_32BGRA, attrs, target);

    CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                 _textureCache,
                                                 *target,
                                                 NULL, // texture attributes
                                                 GL_TEXTURE_2D,
                                                 GL_RGBA, // opengl format
                                                 size.width,
                                                 size.height,
                                                 GL_BGRA, // native iOS format
                                                 GL_UNSIGNED_BYTE,
                                                 0,
                                                 texture);

    CFRelease(empty);
    CFRelease(attrs);
}

- (void)deinitGL {
    glDeleteFramebuffers(1, &_frameBuffer);
    glDeleteFramebuffers(1, &_depthBuffer);
    CFRelease(_target);
    CFRelease(_textureCache);
    CFRelease(_texture);
}

@end

谁能告诉我为什么这在 iOS 上不起作用,尽管它在 Android 上运行良好?

您已经创建了 GLES2 上下文 (kEAGLRenderingAPIOpenGLES2),但您使用的是 GLES3 着色器 (#version 300 es)。

我有点惊讶这个错误没有被 glCompileShader 捕获,但我猜 iOS 驱动程序正在推迟实际编译,直到 glAttachShader 被调用。