blob: a8df3fb4ff566f734cb01e91bcef306eccbc3cf4 [file] [log] [blame]
// 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 <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) {
s_gles2.glDeleteShader(shader);
return 0;
}
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"
"void main(void) {\n"
" gl_Position.xy = position.xy - translation.xy;\n"
" gl_Position.zw = position.zw;\n"
" outCoord = inCoord;\n"
"}\n";
// Similarly, just interpolate texture coordinates.
const char kFragmentShaderSource[] =
"varying lowp vec2 outCoord;\n"
"uniform sampler2D texture;\n"
"void main(void) {\n"
" gl_FragColor = texture2D(texture, outCoord);\n"
"}\n";
// Hard-coded arrays of vertex information.
struct Vertex {
float pos[3];
float coord[2];
};
const Vertex kVertices[] = {
{{ +1, -1, +0 }, { +1, +1 }},
{{ +1, +1, +0 }, { +1, +0 }},
{{ -1, +1, +0 }, { +0, +0 }},
{{ -1, -1, +0 }, { +0, +1 }},
{{ +1, -1, +0 }, { +1, +0 }},
{{ +1, +1, +0 }, { +0, +0 }},
{{ -1, +1, +0 }, { +0, +1 }},
{{ -1, -1, +0 }, { +1, +1 }},
{{ +1, -1, +0 }, { +0, +0 }},
{{ +1, +1, +0 }, { +0, +1 }},
{{ -1, +1, +0 }, { +1, +1 }},
{{ -1, -1, +0 }, { +1, +0 }},
{{ +1, -1, +0 }, { +0, +1 }},
{{ +1, +1, +0 }, { +1, +1 }},
{{ -1, +1, +0 }, { +1, +0 }},
{{ -1, -1, +0 }, { +0, +0 }},
};
// 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
};
const GLint kIndicesLen = sizeof(kIndices) / sizeof(kIndices[0]);
const GLint kIndicesPerDraw = 6;
} // namespace
TextureDraw::TextureDraw() :
mVertexShader(0),
mFragmentShader(0),
mProgram(0),
mPositionSlot(-1),
mInCoordSlot(-1),
mTextureSlot(-1),
mTranslationSlot(-1) {
// 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);
mTranslationSlot = s_gles2.glGetUniformLocation(mProgram, "translation");
mTextureSlot = s_gles2.glGetUniformLocation(mProgram, "texture");
#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);
}
bool TextureDraw::draw(GLuint texture, float rotation, float dx, float dy) {
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);
const intptr_t indexShift = intRotation * kIndicesPerDraw;
s_gles2.glDrawElements(GL_TRIANGLES, kIndicesPerDraw, GL_UNSIGNED_BYTE,
(const GLvoid*)indexShift);
#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.
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);
}
}