mirror of
https://github.com/DKJone/DKWechatHelper.git
synced 2025-06-01 00:46:07 +08:00
646 lines
21 KiB
Objective-C
646 lines
21 KiB
Objective-C
// QGHWDMP4OpenGLView.m
|
|
// Tencent is pleased to support the open source community by making vap available.
|
|
//
|
|
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
|
//
|
|
// Licensed under the MIT License (the "License"); you may not use this file except in
|
|
// compliance with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://opensource.org/licenses/MIT
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software distributed under the License is
|
|
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
|
// either express or implied. See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#import "QGHWDMP4OpenGLView.h"
|
|
#import <QuartzCore/QuartzCore.h>
|
|
#import <AVFoundation/AVUtilities.h>
|
|
#import <mach/mach_time.h>
|
|
#import <GLKit/GLKit.h>
|
|
#import "VAPMacros.h"
|
|
|
|
// Uniform index.
|
|
enum {
|
|
HWD_UNIFORM_Y,
|
|
HWD_UNIFORM_UV,
|
|
HWD_UNIFORM_COLOR_CONVERSION_MATRIX,
|
|
HWD_NUM_UNIFORMS
|
|
};
|
|
GLint hwd_uniforms[HWD_NUM_UNIFORMS];
|
|
|
|
// Attribute index.
|
|
enum {
|
|
ATTRIB_VERTEX,
|
|
ATTRIB_TEXCOORD_RGB,
|
|
ATTRIB_TEXCOORD_ALPHA,
|
|
NUM_ATTRIBUTES
|
|
};
|
|
|
|
// BT.709-HDTV.
|
|
static const GLfloat kColorConversion709[] = {
|
|
1.164, 1.164, 1.164,
|
|
0.0, -0.213, 2.112,
|
|
1.793, -0.533, 0.0,
|
|
};
|
|
|
|
// BT.601 full range-http://www.equasys.de/colorconversion.html
|
|
const GLfloat kColorConversion601FullRange[] = {
|
|
1.0, 1.0, 1.0,
|
|
0.0, -0.343, 1.765,
|
|
1.4, -0.711, 0.0,
|
|
};
|
|
|
|
|
|
// texture coords for blend
|
|
|
|
const GLfloat textureCoordLeft[] = { // 左侧
|
|
0.5, 0.0,
|
|
0.0, 0.0,
|
|
0.5, 1.0,
|
|
0.0, 1.0
|
|
};
|
|
|
|
const GLfloat textureCoordRight[] = { // 右侧
|
|
1.0, 0.0,
|
|
0.5, 0.0,
|
|
1.0, 1.0,
|
|
0.5, 1.0
|
|
};
|
|
|
|
const GLfloat textureCoordTop[] = { // 上侧
|
|
1.0, 0.0,
|
|
0.0, 0.0,
|
|
1.0, 0.5,
|
|
0.0, 0.5
|
|
};
|
|
|
|
const GLfloat textureCoordBottom[] = { // 下侧
|
|
1.0, 0.5,
|
|
0.0, 0.5,
|
|
1.0, 1.0,
|
|
0.0, 1.0
|
|
};
|
|
|
|
#undef cos
|
|
#undef sin
|
|
NSString *const kVertexShaderSource = SHADER_STRING
|
|
(
|
|
attribute vec4 position;
|
|
attribute vec2 RGBTexCoord;
|
|
attribute vec2 alphaTexCoord;
|
|
|
|
varying vec2 RGBTexCoordVarying;
|
|
varying vec2 alphaTexCoordVarying;
|
|
|
|
void main()
|
|
{
|
|
float preferredRotation = 3.14;
|
|
mat4 rotationMatrix = mat4(cos(preferredRotation), -sin(preferredRotation), 0.0, 0.0,sin(preferredRotation),cos(preferredRotation), 0.0, 0.0,0.0,0.0,1.0,0.0,0.0,0.0, 0.0,1.0);
|
|
gl_Position = rotationMatrix * position;
|
|
RGBTexCoordVarying = RGBTexCoord;
|
|
alphaTexCoordVarying = alphaTexCoord;
|
|
}
|
|
);
|
|
|
|
NSString *const kFragmentShaderSource = SHADER_STRING
|
|
(
|
|
varying highp vec2 RGBTexCoordVarying;
|
|
varying highp vec2 alphaTexCoordVarying;
|
|
precision mediump float;
|
|
|
|
uniform sampler2D SamplerY;
|
|
uniform sampler2D SamplerUV;
|
|
uniform mat3 colorConversionMatrix;
|
|
|
|
void main()
|
|
{
|
|
mediump vec3 yuv_rgb;
|
|
lowp vec3 rgb_rgb;
|
|
|
|
mediump vec3 yuv_alpha;
|
|
lowp vec3 rgb_alpha;
|
|
|
|
// Subtract constants to map the video range start at 0
|
|
yuv_rgb.x = (texture2D(SamplerY, RGBTexCoordVarying).r);// - (16.0/255.0));
|
|
yuv_rgb.yz = (texture2D(SamplerUV, RGBTexCoordVarying).ra - vec2(0.5, 0.5));
|
|
|
|
rgb_rgb = colorConversionMatrix * yuv_rgb;
|
|
|
|
|
|
yuv_alpha.x = (texture2D(SamplerY, alphaTexCoordVarying).r);// - (16.0/255.0));
|
|
yuv_alpha.yz = (texture2D(SamplerUV, alphaTexCoordVarying).ra - vec2(0.5, 0.5));
|
|
|
|
rgb_alpha = colorConversionMatrix * yuv_alpha;
|
|
|
|
|
|
gl_FragColor = vec4(rgb_rgb,rgb_alpha.r);
|
|
// gl_FragColor = vec4(1, 0, 0, 1);
|
|
}
|
|
);
|
|
|
|
|
|
@interface QGHWDMP4OpenGLView() {
|
|
|
|
GLint _backingWidth;
|
|
GLint _backingHeight;
|
|
CVOpenGLESTextureRef _lumaTexture;
|
|
CVOpenGLESTextureRef _chromaTexture;
|
|
CVOpenGLESTextureCacheRef _textureCache;
|
|
GLuint _frameBufferHandle;
|
|
GLuint _colorBufferHandle;
|
|
const GLfloat *_preferredConversion;
|
|
}
|
|
|
|
@property GLuint program;
|
|
|
|
- (void)setupBuffers;
|
|
- (void)cleanupTextures;
|
|
|
|
- (BOOL)isValidateProgram:(GLuint)prog;
|
|
|
|
- (BOOL)loadShaders;
|
|
- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type URL:(NSURL *)URL;
|
|
- (BOOL)linkProgram:(GLuint)prog;
|
|
|
|
@end
|
|
|
|
@implementation QGHWDMP4OpenGLView
|
|
|
|
+ (Class)layerClass {
|
|
|
|
return [CAEAGLLayer class];
|
|
}
|
|
|
|
- (id)initWithCoder:(NSCoder *)aDecoder {
|
|
if ((self = [super initWithCoder:aDecoder])) {
|
|
if (![self commonInit]) {
|
|
return nil;
|
|
}
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)init {
|
|
|
|
if (self = [super init]) {
|
|
if (![self commonInit]) {
|
|
return nil;
|
|
}
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithFrame:(CGRect)frame {
|
|
|
|
if (self = [super initWithFrame:frame]) {
|
|
if (![self commonInit]) {
|
|
return nil;
|
|
}
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (BOOL)commonInit {
|
|
|
|
self.contentScaleFactor = [[UIScreen mainScreen] scale];
|
|
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
|
|
eaglLayer.opaque = NO;
|
|
eaglLayer.drawableProperties = @{ kEAGLDrawablePropertyRetainedBacking :[NSNumber numberWithBool:NO],
|
|
kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8};
|
|
_glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
|
if (!_glContext || ![EAGLContext setCurrentContext:_glContext] || ![self loadShaders]) {
|
|
return NO;
|
|
}
|
|
_preferredConversion = kColorConversion709;
|
|
return YES;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
|
|
[self cleanupTextures];
|
|
if(_textureCache) {
|
|
CFRelease(_textureCache);
|
|
}
|
|
if ([self.displayDelegate respondsToSelector:@selector(onViewUnavailableStatus)]) {
|
|
[self.displayDelegate onViewUnavailableStatus];
|
|
}
|
|
}
|
|
|
|
# pragma mark - OpenGL setup
|
|
/**
|
|
初始化opengl环境
|
|
*/
|
|
- (void)setupGL {
|
|
|
|
VAP_Info(kQGVAPModuleCommon, @"setupGL");
|
|
[EAGLContext setCurrentContext:_glContext];
|
|
[self setupBuffers];
|
|
[self loadShaders];
|
|
glUseProgram(self.program);
|
|
glUniform1i(hwd_uniforms[HWD_UNIFORM_Y], 0);
|
|
glUniform1i(hwd_uniforms[HWD_UNIFORM_UV], 1);
|
|
glUniformMatrix3fv(hwd_uniforms[HWD_UNIFORM_COLOR_CONVERSION_MATRIX], 1, GL_FALSE, _preferredConversion);
|
|
// Create CVOpenGLESTextureCacheRef for optimal CVPixelBufferRef to GLES texture conversion.
|
|
if (!_textureCache) {
|
|
CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, _glContext, NULL, &_textureCache);
|
|
if (err != noErr) {
|
|
VAP_Event(kQGVAPModuleCommon, @"Error at CVOpenGLESTextureCacheCreate %d", err);
|
|
return;
|
|
}
|
|
}
|
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
|
}
|
|
|
|
#pragma mark - Utilities
|
|
|
|
- (void)setupBuffers {
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
glEnableVertexAttribArray(ATTRIB_VERTEX);
|
|
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
|
|
glEnableVertexAttribArray(ATTRIB_TEXCOORD_RGB);
|
|
glVertexAttribPointer(ATTRIB_TEXCOORD_RGB, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
|
|
glEnableVertexAttribArray(ATTRIB_TEXCOORD_ALPHA);
|
|
glVertexAttribPointer(ATTRIB_TEXCOORD_ALPHA, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
|
|
glGenFramebuffers(1, &_frameBufferHandle);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferHandle);
|
|
glGenRenderbuffers(1, &_colorBufferHandle);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, _colorBufferHandle);
|
|
[_glContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
|
|
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_backingWidth);
|
|
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_backingHeight);
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorBufferHandle);
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
|
VAP_Event(kQGVAPModuleCommon, @"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
|
}
|
|
}
|
|
|
|
- (void)layoutSubviews {
|
|
|
|
[super layoutSubviews];
|
|
[self updateBackingSize];
|
|
}
|
|
|
|
|
|
/**
|
|
根据容器大小更新渲染尺寸
|
|
*/
|
|
- (void)updateBackingSize {
|
|
|
|
if ([EAGLContext currentContext] != _glContext) {
|
|
[EAGLContext setCurrentContext:_glContext];
|
|
}
|
|
[_glContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
|
|
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_backingWidth);
|
|
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_backingHeight);
|
|
}
|
|
|
|
- (void)cleanupTextures {
|
|
|
|
if (_lumaTexture) {
|
|
CFRelease(_lumaTexture);
|
|
_lumaTexture = NULL;
|
|
}
|
|
if (_chromaTexture) {
|
|
CFRelease(_chromaTexture);
|
|
_chromaTexture = NULL;
|
|
}
|
|
CVOpenGLESTextureCacheFlush(_textureCache, 0);
|
|
}
|
|
|
|
#pragma mark - OpenGLES drawing
|
|
|
|
/**
|
|
上屏
|
|
|
|
@param pixelBuffer 硬解出来的samplebuffer数据
|
|
*/
|
|
- (void)displayPixelBuffer:(CVPixelBufferRef)pixelBuffer {
|
|
|
|
if (!self.window && [self.displayDelegate respondsToSelector:@selector(onViewUnavailableStatus)]) {
|
|
[self.displayDelegate onViewUnavailableStatus];
|
|
return ;
|
|
}
|
|
|
|
if ([EAGLContext currentContext] != _glContext) {
|
|
[EAGLContext setCurrentContext:_glContext];
|
|
}
|
|
|
|
CVReturn err;
|
|
if (pixelBuffer != NULL) {
|
|
int frameWidth = (int)CVPixelBufferGetWidth(pixelBuffer);
|
|
int frameHeight = (int)CVPixelBufferGetHeight(pixelBuffer);
|
|
|
|
if (!_textureCache) {
|
|
VAP_Event(kQGVAPModuleCommon, @"No video texture cache");
|
|
return;
|
|
}
|
|
[self cleanupTextures];
|
|
|
|
_preferredConversion = kColorConversion601FullRange;
|
|
|
|
//y
|
|
glActiveTexture(GL_TEXTURE0);
|
|
err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
|
|
_textureCache,
|
|
pixelBuffer,
|
|
NULL,
|
|
GL_TEXTURE_2D,
|
|
GL_LUMINANCE,
|
|
frameWidth,
|
|
frameHeight,
|
|
GL_LUMINANCE,
|
|
GL_UNSIGNED_BYTE,
|
|
0,
|
|
&_lumaTexture);
|
|
if (err) {
|
|
VAP_Event(kQGVAPModuleCommon, @"Error at CVOpenGLESTextureCacheCreateTextureFromImage %d", err);
|
|
}
|
|
|
|
glBindTexture(CVOpenGLESTextureGetTarget(_lumaTexture), CVOpenGLESTextureGetName(_lumaTexture));
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
// uv
|
|
glActiveTexture(GL_TEXTURE1);
|
|
err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
|
|
_textureCache,
|
|
pixelBuffer,
|
|
NULL,
|
|
GL_TEXTURE_2D,
|
|
GL_LUMINANCE_ALPHA,
|
|
frameWidth / 2.0,
|
|
frameHeight / 2.0,
|
|
GL_LUMINANCE_ALPHA,
|
|
GL_UNSIGNED_BYTE,
|
|
1,
|
|
&_chromaTexture);
|
|
if (err) {
|
|
VAP_Error(kQGVAPModuleCommon, @"Error at CVOpenGLESTextureCacheCreateTextureFromImage %d", err);
|
|
}
|
|
|
|
glBindTexture(CVOpenGLESTextureGetTarget(_chromaTexture), CVOpenGLESTextureGetName(_chromaTexture));
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferHandle);
|
|
|
|
// Set the view port to the entire view.
|
|
glViewport(0, 0, _backingWidth, _backingHeight);
|
|
}
|
|
|
|
// glClearColor(0.1f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
glUseProgram(self.program);
|
|
glUniformMatrix3fv(hwd_uniforms[HWD_UNIFORM_COLOR_CONVERSION_MATRIX], 1, GL_FALSE, _preferredConversion);
|
|
|
|
// 根据视频的方向和高宽比设置四个顶点。
|
|
CGRect vertexRect = AVMakeRectWithAspectRatioInsideRect(CGSizeMake(_backingWidth, _backingHeight), self.layer.bounds);
|
|
|
|
// 计算归一化四坐标来绘制坐标系。
|
|
CGSize normalizedSamplingSize = CGSizeMake(0.0, 0.0);
|
|
CGSize cropScaleAmount = CGSizeMake(vertexRect.size.width/self.layer.bounds.size.width, vertexRect.size.height/self.layer.bounds.size.height);
|
|
|
|
if (cropScaleAmount.width > cropScaleAmount.height) {
|
|
normalizedSamplingSize.width = 1.0;
|
|
normalizedSamplingSize.height = cropScaleAmount.height/cropScaleAmount.width;
|
|
} else {
|
|
normalizedSamplingSize.width = 1.0;
|
|
normalizedSamplingSize.height = cropScaleAmount.width/cropScaleAmount.height;
|
|
}
|
|
|
|
GLfloat quadVertexData [] = {
|
|
-1 * normalizedSamplingSize.width, -1 * normalizedSamplingSize.height,
|
|
normalizedSamplingSize.width, -1 * normalizedSamplingSize.height,
|
|
-1 * normalizedSamplingSize.width, normalizedSamplingSize.height,
|
|
normalizedSamplingSize.width, normalizedSamplingSize.height,
|
|
};
|
|
|
|
// 更新顶点数据
|
|
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, quadVertexData);
|
|
glEnableVertexAttribArray(ATTRIB_VERTEX);
|
|
glVertexAttribPointer(ATTRIB_TEXCOORD_RGB, 2, GL_FLOAT, 0, 0, [self quadTextureRGBData]);
|
|
glEnableVertexAttribArray(ATTRIB_TEXCOORD_RGB);
|
|
glVertexAttribPointer(ATTRIB_TEXCOORD_ALPHA, 2, GL_FLOAT, 0, 0, [self quedTextureAlphaData]);
|
|
glEnableVertexAttribArray(ATTRIB_TEXCOORD_ALPHA);
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, _colorBufferHandle);
|
|
if ([EAGLContext currentContext] == _glContext && !self.pause && self.window && [UIApplication sharedApplication].applicationState != UIApplicationStateBackground) {
|
|
[_glContext presentRenderbuffer:GL_RENDERBUFFER];
|
|
}
|
|
}
|
|
|
|
- (const void *)quedTextureAlphaData {
|
|
|
|
switch (self.blendMode) {
|
|
case QGHWDTextureBlendMode_AlphaLeft:
|
|
return textureCoordLeft;
|
|
case QGHWDTextureBlendMode_AlphaRight:
|
|
return textureCoordRight;
|
|
case QGHWDTextureBlendMode_AlphaTop:
|
|
return textureCoordTop;
|
|
case QGHWDTextureBlendMode_AlphaBottom:
|
|
return textureCoordBottom;
|
|
default:
|
|
return textureCoordLeft;
|
|
}
|
|
}
|
|
|
|
- (const void *)quadTextureRGBData {
|
|
|
|
switch (self.blendMode) {
|
|
case QGHWDTextureBlendMode_AlphaLeft:
|
|
return textureCoordRight;
|
|
case QGHWDTextureBlendMode_AlphaRight:
|
|
return textureCoordLeft;
|
|
case QGHWDTextureBlendMode_AlphaTop:
|
|
return textureCoordBottom;
|
|
case QGHWDTextureBlendMode_AlphaBottom:
|
|
return textureCoordTop;
|
|
default:
|
|
return textureCoordRight;
|
|
}
|
|
}
|
|
|
|
#pragma mark - OpenGL ES 2 shader compilation
|
|
|
|
- (BOOL)loadShaders {
|
|
|
|
GLuint vShader, fShader;
|
|
self.program = glCreateProgram();
|
|
// Create and compile the vertex shader.
|
|
if (![self compileShader:&vShader type:GL_VERTEX_SHADER source:kVertexShaderSource]) {
|
|
VAP_Error(kQGVAPModuleCommon, @"Failed to compile vertex shader");
|
|
return NO;
|
|
}
|
|
// Create and compile fragment shader.
|
|
if (![self compileShader:&fShader type:GL_FRAGMENT_SHADER source:kFragmentShaderSource]) {
|
|
VAP_Error(kQGVAPModuleCommon, @"Failed to compile fragment shader");
|
|
return NO;
|
|
}
|
|
// Attach vertex shader to program.
|
|
glAttachShader(self.program, vShader);
|
|
// Attach fragment shader to program.
|
|
glAttachShader(self.program, fShader);
|
|
// Bind attribute locations. This needs to be done prior to linking.
|
|
glBindAttribLocation(self.program, ATTRIB_VERTEX, "position");
|
|
glBindAttribLocation(self.program, ATTRIB_TEXCOORD_RGB, "RGBTexCoord");
|
|
glBindAttribLocation(self.program, ATTRIB_TEXCOORD_ALPHA, "alphaTexCoord");
|
|
// Link the program.
|
|
if (![self linkProgram:self.program]) {
|
|
VAP_Event(kQGVAPModuleCommon, @"Failed to link program: %d", self.program);
|
|
if (vShader) {
|
|
glDeleteShader(vShader);
|
|
vShader = 0;
|
|
}
|
|
if (fShader) {
|
|
glDeleteShader(fShader);
|
|
fShader = 0;
|
|
}
|
|
if (self.program) {
|
|
glDeleteProgram(self.program);
|
|
self.program = 0;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
// Get uniforms' location.
|
|
hwd_uniforms[HWD_UNIFORM_Y] = glGetUniformLocation(self.program, "SamplerY");
|
|
hwd_uniforms[HWD_UNIFORM_UV] = glGetUniformLocation(self.program, "SamplerUV");
|
|
hwd_uniforms[HWD_UNIFORM_COLOR_CONVERSION_MATRIX] = glGetUniformLocation(self.program, "colorConversionMatrix");
|
|
|
|
// Release vertex and fragment shaders.
|
|
if (vShader) {
|
|
glDetachShader(self.program, vShader);
|
|
glDeleteShader(vShader);
|
|
}
|
|
if (fShader) {
|
|
glDetachShader(self.program, fShader);
|
|
glDeleteShader(fShader);
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type source:(const NSString *)sourceString {
|
|
|
|
GLint status;
|
|
const GLchar *source;
|
|
source = (GLchar *)[sourceString UTF8String];
|
|
*shader = glCreateShader(type);
|
|
glShaderSource(*shader, 1, &source, NULL);
|
|
glCompileShader(*shader);
|
|
#if defined(DEBUG)
|
|
GLint lengthOfLog;
|
|
glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &lengthOfLog);
|
|
if (lengthOfLog > 0) {
|
|
GLchar *log = (GLchar *)malloc(lengthOfLog);
|
|
glGetShaderInfoLog(*shader, lengthOfLog, &lengthOfLog, log);
|
|
VAP_Info(kQGVAPModuleCommon, @"MODULE_DECODE Shader compile log:\n%s", log)
|
|
free(log);
|
|
}
|
|
#endif
|
|
glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
|
|
if (status == 0) {
|
|
glDeleteShader(*shader);
|
|
return NO;
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type URL:(NSURL *)URL {
|
|
|
|
VAP_Info(kQGVAPModuleCommon, @"compileShader");
|
|
NSError *error;
|
|
NSString *sourceString = [[NSString alloc] initWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:&error];
|
|
if (sourceString == nil) {
|
|
VAP_Event(kQGVAPModuleCommon, @"Failed to load vertex shader: %@", [error localizedDescription]);
|
|
return NO;
|
|
}
|
|
|
|
const GLchar *source;
|
|
source = (GLchar *)[sourceString UTF8String];
|
|
*shader = glCreateShader(type);
|
|
glShaderSource(*shader, 1, &source, NULL);
|
|
glCompileShader(*shader);
|
|
|
|
#if defined(DEBUG)
|
|
GLint lengthOfLog;
|
|
glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &lengthOfLog);
|
|
if (lengthOfLog > 0) {
|
|
GLchar *log = (GLchar *)malloc(lengthOfLog);
|
|
glGetShaderInfoLog(*shader, lengthOfLog, &lengthOfLog, log);
|
|
VAP_Info(kQGVAPModuleCommon, @"Shader compile log:\n%s", log);
|
|
free(log);
|
|
}
|
|
#endif
|
|
|
|
GLint status;
|
|
glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
|
|
if (status == 0) {
|
|
glDeleteShader(*shader);
|
|
return NO;
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)linkProgram:(GLuint)prog {
|
|
|
|
GLint status;
|
|
glLinkProgram(prog);
|
|
|
|
#if defined(DEBUG)
|
|
GLint lengthOfLog;
|
|
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &lengthOfLog);
|
|
if (lengthOfLog > 0) {
|
|
GLchar *log = (GLchar *)malloc(lengthOfLog);
|
|
glGetProgramInfoLog(prog, lengthOfLog, &lengthOfLog, log);
|
|
VAP_Info(kQGVAPModuleCommon, @"Program link log:\n%s", log);
|
|
free(log);
|
|
}
|
|
#endif
|
|
|
|
glGetProgramiv(prog, GL_LINK_STATUS, &status);
|
|
if (status == 0) {
|
|
return NO;
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)isValidateProgram:(GLuint)prog {
|
|
|
|
GLint logLength, status;
|
|
glValidateProgram(prog);
|
|
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
|
|
if (logLength > 0) {
|
|
GLchar *log = (GLchar *)malloc(logLength);
|
|
glGetProgramInfoLog(prog, logLength, &logLength, log);
|
|
VAP_Info(kQGVAPModuleCommon, @"Program validate log:\n%s", log);
|
|
free(log);
|
|
}
|
|
|
|
glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
|
|
if (status == 0) {
|
|
VAP_Event(kQGVAPModuleCommon, @"program is not valid:%@",@(status));
|
|
return NO;
|
|
}
|
|
VAP_Info(kQGVAPModuleCommon, @"programe is valid");
|
|
return YES;
|
|
}
|
|
|
|
- (void)dispose {
|
|
|
|
glDisableVertexAttribArray(ATTRIB_VERTEX);
|
|
glDisableVertexAttribArray(ATTRIB_TEXCOORD_RGB);
|
|
glDisableVertexAttribArray(ATTRIB_TEXCOORD_ALPHA);
|
|
}
|
|
|
|
@end
|