| // 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 "TextureDraw.h" |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <algorithm> |
| #include <string> |
| |
| #include "OpenGLESDispatch/DispatchTables.h" |
| #include "host-common/crash_reporter.h" |
| #include "host-common/logging.h" |
| |
| #ifndef NDEBUG |
| #define DEBUG_TEXTURE_DRAW |
| #endif |
| |
| namespace gfxstream { |
| namespace gl { |
| namespace { |
| |
| // Helper function to create a new shader. |
| // |shaderType| is the shader type (e.g. GL_VERTEX_SHADER). |
| // |shaderText| is a 0-terminated C string for the shader source to use. |
| // On success, return the handle of the new compiled shader, or 0 on failure. |
| GLuint createShader(GLint shaderType, const char* shaderText) { |
| // Create new shader handle and attach source. |
| GLuint shader = s_gles2.glCreateShader(shaderType); |
| if (!shader) { |
| return 0; |
| } |
| const GLchar* text = static_cast<const GLchar*>(shaderText); |
| const GLint textLen = ::strlen(shaderText); |
| s_gles2.glShaderSource(shader, 1, &text, &textLen); |
| |
| // Compiler the shader. |
| GLint success; |
| s_gles2.glCompileShader(shader); |
| s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success); |
| if (success == GL_FALSE) { |
| GLint infoLogLength; |
| s_gles2.glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); |
| std::string infoLog(infoLogLength + 1, '\0'); |
| fprintf(stderr, "%s: TextureDraw shader compile failed.\n", __func__); |
| s_gles2.glGetShaderInfoLog(shader, infoLogLength, 0, &infoLog[0]); |
| fprintf(stderr, "%s: Info log:\n%s\n", __func__, |
| infoLog.c_str()); |
| fprintf(stderr, "%s: Source:\n%s\n", __func__, |
| shaderText); |
| s_gles2.glDeleteShader(shader); |
| |
| // No point in continuing as it's going to be a black screen. |
| // Send a crash report. |
| // emugl::emugl_crash_reporter( |
| // "FATAL: Could not compile shader for guest framebuffer blit. " |
| // "There may be an issue with the GPU drivers on your machine. " |
| // "Try using software rendering; launch the emulator " |
| // "from the command line with -gpu swiftshader_indirect. "); |
| } |
| |
| return shader; |
| } |
| |
| // No scaling / projection since we want to fill the whole viewport with |
| // the texture, hence a trivial vertex shader that only supports translation. |
| // Note: we used to have a proper free-angle rotation support in this shader, |
| // but looks like SwiftShader doesn't support either complicated calculations |
| // for gl_Position/varyings or just doesn't like trigonometric functions in |
| // shader; anyway the new code has hardcoded texture coordinate mapping for |
| // different rotation angles and works in both native OpenGL and SwiftShader. |
| const char kVertexShaderSource[] = |
| "attribute vec4 position;\n" |
| "attribute vec2 inCoord;\n" |
| "varying vec2 outCoord;\n" |
| "uniform vec2 translation;\n" |
| "uniform vec2 scale;\n" |
| "uniform vec2 coordTranslation;\n" |
| "uniform vec2 coordScale;\n" |
| |
| "void main(void) {\n" |
| " gl_Position.xy = position.xy * scale.xy - translation.xy;\n" |
| " gl_Position.zw = position.zw;\n" |
| " outCoord = inCoord * coordScale + coordTranslation;\n" |
| "}\n"; |
| |
| // Similarly, just interpolate texture coordinates. |
| const char kFragmentShaderSource[] = |
| "#define kComposeModeDevice 2\n" |
| "precision mediump float;\n" |
| "varying lowp vec2 outCoord;\n" |
| "uniform sampler2D tex;\n" |
| "uniform float alpha;\n" |
| "uniform int composeMode;\n" |
| "uniform vec4 color ;\n" |
| |
| "void main(void) {\n" |
| " if (composeMode == kComposeModeDevice) {\n" |
| " gl_FragColor = alpha * texture2D(tex, outCoord);\n" |
| " } else {\n" |
| " gl_FragColor = alpha * color;\n" |
| " }\n" |
| "}\n"; |
| |
| // Hard-coded arrays of vertex information. |
| struct Vertex { |
| float pos[3]; |
| float coord[2]; |
| }; |
| |
| const Vertex kVertices[] = { |
| // 0 degree |
| {{ +1, -1, +0 }, { +1, +0 }}, |
| {{ +1, +1, +0 }, { +1, +1 }}, |
| {{ -1, +1, +0 }, { +0, +1 }}, |
| {{ -1, -1, +0 }, { +0, +0 }}, |
| // 90 degree clock-wise |
| {{ +1, -1, +0 }, { +1, +1 }}, |
| {{ +1, +1, +0 }, { +0, +1 }}, |
| {{ -1, +1, +0 }, { +0, +0 }}, |
| {{ -1, -1, +0 }, { +1, +0 }}, |
| // 180 degree clock-wise |
| {{ +1, -1, +0 }, { +0, +1 }}, |
| {{ +1, +1, +0 }, { +0, +0 }}, |
| {{ -1, +1, +0 }, { +1, +0 }}, |
| {{ -1, -1, +0 }, { +1, +1 }}, |
| // 270 degree clock-wise |
| {{ +1, -1, +0 }, { +0, +0 }}, |
| {{ +1, +1, +0 }, { +1, +0 }}, |
| {{ -1, +1, +0 }, { +1, +1 }}, |
| {{ -1, -1, +0 }, { +0, +1 }}, |
| // flip horizontally |
| {{ +1, -1, +0 }, { +0, +0 }}, |
| {{ +1, +1, +0 }, { +0, +1 }}, |
| {{ -1, +1, +0 }, { +1, +1 }}, |
| {{ -1, -1, +0 }, { +1, +0 }}, |
| // flip vertically |
| {{ +1, -1, +0 }, { +1, +1 }}, |
| {{ +1, +1, +0 }, { +1, +0 }}, |
| {{ -1, +1, +0 }, { +0, +0 }}, |
| {{ -1, -1, +0 }, { +0, +1 }}, |
| // flip source image horizontally, the rotate 90 degrees clock-wise |
| {{ +1, -1, +0 }, { +0, +1 }}, |
| {{ +1, +1, +0 }, { +1, +1 }}, |
| {{ -1, +1, +0 }, { +1, +0 }}, |
| {{ -1, -1, +0 }, { +0, +0 }}, |
| // flip source image vertically, the rotate 90 degrees clock-wise |
| {{ +1, -1, +0 }, { +1, +0 }}, |
| {{ +1, +1, +0 }, { +0, +0 }}, |
| {{ -1, +1, +0 }, { +0, +1 }}, |
| {{ -1, -1, +0 }, { +1, +1 }}, |
| }; |
| |
| // Vertex indices for predefined rotation angles. |
| 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 |
| 16, 17, 18 ,18, 19, 16, // flip h |
| 20, 21, 22, 22, 23, 20, // flip v |
| 24, 25, 26, 26, 27, 24, // flip h, 90 |
| 28, 29, 30, 30, 31, 28 // flip v, 90 |
| }; |
| |
| const GLint kIndicesPerDraw = 6; |
| |
| } // namespace |
| |
| TextureDraw::TextureDraw() |
| : mVertexShader(0), |
| mFragmentShader(0), |
| mProgram(0), |
| mCoordTranslation(-1), |
| mCoordScale(-1), |
| mPositionSlot(-1), |
| mInCoordSlot(-1), |
| mScaleSlot(-1), |
| mTextureSlot(-1), |
| mTranslationSlot(-1), |
| mMaskTexture(0), |
| mMaskTextureWidth(0), |
| mMaskTextureHeight(0), |
| mHaveNewMask(false), |
| mMaskIsValid(false), |
| mShouldReallocateTexture(true) { |
| // Create shaders and program. |
| mVertexShader = createShader(GL_VERTEX_SHADER, kVertexShaderSource); |
| mFragmentShader = createShader(GL_FRAGMENT_SHADER, kFragmentShaderSource); |
| |
| mProgram = s_gles2.glCreateProgram(); |
| s_gles2.glAttachShader(mProgram, mVertexShader); |
| s_gles2.glAttachShader(mProgram, mFragmentShader); |
| |
| GLint success; |
| s_gles2.glLinkProgram(mProgram); |
| s_gles2.glGetProgramiv(mProgram, GL_LINK_STATUS, &success); |
| if (success == GL_FALSE) { |
| GLchar messages[256]; |
| s_gles2.glGetProgramInfoLog( |
| mProgram, sizeof(messages), 0, &messages[0]); |
| ERR("%s: Could not create/link program: %s\n", __FUNCTION__, messages); |
| s_gles2.glDeleteProgram(mProgram); |
| mProgram = 0; |
| return; |
| } |
| |
| s_gles2.glUseProgram(mProgram); |
| |
| // Retrieve attribute/uniform locations. |
| mPositionSlot = s_gles2.glGetAttribLocation(mProgram, "position"); |
| s_gles2.glEnableVertexAttribArray(mPositionSlot); |
| |
| mInCoordSlot = s_gles2.glGetAttribLocation(mProgram, "inCoord"); |
| s_gles2.glEnableVertexAttribArray(mInCoordSlot); |
| |
| mAlpha = s_gles2.glGetUniformLocation(mProgram, "alpha"); |
| mComposeMode = s_gles2.glGetUniformLocation(mProgram, "composeMode"); |
| mColor = s_gles2.glGetUniformLocation(mProgram, "color"); |
| mCoordTranslation = s_gles2.glGetUniformLocation(mProgram, "coordTranslation"); |
| mCoordScale = s_gles2.glGetUniformLocation(mProgram, "coordScale"); |
| mScaleSlot = s_gles2.glGetUniformLocation(mProgram, "scale"); |
| mTranslationSlot = s_gles2.glGetUniformLocation(mProgram, "translation"); |
| mTextureSlot = s_gles2.glGetUniformLocation(mProgram, "tex"); |
| |
| // set default uniform values |
| s_gles2.glUniform1f(mAlpha, 1.0); |
| s_gles2.glUniform1i(mComposeMode, 2); |
| s_gles2.glUniform2f(mTranslationSlot, 0.0, 0.0); |
| s_gles2.glUniform2f(mScaleSlot, 1.0, 1.0); |
| s_gles2.glUniform2f(mCoordTranslation, 0.0, 0.0); |
| s_gles2.glUniform2f(mCoordScale, 1.0, 1.0); |
| |
| #if 0 |
| printf("SLOTS position=%d inCoord=%d texture=%d translation=%d\n", |
| mPositionSlot, mInCoordSlot, mTextureSlot, mTranslationSlot); |
| #endif |
| |
| // Create vertex and index buffers. |
| s_gles2.glGenBuffers(1, &mVertexBuffer); |
| s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); |
| s_gles2.glBufferData( |
| GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, GL_STATIC_DRAW); |
| |
| 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); |
| |
| // Reset state. |
| s_gles2.glUseProgram(0); |
| s_gles2.glDisableVertexAttribArray(mPositionSlot); |
| s_gles2.glDisableVertexAttribArray(mInCoordSlot); |
| s_gles2.glBindBuffer(GL_ARRAY_BUFFER, 0); |
| s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| |
| // Create a texture handle for use with an overlay mask |
| s_gles2.glGenTextures(1, &mMaskTexture); |
| } |
| |
| bool TextureDraw::drawImpl(GLuint texture, float rotation, |
| float dx, float dy, bool wantOverlay) { |
| if (!mProgram) { |
| ERR("%s: no program\n", __FUNCTION__); |
| return false; |
| } |
| |
| // TODO(digit): Save previous program state. |
| |
| s_gles2.glUseProgram(mProgram); |
| |
| s_gles2.glEnable(GL_BLEND); |
| s_gles2.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
| #ifdef DEBUG_TEXTURE_DRAW |
| GLenum err = s_gles2.glGetError(); |
| if (err != GL_NO_ERROR) { |
| ERR("%s: Could not use program error=0x%x\n", |
| __FUNCTION__, err); |
| } |
| #endif |
| |
| // Setup the |position| attribute values. |
| s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); |
| |
| #ifdef DEBUG_TEXTURE_DRAW |
| err = s_gles2.glGetError(); |
| if (err != GL_NO_ERROR) { |
| ERR("%s: Could not bind GL_ARRAY_BUFFER error=0x%x\n", |
| __FUNCTION__, err); |
| } |
| #endif |
| |
| s_gles2.glEnableVertexAttribArray(mPositionSlot); |
| s_gles2.glVertexAttribPointer(mPositionSlot, |
| 3, |
| GL_FLOAT, |
| GL_FALSE, |
| sizeof(Vertex), |
| 0); |
| |
| #ifdef DEBUG_TEXTURE_DRAW |
| err = s_gles2.glGetError(); |
| if (err != GL_NO_ERROR) { |
| ERR("%s: Could glVertexAttribPointer with mPositionSlot error=0x%x\n", |
| __FUNCTION__, err); |
| } |
| #endif |
| |
| // Setup the |inCoord| attribute values. |
| s_gles2.glEnableVertexAttribArray(mInCoordSlot); |
| s_gles2.glVertexAttribPointer(mInCoordSlot, |
| 2, |
| GL_FLOAT, |
| GL_FALSE, |
| sizeof(Vertex), |
| reinterpret_cast<GLvoid*>( |
| static_cast<uintptr_t>( |
| sizeof(float) * 3))); |
| |
| // setup the |texture| uniform value. |
| s_gles2.glActiveTexture(GL_TEXTURE0); |
| s_gles2.glBindTexture(GL_TEXTURE_2D, texture); |
| s_gles2.glUniform1i(mTextureSlot, 0); |
| |
| // setup the |translation| uniform value. |
| s_gles2.glUniform2f(mTranslationSlot, dx, dy); |
| |
| #ifdef DEBUG_TEXTURE_DRAW |
| // Validate program, just to be sure. |
| s_gles2.glValidateProgram(mProgram); |
| GLint validState = 0; |
| s_gles2.glGetProgramiv(mProgram, GL_VALIDATE_STATUS, &validState); |
| if (validState == GL_FALSE) { |
| GLchar messages[256] = {}; |
| s_gles2.glGetProgramInfoLog( |
| mProgram, sizeof(messages), 0, &messages[0]); |
| ERR("%s: Could not run program: '%s'\n", __FUNCTION__, messages); |
| return false; |
| } |
| #endif |
| |
| // Do the rendering. |
| s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer); |
| #ifdef DEBUG_TEXTURE_DRAW |
| err = s_gles2.glGetError(); |
| if (err != GL_NO_ERROR) { |
| ERR("%s: Could not glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) error=0x%x\n", |
| __FUNCTION__, err); |
| } |
| #endif |
| |
| // We may only get 0, 90, 180, 270 in |rotation| so far. |
| const int intRotation = ((int)rotation)/90; |
| assert(intRotation >= 0 && intRotation <= 3); |
| intptr_t indexShift = 0; |
| switch (intRotation) { |
| case 0: |
| indexShift = 5 * kIndicesPerDraw; |
| break; |
| case 1: |
| indexShift = 7 * kIndicesPerDraw; |
| break; |
| case 2: |
| indexShift = 4 * kIndicesPerDraw; |
| break; |
| case 3: |
| indexShift = 6 * kIndicesPerDraw; |
| break; |
| } |
| s_gles2.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| s_gles2.glClear(GL_COLOR_BUFFER_BIT); |
| s_gles2.glDrawElements(GL_TRIANGLES, kIndicesPerDraw, GL_UNSIGNED_BYTE, |
| (const GLvoid*)indexShift); |
| |
| bool shouldDrawMask = false; |
| GLfloat scale[2]; |
| s_gles2.glGetUniformfv(mProgram, mScaleSlot, scale); |
| GLfloat overlayScale[2]; |
| { |
| android::base::AutoLock lock(mMaskLock); |
| if (wantOverlay && mHaveNewMask) { |
| // Create a texture from the mask image and make it |
| // available to be blended |
| GLint prevUnpackAlignment; |
| s_gles2.glGetIntegerv(GL_UNPACK_ALIGNMENT, &prevUnpackAlignment); |
| s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, 1); |
| |
| s_gles2.glBindTexture(GL_TEXTURE_2D, mMaskTexture); |
| |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| |
| if (mShouldReallocateTexture) { |
| mMaskTextureWidth = mMaskWidth; |
| mMaskTextureHeight = mMaskHeight; |
| // mMaskPixels is actually not used here, we only use |
| // glTexImage2D here to resize the texture |
| s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, |
| mMaskTextureWidth, mMaskTextureHeight, 0, |
| GL_RGBA, GL_UNSIGNED_BYTE, |
| mMaskPixels.data()); |
| mShouldReallocateTexture = false; |
| } |
| |
| // Put the new texture in the center. |
| s_gles2.glTexSubImage2D( |
| GL_TEXTURE_2D, 0, (mMaskTextureWidth - mMaskWidth) / 2, |
| (mMaskTextureHeight - mMaskHeight) / 2, mMaskWidth, |
| mMaskHeight, GL_RGBA, GL_UNSIGNED_BYTE, mMaskPixels.data()); |
| |
| s_gles2.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
| s_gles2.glEnable(GL_BLEND); |
| |
| s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, prevUnpackAlignment); |
| |
| mHaveNewMask = false; |
| mMaskIsValid = true; |
| } |
| shouldDrawMask = mMaskIsValid && wantOverlay; |
| // Scale the texture to only show that actual mask. |
| overlayScale[0] = static_cast<float>(mMaskTextureWidth) / |
| static_cast<float>(mMaskWidth) * scale[0]; |
| overlayScale[1] = static_cast<float>(mMaskTextureHeight) / |
| static_cast<float>(mMaskHeight) * scale[1]; |
| } |
| |
| if (shouldDrawMask) { |
| if (mBlendResetNeeded) { |
| s_gles2.glEnable(GL_BLEND); |
| mBlendResetNeeded = false; |
| } |
| s_gles2.glUniform2f(mScaleSlot, overlayScale[0], overlayScale[1]); |
| // mMaskTexture should only be accessed on the thread where drawImpl is |
| // called, hence no need for lock. |
| s_gles2.glBindTexture(GL_TEXTURE_2D, mMaskTexture); |
| s_gles2.glDrawElements(GL_TRIANGLES, kIndicesPerDraw, GL_UNSIGNED_BYTE, |
| (const GLvoid*)indexShift); |
| // Reset to the "normal" texture |
| s_gles2.glBindTexture(GL_TEXTURE_2D, texture); |
| s_gles2.glUniform2f(mScaleSlot, scale[0], scale[1]); |
| } |
| |
| #ifdef DEBUG_TEXTURE_DRAW |
| err = s_gles2.glGetError(); |
| if (err != GL_NO_ERROR) { |
| ERR("%s: Could not glDrawElements() error=0x%x\n", |
| __FUNCTION__, err); |
| } |
| #endif |
| |
| // TODO(digit): Restore previous program state. |
| // For now, reset back to zero and assume other users will |
| // follow the same protocol. |
| s_gles2.glUseProgram(0); |
| s_gles2.glDisableVertexAttribArray(mPositionSlot); |
| s_gles2.glDisableVertexAttribArray(mInCoordSlot); |
| s_gles2.glBindBuffer(GL_ARRAY_BUFFER, 0); |
| s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| |
| return true; |
| } |
| |
| TextureDraw::~TextureDraw() { |
| s_gles2.glDeleteBuffers(1, &mIndexBuffer); |
| s_gles2.glDeleteBuffers(1, &mVertexBuffer); |
| |
| if (mFragmentShader) { |
| s_gles2.glDeleteShader(mFragmentShader); |
| } |
| if (mVertexShader) { |
| s_gles2.glDeleteShader(mVertexShader); |
| } |
| if (mMaskTexture) { |
| s_gles2.glDeleteTextures(1, &mMaskTexture); |
| } |
| } |
| |
| void TextureDraw::setScreenMask(int width, int height, const unsigned char* rgbaData) { |
| android::base::AutoLock lock(mMaskLock); |
| if (width <= 0 || height <= 0 || rgbaData == nullptr) { |
| mMaskIsValid = false; |
| return; |
| } |
| |
| mShouldReallocateTexture = |
| (width > mMaskTextureWidth) || (height > mMaskTextureHeight); |
| auto nextMaskTextureWidth = std::max(width, mMaskTextureWidth); |
| auto nextMaskTextureHeight = std::max(height, mMaskTextureHeight); |
| mMaskPixels.resize(nextMaskTextureWidth * nextMaskTextureHeight * 4); |
| // Save the data for use in the right context |
| std::copy(rgbaData, rgbaData + width * height * 4, mMaskPixels.begin()); |
| |
| mHaveNewMask = true; |
| mMaskWidth = width; |
| mMaskHeight = height; |
| } |
| |
| void TextureDraw::preDrawLayer() { |
| if (!mProgram) { |
| ERR("%s: no program\n", __FUNCTION__); |
| return; |
| } |
| s_gles2.glUseProgram(mProgram); |
| #ifdef DEBUG_TEXTURE_DRAW |
| GLenum err = s_gles2.glGetError(); |
| if (err != GL_NO_ERROR) { |
| ERR("%s: Could not use program error=0x%x\n", |
| __FUNCTION__, err); |
| } |
| #endif |
| |
| s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); |
| #ifdef DEBUG_TEXTURE_DRAW |
| err = s_gles2.glGetError(); |
| if (err != GL_NO_ERROR) { |
| ERR("%s: Could not bind GL_ARRAY_BUFFER error=0x%x\n", |
| __FUNCTION__, err); |
| } |
| #endif |
| s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer); |
| #ifdef DEBUG_TEXTURE_DRAW |
| err = s_gles2.glGetError(); |
| if (err != GL_NO_ERROR) { |
| ERR("%s: Could not glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) error=0x%x\n", |
| __FUNCTION__, err); |
| } |
| #endif |
| |
| s_gles2.glEnableVertexAttribArray(mPositionSlot); |
| s_gles2.glVertexAttribPointer(mPositionSlot, |
| 3, |
| GL_FLOAT, |
| GL_FALSE, |
| sizeof(Vertex), |
| 0); |
| |
| s_gles2.glEnableVertexAttribArray(mInCoordSlot); |
| s_gles2.glVertexAttribPointer(mInCoordSlot, |
| 2, |
| GL_FLOAT, |
| GL_FALSE, |
| sizeof(Vertex), |
| reinterpret_cast<GLvoid*>( |
| static_cast<uintptr_t>( |
| sizeof(float) * 3))); |
| #ifdef DEBUG_TEXTURE_DRAW |
| err = s_gles2.glGetError(); |
| if (err != GL_NO_ERROR) { |
| ERR("%s: Could glVertexAttribPointer with mPositionSlot error=0x%x\n", |
| __FUNCTION__, err); |
| } |
| #endif |
| |
| // set composition default |
| s_gles2.glUniform1i(mComposeMode, 2); |
| s_gles2.glActiveTexture(GL_TEXTURE0); |
| s_gles2.glUniform1i(mTextureSlot, 0); |
| s_gles2.glEnable(GL_BLEND); |
| s_gles2.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
| } |
| |
| void TextureDraw::prepareForDrawLayer() { |
| // clear color |
| s_gles2.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| } |
| |
| void TextureDraw::drawLayer(const ComposeLayer& layer, int frameWidth, int frameHeight, |
| int cbWidth, int cbHeight, GLuint texture) { |
| preDrawLayer(); |
| switch(layer.composeMode) { |
| case HWC2_COMPOSITION_DEVICE: |
| s_gles2.glBindTexture(GL_TEXTURE_2D, texture); |
| break; |
| case HWC2_COMPOSITION_SOLID_COLOR: { |
| s_gles2.glUniform1i(mComposeMode, layer.composeMode); |
| s_gles2.glUniform4f(mColor, |
| layer.color.r/255.0, layer.color.g/255.0, |
| layer.color.b/255.0, layer.color.a/255.0); |
| break; |
| } |
| case HWC2_COMPOSITION_CLIENT: |
| case HWC2_COMPOSITION_CURSOR: |
| case HWC2_COMPOSITION_SIDEBAND: |
| case HWC2_COMPOSITION_INVALID: |
| default: |
| ERR("%s: invalid composition mode %d", __FUNCTION__, layer.composeMode); |
| return; |
| } |
| |
| switch(layer.blendMode) { |
| case HWC2_BLEND_MODE_NONE: |
| s_gles2.glDisable(GL_BLEND); |
| mBlendResetNeeded = true; |
| break; |
| case HWC2_BLEND_MODE_PREMULTIPLIED: |
| break; |
| case HWC2_BLEND_MODE_INVALID: |
| case HWC2_BLEND_MODE_COVERAGE: |
| default: |
| ERR("%s: invalid blendMode %d", __FUNCTION__, layer.blendMode); |
| return; |
| } |
| |
| s_gles2.glUniform1f(mAlpha, layer.alpha); |
| |
| float edges[4]; |
| edges[0] = 1 - 2.0 * (frameWidth - layer.displayFrame.left)/frameWidth; |
| edges[1] = 1 - 2.0 * (frameHeight - layer.displayFrame.top)/frameHeight; |
| edges[2] = 1 - 2.0 * (frameWidth - layer.displayFrame.right)/frameWidth; |
| edges[3] = 1- 2.0 * (frameHeight - layer.displayFrame.bottom)/frameHeight; |
| |
| float crop[4]; |
| crop[0] = layer.crop.left/cbWidth; |
| crop[1] = layer.crop.top/cbHeight; |
| crop[2] = layer.crop.right/cbWidth; |
| crop[3] = layer.crop.bottom/cbHeight; |
| |
| // setup the |translation| uniform value. |
| s_gles2.glUniform2f(mTranslationSlot, (-edges[2] - edges[0])/2, |
| (-edges[3] - edges[1])/2); |
| s_gles2.glUniform2f(mScaleSlot, (edges[2] - edges[0])/2, |
| (edges[1] - edges[3])/2); |
| s_gles2.glUniform2f(mCoordTranslation, crop[0], crop[3]); |
| s_gles2.glUniform2f(mCoordScale, crop[2] - crop[0], crop[1] - crop[3]); |
| |
| intptr_t indexShift; |
| switch(layer.transform) { |
| case HWC_TRANSFORM_ROT_90: |
| indexShift = 1 * kIndicesPerDraw; |
| break; |
| case HWC_TRANSFORM_ROT_180: |
| indexShift = 2 * kIndicesPerDraw; |
| break; |
| case HWC_TRANSFORM_ROT_270: |
| indexShift = 3 * kIndicesPerDraw; |
| break; |
| case HWC_TRANSFORM_FLIP_H: |
| indexShift = 4 * kIndicesPerDraw; |
| break; |
| case HWC_TRANSFORM_FLIP_V: |
| indexShift = 5 * kIndicesPerDraw; |
| break; |
| case HWC_TRANSFORM_FLIP_H_ROT_90: |
| indexShift = 6 * kIndicesPerDraw; |
| break; |
| case HWC_TRANSFORM_FLIP_V_ROT_90: |
| indexShift = 7 * kIndicesPerDraw; |
| break; |
| default: |
| indexShift = 0; |
| } |
| s_gles2.glDrawElements(GL_TRIANGLES, kIndicesPerDraw, GL_UNSIGNED_BYTE, |
| (const GLvoid*)indexShift); |
| #ifdef DEBUG_TEXTURE_DRAW |
| GLenum err = s_gles2.glGetError(); |
| if (err != GL_NO_ERROR) { |
| ERR("%s: Could not glDrawElements() error=0x%x\n", |
| __FUNCTION__, err); |
| } |
| #endif |
| |
| // restore the default value for the next draw layer |
| if (layer.composeMode != HWC2_COMPOSITION_DEVICE) { |
| s_gles2.glUniform1i(mComposeMode, HWC2_COMPOSITION_DEVICE); |
| } |
| if (layer.blendMode != HWC2_BLEND_MODE_PREMULTIPLIED) { |
| s_gles2.glEnable(GL_BLEND); |
| mBlendResetNeeded = false; |
| s_gles2.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
| } |
| } |
| |
| // Do Post right after drawing each layer, so keep using this program |
| void TextureDraw::cleanupForDrawLayer() { |
| s_gles2.glUniform1f(mAlpha, 1.0); |
| s_gles2.glUniform1i(mComposeMode, HWC2_COMPOSITION_DEVICE); |
| s_gles2.glUniform2f(mTranslationSlot, 0.0, 0.0); |
| s_gles2.glUniform2f(mScaleSlot, 1.0, 1.0); |
| s_gles2.glUniform2f(mCoordTranslation, 0.0, 0.0); |
| s_gles2.glUniform2f(mCoordScale, 1.0, 1.0); |
| } |
| |
| } // namespace gl |
| } // namespace gfxstream |