You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
686 lines
24 KiB
686 lines
24 KiB
// 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 "DispatchTables.h"
|
|
|
|
#include "host-common/crash_reporter.h"
|
|
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#define ERR(...) fprintf(stderr, __VA_ARGS__)
|
|
|
|
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);
|
|
|
|
#ifndef NDEBUG
|
|
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);
|
|
|
|
#ifndef NDEBUG
|
|
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);
|
|
|
|
#ifndef NDEBUG
|
|
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);
|
|
|
|
#ifndef NDEBUG
|
|
// 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);
|
|
#ifndef NDEBUG
|
|
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.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 = std::max(mMaskTextureWidth, mMaskWidth);
|
|
mMaskTextureHeight = std::max(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]);
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
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::prepareForDrawLayer() {
|
|
if (!mProgram) {
|
|
ERR("%s: no program\n", __FUNCTION__);
|
|
return;
|
|
}
|
|
s_gles2.glUseProgram(mProgram);
|
|
#ifndef NDEBUG
|
|
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);
|
|
#ifndef NDEBUG
|
|
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);
|
|
#ifndef NDEBUG
|
|
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)));
|
|
#ifndef NDEBUG
|
|
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);
|
|
|
|
// clear color
|
|
s_gles2.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
}
|
|
|
|
void TextureDraw::drawLayer(ComposeLayer* l, int frameWidth, int frameHeight,
|
|
int cbWidth, int cbHeight, GLuint texture) {
|
|
switch(l->composeMode) {
|
|
case HWC2_COMPOSITION_DEVICE:
|
|
s_gles2.glBindTexture(GL_TEXTURE_2D, texture);
|
|
break;
|
|
case HWC2_COMPOSITION_SOLID_COLOR: {
|
|
s_gles2.glUniform1i(mComposeMode, l->composeMode);
|
|
s_gles2.glUniform4f(mColor,
|
|
l->color.r/255.0, l->color.g/255.0,
|
|
l->color.b/255.0, l->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__, l->composeMode);
|
|
return;
|
|
}
|
|
|
|
switch(l->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__, l->blendMode);
|
|
return;
|
|
}
|
|
|
|
s_gles2.glUniform1f(mAlpha, l->alpha);
|
|
|
|
float edges[4];
|
|
edges[0] = 1 - 2.0 * (frameWidth - l->displayFrame.left)/frameWidth;
|
|
edges[1] = 1 - 2.0 * (frameHeight - l->displayFrame.top)/frameHeight;
|
|
edges[2] = 1 - 2.0 * (frameWidth - l->displayFrame.right)/frameWidth;
|
|
edges[3] = 1- 2.0 * (frameHeight - l->displayFrame.bottom)/frameHeight;
|
|
|
|
float crop[4];
|
|
crop[0] = l->crop.left/cbWidth;
|
|
crop[1] = l->crop.top/cbHeight;
|
|
crop[2] = l->crop.right/cbWidth;
|
|
crop[3] = l->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(l->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);
|
|
#ifndef NDEBUG
|
|
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 (l->composeMode != HWC2_COMPOSITION_DEVICE) {
|
|
s_gles2.glUniform1i(mComposeMode, HWC2_COMPOSITION_DEVICE);
|
|
}
|
|
if (l->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);
|
|
}
|