// 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 #include #include #include #define ERR(...) fprintf(stderr, __VA_ARGS__) // M_PI isn't defined in C++ (when strict ISO compliance is enabled) #ifndef M_PI #define M_PI 3.14159265358979323846264338327 #endif 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(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 clockwise // rotation. const char kVertexShaderSource[] = "#version 100\n" "const int _translator_gl_MaxVertexUniformVectors = 256;\n" "const int _translator_gl_MaxFragmentUniformVectors = 256;\n" "const int _translator_gl_MaxVaryingVectors = 15;\n" "#define gl_MaxVertexUniformVectors _translator_gl_MaxVertexUniformVectors\n" "#define gl_MaxFragmentUniformVectors _translator_gl_MaxFragmentUniformVectors\n" "#define gl_MaxVaryingVectors _translator_gl_MaxVaryingVectors\n" "#line 1\n" "attribute vec4 position;\n" "attribute vec2 inCoord;\n" "varying lowp vec2 outCoord;\n" "uniform float rotation;\n" "uniform vec2 translation;\n" "void main(void) {\n" " float cs = cos(rotation);\n" " float sn = sin(rotation);\n" " gl_Position.x = position.x * cs + position.y * sn - translation.x;\n" " gl_Position.y = position.y * cs - position.x * sn - translation.y;\n" " gl_Position.zw = position.zw;\n" " outCoord = inCoord;\n" "}\n"; // Similarly, just interpolate texture coordinates. const char kFragmentShaderSource[] = "#version 100\n" "const int _translator_gl_MaxVertexUniformVectors = 256;\n" "const int _translator_gl_MaxFragmentUniformVectors = 256;\n" "const int _translator_gl_MaxVaryingVectors = 15;\n" "#define gl_MaxVertexUniformVectors _translator_gl_MaxVertexUniformVectors\n" "#define gl_MaxFragmentUniformVectors _translator_gl_MaxFragmentUniformVectors\n" "#define gl_MaxVaryingVectors _translator_gl_MaxVaryingVectors\n" "#line 1\n" "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 }}, }; const GLubyte kIndices[] = { 0, 1, 2, 2, 3, 0 }; const GLint kIndicesLen = sizeof(kIndices) / sizeof(kIndices[0]); } // namespace TextureDraw::TextureDraw(EGLDisplay display) : mDisplay(display), mVertexShader(0), mFragmentShader(0), mProgram(0), mPositionSlot(-1), mInCoordSlot(-1), mTextureSlot(-1), mRotationSlot(-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); mRotationSlot = s_gles2.glGetUniformLocation(mProgram, "rotation"); mTranslationSlot = s_gles2.glGetUniformLocation(mProgram, "translation"); mTextureSlot = s_gles2.glGetUniformLocation(mProgram, "texture"); #if 0 printf("SLOTS position=%d inCoord=%d texture=%d rotation=%d\n", mPositionSlot, mInCoordSlot, mTextureSlot, mRotationSlot); #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. GLenum err; s_gles2.glUseProgram(mProgram); err = s_gles2.glGetError(); if (err != GL_NO_ERROR) { ERR("%s: Could not use program error=0x%x\n", __FUNCTION__, err); } // Setup the |position| attribute values. s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); err = s_gles2.glGetError(); if (err != GL_NO_ERROR) { ERR("%s: Could not bind GL_ARRAY_BUFFER error=0x%x\n", __FUNCTION__, err); } s_gles2.glEnableVertexAttribArray(mPositionSlot); s_gles2.glVertexAttribPointer(mPositionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0); err = s_gles2.glGetError(); if (err != GL_NO_ERROR) { ERR("%s: Could glVertexAttribPointer with mPositionSlot error=0x%x\n", __FUNCTION__, err); } // Setup the |inCoord| attribute values. s_gles2.glEnableVertexAttribArray(mInCoordSlot); s_gles2.glVertexAttribPointer(mInCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast( static_cast( 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 |rotation| uniform value. s_gles2.glUniform1f(mRotationSlot, rotation * M_PI / 180.); s_gles2.glUniform2f(mTranslationSlot, dx, dy); #if 1 // 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); err = s_gles2.glGetError(); if (err != GL_NO_ERROR) { ERR("%s: Could not glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) error=0x%x\n", __FUNCTION__, err); } s_gles2.glDrawElements(GL_TRIANGLES, kIndicesLen, GL_UNSIGNED_BYTE, 0); err = s_gles2.glGetError(); if (err != GL_NO_ERROR) { ERR("%s: Could not glDrawElements() error=0x%x\n", __FUNCTION__, err); } // 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); } }