| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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. |
| */ |
| |
| #include "TextureResize.h" |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <sstream> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <GLES2/gl2ext.h> |
| |
| #include "OpenGLESDispatch/DispatchTables.h" |
| #include "aemu/base/synchronization/Lock.h" |
| #include "host-common/logging.h" |
| #include "host-common/misc.h" |
| #include "host-common/opengl/misc.h" |
| |
| namespace gfxstream { |
| namespace gl { |
| |
| // #define V(...) VERBOSE_PRINT(gles,__VA_ARGS__) |
| #define V(...) |
| #define MAX_FACTOR_POWER 4 |
| |
| static const char kCommonShaderSource[] = |
| "precision mediump float;\n" |
| "varying vec2 vUV00, vUV01;\n" |
| "#if FACTOR > 2\n" |
| "varying vec2 vUV02, vUV03;\n" |
| "#if FACTOR > 4\n" |
| "varying vec2 vUV04, vUV05, vUV06, vUV07;\n" |
| "#if FACTOR > 8\n" |
| "varying vec2 vUV08, vUV09, vUV10, vUV11, vUV12, vUV13, vUV14, vUV15;\n" |
| "#endif\n" |
| "#endif\n" |
| "#endif\n"; |
| |
| static const char kVertexShaderSource[] = |
| "attribute vec2 aPosition;\n" |
| |
| "void main() {\n" |
| " gl_Position = vec4(aPosition, 0, 1);\n" |
| " vec2 uv = ((aPosition + 1.0) / 2.0) + 0.5 / kDimension;\n" |
| " vUV00 = uv;\n" |
| " #ifdef HORIZONTAL\n" |
| " vUV01 = uv + vec2( 1.0 / kDimension.x, 0);\n" |
| " #if FACTOR > 2\n" |
| " vUV02 = uv + vec2( 2.0 / kDimension.x, 0);\n" |
| " vUV03 = uv + vec2( 3.0 / kDimension.x, 0);\n" |
| " #if FACTOR > 4\n" |
| " vUV04 = uv + vec2( 4.0 / kDimension.x, 0);\n" |
| " vUV05 = uv + vec2( 5.0 / kDimension.x, 0);\n" |
| " vUV06 = uv + vec2( 6.0 / kDimension.x, 0);\n" |
| " vUV07 = uv + vec2( 7.0 / kDimension.x, 0);\n" |
| " #if FACTOR > 8\n" |
| " vUV08 = uv + vec2( 8.0 / kDimension.x, 0);\n" |
| " vUV09 = uv + vec2( 9.0 / kDimension.x, 0);\n" |
| " vUV10 = uv + vec2(10.0 / kDimension.x, 0);\n" |
| " vUV11 = uv + vec2(11.0 / kDimension.x, 0);\n" |
| " vUV12 = uv + vec2(12.0 / kDimension.x, 0);\n" |
| " vUV13 = uv + vec2(13.0 / kDimension.x, 0);\n" |
| " vUV14 = uv + vec2(14.0 / kDimension.x, 0);\n" |
| " vUV15 = uv + vec2(15.0 / kDimension.x, 0);\n" |
| " #endif\n" // FACTOR > 8 |
| " #endif\n" // FACTOR > 4 |
| " #endif\n" // FACTOR > 2 |
| |
| " #else\n" |
| " vUV01 = uv + vec2(0, 1.0 / kDimension.y);\n" |
| " #if FACTOR > 2\n" |
| " vUV02 = uv + vec2(0, 2.0 / kDimension.y);\n" |
| " vUV03 = uv + vec2(0, 3.0 / kDimension.y);\n" |
| " #if FACTOR > 4\n" |
| " vUV04 = uv + vec2(0, 4.0 / kDimension.y);\n" |
| " vUV05 = uv + vec2(0, 5.0 / kDimension.y);\n" |
| " vUV06 = uv + vec2(0, 6.0 / kDimension.y);\n" |
| " vUV07 = uv + vec2(0, 7.0 / kDimension.y);\n" |
| " #if FACTOR > 8\n" |
| " vUV08 = uv + vec2(0, 8.0 / kDimension.y);\n" |
| " vUV09 = uv + vec2(0, 9.0 / kDimension.y);\n" |
| " vUV10 = uv + vec2(0, 10.0 / kDimension.y);\n" |
| " vUV11 = uv + vec2(0, 11.0 / kDimension.y);\n" |
| " vUV12 = uv + vec2(0, 12.0 / kDimension.y);\n" |
| " vUV13 = uv + vec2(0, 13.0 / kDimension.y);\n" |
| " vUV14 = uv + vec2(0, 14.0 / kDimension.y);\n" |
| " vUV15 = uv + vec2(0, 15.0 / kDimension.y);\n" |
| " #endif\n" // FACTOR > 8 |
| " #endif\n" // FACTOR > 4 |
| " #endif\n" // FACTOR > 2 |
| " #endif\n" // HORIZONTAL/VERTICAL |
| "}\n"; |
| |
| const char kFragmentShaderSource[] = |
| "uniform sampler2D uTexture;\n" |
| |
| "vec3 read(vec2 uv) {\n" |
| " vec3 r = texture2D(uTexture, uv).rgb;\n" |
| " #ifdef HORIZONTAL\n" |
| " r.rgb = pow(r.rgb, vec3(2.2));\n" |
| " #endif\n" |
| " return r;\n" |
| "}\n" |
| |
| "void main() {\n" |
| " vec3 sum = read(vUV00) + read(vUV01);\n" |
| " #if FACTOR > 2\n" |
| " sum += read(vUV02) + read(vUV03);\n" |
| " #if FACTOR > 4\n" |
| " sum += read(vUV04) + read(vUV05) + read(vUV06) + read(vUV07);\n" |
| " #if FACTOR > 8\n" |
| " sum += read(vUV08) + read(vUV09) + read(vUV10) + read(vUV11) +" |
| " read(vUV12) + read(vUV13) + read(vUV14) + read(vUV15);\n" |
| " #endif\n" |
| " #endif\n" |
| " #endif\n" |
| " sum /= float(FACTOR);\n" |
| " #ifdef VERTICAL\n" |
| " sum.rgb = pow(sum.rgb, vec3(1.0 / 2.2));\n" |
| " #endif\n" |
| " gl_FragColor = vec4(sum.rgb, 1.0);\n" |
| "}\n"; |
| |
| // Vertex shader for anti-aliasing - doesn't do anything special. |
| const char kGenericVertexShaderSource[] = R"( |
| attribute vec2 position; |
| attribute vec2 inCoord; |
| varying vec2 outCoord; |
| void main(void) { |
| gl_Position = vec4(position.x, position.y, 0.0, 1.0); |
| outCoord = inCoord; |
| })"; |
| |
| // Fragment shader |
| const char kGenericFragmentShaderSource[] = R"( |
| precision mediump float; |
| uniform sampler2D texSampler; |
| varying vec2 outCoord; |
| void main(void) { |
| gl_FragColor = texture2D(texSampler, outCoord); |
| } |
| )"; |
| |
| static const float kVertexData[] = {-1, -1, 3, -1, -1, 3}; |
| static android::base::Lock s_postContextResources; |
| static std::vector<GLuint> s_programsToRelease; |
| static std::vector<GLuint> s_framebuffersToRelease; |
| |
| static GLuint createShader(GLenum type, std::initializer_list<const char*> source) { |
| GLint success, infoLength; |
| |
| GLuint shader = s_gles2.glCreateShader(type); |
| if (shader) { |
| s_gles2.glShaderSource(shader, source.size(), source.begin(), nullptr); |
| s_gles2.glCompileShader(shader); |
| s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success); |
| if (success == GL_FALSE) { |
| s_gles2.glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLength); |
| std::string infoLog(infoLength + 1, '\0'); |
| s_gles2.glGetShaderInfoLog(shader, infoLength, nullptr, &infoLog[0]); |
| ERR("%s shader compile failed:\n%s\n", |
| (type == GL_VERTEX_SHADER) ? "Vertex" : "Fragment", |
| infoLog.c_str()); |
| s_gles2.glDeleteShader(shader); |
| shader = 0; |
| } |
| } |
| return shader; |
| } |
| |
| static void attachShaders(TextureResize::Framebuffer* fb, const char* factorDefine, |
| const char* dimensionDefine, GLuint width, GLuint height) { |
| |
| std::ostringstream dimensionConst; |
| dimensionConst << "const vec2 kDimension = vec2(" << width << ", " << height << ");\n"; |
| |
| GLuint vShader = createShader(GL_VERTEX_SHADER, { |
| factorDefine, dimensionDefine, |
| kCommonShaderSource, dimensionConst.str().c_str(), kVertexShaderSource |
| }); |
| GLuint fShader = createShader(GL_FRAGMENT_SHADER, { |
| factorDefine, dimensionDefine, kCommonShaderSource, kFragmentShaderSource |
| }); |
| |
| if (!vShader || !fShader) { |
| return; |
| } |
| if (!fb->program) { |
| fb->program = s_gles2.glCreateProgram(); |
| } |
| s_gles2.glAttachShader(fb->program, vShader); |
| s_gles2.glAttachShader(fb->program, fShader); |
| s_gles2.glLinkProgram(fb->program); |
| s_gles2.glDeleteShader(vShader); |
| s_gles2.glDeleteShader(fShader); |
| |
| fb->aPosition = s_gles2.glGetAttribLocation(fb->program, "aPosition"); |
| fb->uTexture = s_gles2.glGetUniformLocation(fb->program, "uTexture"); |
| } |
| |
| TextureResize::TextureResize(GLuint width, GLuint height) : |
| mWidth(width), |
| mHeight(height), |
| mFactor(1), |
| mFBWidth({0,}), |
| mFBHeight({0,}), |
| // Use unsigned byte as the default since it has the most support |
| // and is the input/output format in the end |
| // (TODO) until HDR is common on both guest and host, and we'll |
| // cross that bridge when we get there. |
| mTextureDataType(GL_UNSIGNED_BYTE) { |
| |
| // Fix color banding by trying to use a texture type with a high precision. |
| const char* exts = (const char*)s_gles2.glGetString(GL_EXTENSIONS); |
| |
| bool hasColorBufferFloat = |
| emugl::getRenderer() == SELECTED_RENDERER_HOST || |
| emugl::hasExtension(exts, "GL_EXT_color_buffer_float"); |
| bool hasColorBufferHalfFloat = |
| emugl::hasExtension(exts, "GL_EXT_color_buffer_half_float"); |
| bool hasTextureFloat = |
| emugl::hasExtension(exts, "GL_OES_texture_float"); |
| bool hasTextureHalfFloat = |
| emugl::hasExtension(exts, "GL_OES_texture_half_float"); |
| bool hasTextureFloatLinear = |
| emugl::hasExtension(exts, "GL_OES_texture_float_linear"); |
| |
| if (hasColorBufferFloat && hasTextureFloat) { |
| mTextureDataType = GL_FLOAT; |
| } else if (hasColorBufferHalfFloat && hasTextureHalfFloat) { |
| mTextureDataType = GL_HALF_FLOAT_OES; |
| } |
| |
| if (hasTextureFloat || hasTextureHalfFloat) { |
| mTextureFilteringMode = |
| hasTextureFloatLinear ? GL_LINEAR : GL_NEAREST; |
| } |
| |
| s_gles2.glGenTextures(1, &mFBWidth.texture); |
| s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| |
| s_gles2.glGenTextures(1, &mFBHeight.texture); |
| s_gles2.glBindTexture(GL_TEXTURE_2D, mFBHeight.texture); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mTextureFilteringMode); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mTextureFilteringMode); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| |
| s_gles2.glGenBuffers(1, &mVertexBuffer); |
| s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); |
| s_gles2.glBufferData(GL_ARRAY_BUFFER, sizeof(kVertexData), kVertexData, GL_STATIC_DRAW); |
| |
| // Clear bindings. |
| s_gles2.glBindTexture(GL_TEXTURE_2D, 0); |
| s_gles2.glBindBuffer(GL_ARRAY_BUFFER, 0); |
| } |
| |
| TextureResize::~TextureResize() { |
| GLuint tex[2] = {mFBWidth.texture, mFBHeight.texture}; |
| s_gles2.glDeleteTextures(2, tex); |
| s_gles2.glDeleteBuffers(1, &mVertexBuffer); |
| // b/242245912 |
| // There seems to be a mesa bug that we have to delete the |
| // program in the post thread. |
| android::base::AutoLock lock(s_postContextResources); |
| s_programsToRelease.push_back(mFBWidth.program); |
| s_programsToRelease.push_back(mFBHeight.program); |
| // b/285421327 |
| // We should create, use and destroy framebuffers in the same context. |
| // Framebuffer ownership is driver-dependent. |
| s_framebuffersToRelease.push_back(mFBWidth.framebuffer); |
| s_framebuffersToRelease.push_back(mFBHeight.framebuffer); |
| } |
| |
| GLuint TextureResize::update(GLuint texture) { |
| // Store the viewport. The viewport is clobbered due to the framebuffers. |
| GLint vport[4] = { 0, }; |
| s_gles2.glGetIntegerv(GL_VIEWPORT, vport); |
| GLint prevFbo = 0; |
| s_gles2.glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &prevFbo); |
| // Correctly deal with rotated screens. |
| GLint tWidth = vport[2], tHeight = vport[3]; |
| if ((mWidth < mHeight) != (tWidth < tHeight)) { |
| std::swap(tWidth, tHeight); |
| } |
| |
| // Compute the scaling factor needed to get an image just larger than the target viewport. |
| unsigned int factor = 1; |
| for (int i = 0, w = mWidth / 2, h = mHeight / 2; |
| i < MAX_FACTOR_POWER && w >= tWidth && h >= tHeight; |
| i++, w /= 2, h /= 2, factor *= 2) { |
| } |
| |
| // No resizing needed if factor == 1 |
| if (factor == 1) { |
| return texture; |
| } |
| |
| s_gles2.glGetError(); // Clear any GL errors. |
| setupFramebuffers(factor); |
| resize(texture); |
| s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); |
| s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]); // Restore the viewport. |
| // If there was an error while resizing, just use the unscaled texture. |
| GLenum error = s_gles2.glGetError(); |
| if (error != GL_NO_ERROR) { |
| V("GL error while resizing: 0x%x (ignored)\n", error); |
| return texture; |
| } |
| |
| return mFBHeight.texture; |
| } |
| |
| GLuint TextureResize::update(GLuint texture, int width, int height, int rotation) { |
| if (mGenericResizer.get() == nullptr) { |
| mGenericResizer.reset(new TextureResize::GenericResizer()); |
| } |
| return mGenericResizer->draw(texture, width, height, rotation); |
| } |
| |
| void TextureResize::setupFramebuffers(unsigned int factor) { |
| if (factor == mFactor) { |
| // The factor hasn't changed, no need to update the framebuffers. |
| return; |
| } |
| |
| // Update the framebuffer sizes to match the new factor. |
| s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture); |
| s_gles2.glTexImage2D( |
| GL_TEXTURE_2D, 0, GL_RGB, mWidth / factor, mHeight, 0, GL_RGB, |
| mTextureDataType, nullptr); |
| s_gles2.glBindTexture(GL_TEXTURE_2D, 0); |
| |
| s_gles2.glBindTexture(GL_TEXTURE_2D, mFBHeight.texture); |
| s_gles2.glTexImage2D( |
| GL_TEXTURE_2D, 0, GL_RGB, mWidth / factor, mHeight / factor, 0, GL_RGB, |
| mTextureDataType, nullptr); |
| s_gles2.glBindTexture(GL_TEXTURE_2D, 0); |
| |
| // Update the shaders to the new factor. |
| std::ostringstream factorDefine; |
| factorDefine << "#define FACTOR " << factor << '\n'; |
| const std::string factorDefineStr = factorDefine.str(); |
| attachShaders(&mFBWidth, factorDefineStr.c_str(), "#define HORIZONTAL\n", mWidth, mHeight); |
| attachShaders(&mFBHeight, factorDefineStr.c_str(), "#define VERTICAL\n", mWidth, mHeight); |
| |
| mFactor = factor; |
| |
| s_gles2.glBindTexture(GL_TEXTURE_2D, 0); |
| } |
| |
| void TextureResize::resize(GLuint texture) { |
| s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); |
| s_gles2.glActiveTexture(GL_TEXTURE0); |
| |
| if (!mFBWidth.framebuffer) { |
| s_gles2.glGenFramebuffers(1, &mFBWidth.framebuffer); |
| } |
| if (!mFBHeight.framebuffer) { |
| s_gles2.glGenFramebuffers(1, &mFBHeight.framebuffer); |
| } |
| |
| // First scale the horizontal dimension by rendering the input texture to a scaled framebuffer. |
| s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer); |
| s_gles2.glFramebufferTexture2D( |
| GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFBWidth.texture, 0); |
| s_gles2.glClear(GL_COLOR_BUFFER_BIT); |
| s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight); |
| s_gles2.glUseProgram(mFBWidth.program); |
| s_gles2.glEnableVertexAttribArray(mFBWidth.aPosition); |
| s_gles2.glVertexAttribPointer(mFBWidth.aPosition, 2, GL_FLOAT, GL_FALSE, 0, 0); |
| s_gles2.glBindTexture(GL_TEXTURE_2D, texture); |
| |
| // Store the current texture filters and set to nearest for scaling. |
| GLint mag_filter, min_filter; |
| s_gles2.glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &mag_filter); |
| s_gles2.glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &min_filter); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| s_gles2.glUniform1i(mFBWidth.uTexture, 0); |
| s_gles2.glDrawArrays(GL_TRIANGLES, 0, sizeof(kVertexData) / (2 * sizeof(float))); |
| |
| // Restore the previous texture filters. |
| s_gles2.glDisableVertexAttribArray(mFBWidth.aPosition); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter); |
| s_gles2.glFramebufferTexture2D( |
| GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); |
| // Secondly, scale the vertical dimension using the second framebuffer. |
| s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer); |
| s_gles2.glFramebufferTexture2D( |
| GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFBHeight.texture, 0); |
| s_gles2.glClear(GL_COLOR_BUFFER_BIT); |
| s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight / mFactor); |
| s_gles2.glUseProgram(mFBHeight.program); |
| s_gles2.glEnableVertexAttribArray(mFBHeight.aPosition); |
| s_gles2.glVertexAttribPointer(mFBHeight.aPosition, 2, GL_FLOAT, GL_FALSE, 0, 0); |
| s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture); |
| s_gles2.glUniform1i(mFBHeight.uTexture, 0); |
| s_gles2.glDrawArrays(GL_TRIANGLES, 0, sizeof(kVertexData) / (2 * sizeof(float))); |
| s_gles2.glFramebufferTexture2D( |
| GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); |
| // Clear the bindings. (Viewport restored outside) |
| s_gles2.glBindBuffer(GL_ARRAY_BUFFER, 0); |
| s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| s_gles2.glBindTexture(GL_TEXTURE_2D, 0); |
| s_gles2.glDisableVertexAttribArray(mFBHeight.aPosition); |
| s_gles2.glUseProgram(0); |
| android::base::AutoLock lock(s_postContextResources); |
| while (s_programsToRelease.size()) { |
| s_gles2.glDeleteProgram(s_programsToRelease.back()); |
| s_programsToRelease.pop_back(); |
| } |
| if (s_framebuffersToRelease.size()) { |
| s_gles2.glDeleteFramebuffers(s_framebuffersToRelease.size(), |
| s_framebuffersToRelease.data()); |
| s_framebuffersToRelease.clear(); |
| } |
| } |
| |
| struct Vertex { |
| float pos[2]; |
| float coord[2]; |
| }; |
| |
| TextureResize::GenericResizer::GenericResizer() : |
| mProgram(0), |
| mVertexBuffer(0), |
| mIndexBuffer(0), |
| mWidth(0), |
| mHeight(0) { |
| GLuint vertex_shader = |
| createShader(GL_VERTEX_SHADER, {kGenericVertexShaderSource}); |
| GLuint fragment_shader = |
| createShader(GL_FRAGMENT_SHADER, {kGenericFragmentShaderSource}); |
| |
| mProgram = s_gles2.glCreateProgram(); |
| s_gles2.glAttachShader(mProgram, vertex_shader); |
| s_gles2.glAttachShader(mProgram, fragment_shader); |
| s_gles2.glLinkProgram(mProgram); |
| |
| // Shader objects no longer needed. |
| s_gles2.glDeleteShader(vertex_shader); |
| s_gles2.glDeleteShader(fragment_shader); |
| |
| // Check for errors. |
| GLint success; |
| s_gles2.glGetProgramiv(mProgram, GL_LINK_STATUS, &success); |
| if (success == GL_FALSE) { |
| GLchar infolog[256]; |
| s_gles2.glGetProgramInfoLog(mProgram, sizeof(infolog), 0, infolog); |
| fprintf(stderr, "Could not create/link program: %s\n", infolog); |
| return; |
| } |
| |
| // Get all the attributes and uniforms. |
| mPositionAttribLocation = |
| s_gles2.glGetAttribLocation(mProgram, "position"); |
| mInCoordAttribLocation = |
| s_gles2.glGetAttribLocation(mProgram, "inCoord"); |
| mInputUniformLocation = |
| s_gles2.glGetUniformLocation(mProgram, "texSampler"); |
| |
| // Create vertex buffers. |
| static const Vertex kVertices[] = { |
| // 0 degree |
| {{ +1, -1 }, { +1, +0 }}, |
| {{ +1, +1 }, { +1, +1 }}, |
| {{ -1, +1 }, { +0, +1 }}, |
| {{ -1, -1 }, { +0, +0 }}, |
| // 90 degree clock-wise |
| {{ +1, -1 }, { +0, +0 }}, |
| {{ +1, +1 }, { +1, +0 }}, |
| {{ -1, +1 }, { +1, +1 }}, |
| {{ -1, -1 }, { +0, +1 }}, |
| // 180 degree clock-wise |
| {{ +1, -1 }, { +0, +1 }}, |
| {{ +1, +1 }, { +0, +0 }}, |
| {{ -1, +1 }, { +1, +0 }}, |
| {{ -1, -1 }, { +1, +1 }}, |
| // 270 degree clock-wise |
| {{ +1, -1 }, { +1, +1 }}, |
| {{ +1, +1 }, { +0, +1 }}, |
| {{ -1, +1 }, { +0, +0 }}, |
| {{ -1, -1 }, { +1, +0 }}, |
| }; |
| s_gles2.glGenBuffers(1, &mVertexBuffer); |
| s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); |
| s_gles2.glBufferData(GL_ARRAY_BUFFER, |
| sizeof(kVertices), |
| kVertices, |
| GL_STATIC_DRAW); |
| |
| // indices for predefined rotation angles. |
| static const GLubyte kIndices[] = { |
| 0, 1, 2, 2, 3, 0, // 0 |
| 4, 5, 6, 6, 7, 4, // 90 |
| 8, 9, 10, 10, 11, 8, // 180 |
| 12, 13, 14, 14, 15, 12, // 270 |
| }; |
| s_gles2.glGenBuffers(1, &mIndexBuffer); |
| s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer); |
| s_gles2.glBufferData(GL_ELEMENT_ARRAY_BUFFER, |
| sizeof(kIndices), |
| kIndices, |
| GL_STATIC_DRAW); |
| |
| s_gles2.glGenTextures(1, &mFrameBuffer.texture); |
| s_gles2.glBindTexture(GL_TEXTURE_2D, mFrameBuffer.texture); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| |
| s_gles2.glGenFramebuffers(1, &mFrameBuffer.framebuffer); |
| |
| // Clear bindings. |
| s_gles2.glBindTexture(GL_TEXTURE_2D, 0); |
| s_gles2.glBindBuffer(GL_ARRAY_BUFFER, 0); |
| s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| } |
| |
| GLuint TextureResize::GenericResizer::draw(GLuint texture, int width, int height, |
| int rotation) { |
| if (mWidth != width || mHeight != height) { |
| // update the framebuffer to match the new resolution |
| mWidth = width; |
| mHeight = height; |
| s_gles2.glBindTexture(GL_TEXTURE_2D, mFrameBuffer.texture); |
| s_gles2.glTexImage2D( |
| GL_TEXTURE_2D, 0, GL_RGB, mWidth, mHeight, 0, GL_RGB, |
| GL_UNSIGNED_BYTE, nullptr); |
| s_gles2.glBindTexture(GL_TEXTURE_2D, 0); |
| |
| } |
| |
| // Store the viewport. |
| GLint vport[4] = { 0, }; |
| s_gles2.glGetIntegerv(GL_VIEWPORT, vport); |
| |
| s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFrameBuffer.framebuffer); |
| s_gles2.glFramebufferTexture2D( |
| GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFrameBuffer.texture, 0); |
| s_gles2.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| s_gles2.glViewport(0, 0, mWidth, mHeight); |
| s_gles2.glUseProgram(mProgram); |
| s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); |
| s_gles2.glEnableVertexAttribArray(mPositionAttribLocation); |
| s_gles2.glVertexAttribPointer(mPositionAttribLocation, |
| 2, // components per attrib |
| GL_FLOAT, |
| GL_FALSE, |
| sizeof(Vertex), // stride |
| 0); // offset |
| s_gles2.glEnableVertexAttribArray(mInCoordAttribLocation); |
| s_gles2.glVertexAttribPointer(mInCoordAttribLocation, |
| 2, |
| GL_FLOAT, |
| GL_FALSE, |
| sizeof(Vertex), |
| reinterpret_cast<GLvoid*>(sizeof(float) * 2)); |
| s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer); |
| s_gles2.glActiveTexture(GL_TEXTURE0); |
| s_gles2.glBindTexture(GL_TEXTURE_2D, texture); |
| s_gles2.glUniform1i(mInputUniformLocation, 0); |
| intptr_t indexShift; |
| switch(rotation) { |
| case SKIN_ROTATION_0: |
| indexShift = 0; |
| break; |
| case SKIN_ROTATION_90: |
| indexShift = 6; |
| break; |
| case SKIN_ROTATION_180: |
| indexShift = 12; |
| break; |
| case SKIN_ROTATION_270: |
| indexShift = 18; |
| break; |
| default: |
| indexShift = 0; |
| } |
| s_gles2.glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (const GLvoid*)indexShift); |
| |
| // Clear the bindings. |
| s_gles2.glBindBuffer(GL_ARRAY_BUFFER, 0); |
| s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| s_gles2.glBindTexture(GL_TEXTURE_2D, 0); |
| s_gles2.glDisableVertexAttribArray(mPositionAttribLocation); |
| s_gles2.glDisableVertexAttribArray(mInCoordAttribLocation); |
| |
| // Restore the viewport. |
| s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]); |
| |
| return mFrameBuffer.texture; |
| } |
| |
| TextureResize::GenericResizer::~GenericResizer() { |
| s_gles2.glDeleteFramebuffers(1, &mFrameBuffer.framebuffer); |
| s_gles2.glDeleteTextures(1, &mFrameBuffer.texture); |
| s_gles2.glUseProgram(0); |
| s_gles2.glDeleteProgram(mProgram); |
| s_gles2.glBindBuffer(GL_ARRAY_BUFFER, 0); |
| s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| s_gles2.glDeleteBuffers(1, &mVertexBuffer); |
| s_gles2.glDeleteBuffers(1, &mIndexBuffer); |
| } |
| |
| } // namespace gl |
| } // namespace gfxstream |