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
被调用。
我有一些在 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
被调用。