summaryrefslogtreecommitdiffstats
path: root/media/mca/filterfw/native/core/shader_program.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/mca/filterfw/native/core/shader_program.cpp')
-rw-r--r--media/mca/filterfw/native/core/shader_program.cpp1122
1 files changed, 1122 insertions, 0 deletions
diff --git a/media/mca/filterfw/native/core/shader_program.cpp b/media/mca/filterfw/native/core/shader_program.cpp
new file mode 100644
index 0000000..d92eb31
--- /dev/null
+++ b/media/mca/filterfw/native/core/shader_program.cpp
@@ -0,0 +1,1122 @@
+/*
+ * Copyright (C) 2011 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 "base/logging.h"
+
+#include "core/geometry.h"
+#include "core/gl_buffer_interface.h"
+#include "core/gl_env.h"
+#include "core/gl_frame.h"
+#include "core/shader_program.h"
+#include "core/vertex_frame.h"
+
+#include <string>
+#include <sstream>
+#include <vector>
+
+namespace android {
+namespace filterfw {
+
+// VBO attachment keys
+static const int kDefaultVboKey = 1;
+
+static const char* s_default_vertex_shader_source_ =
+ "attribute vec4 a_position;\n"
+ "attribute vec2 a_texcoord;\n"
+ "varying vec2 v_texcoord;\n"
+ "void main() {\n"
+ " gl_Position = a_position;\n"
+ " v_texcoord = a_texcoord;\n"
+ "}\n";
+
+// Helper Functions ////////////////////////////////////////////////////////////
+// Maps coordinates x,y in the unit rectangle over to the quadrangle specified
+// by the four points in b. The result coordinates are written to xt and yt.
+static void GetTileCoords(const float* b, float x, float y, float* xt, float* yt) {
+ const float w0 = (1.0f - x) * (1.0f - y);
+ const float w1 = x * (1.0f - y);
+ const float w2 = (1.0f - x) * y;
+ const float w3 = x * y;
+
+ *xt = w0 * b[0] + w1 * b[2] + w2 * b[4] + w3 * b[6];
+ *yt = w0 * b[1] + w1 * b[3] + w2 * b[5] + w3 * b[7];
+}
+
+static inline float AdjustRatio(float current, float next) {
+ return (current + next) / 2.0;
+}
+
+// VertexAttrib implementation /////////////////////////////////////////////////
+ShaderProgram::VertexAttrib::VertexAttrib()
+ : is_const(true),
+ index(-1),
+ normalized(false),
+ stride(0),
+ components(0),
+ offset(0),
+ type(GL_FLOAT),
+ vbo(0),
+ values(NULL),
+ owned_data(NULL) {
+}
+
+// ShaderProgram implementation ////////////////////////////////////////////////
+ShaderProgram::ShaderProgram(GLEnv* gl_env, const std::string& fragment_shader)
+ : fragment_shader_source_(fragment_shader),
+ vertex_shader_source_(s_default_vertex_shader_source_),
+ fragment_shader_(0),
+ vertex_shader_(0),
+ program_(0),
+ gl_env_(gl_env),
+ base_texture_unit_(GL_TEXTURE0),
+ source_coords_(NULL),
+ target_coords_(NULL),
+ manage_coordinates_(false),
+ tile_x_count_(1),
+ tile_y_count_(1),
+ vertex_count_(4),
+ draw_mode_(GL_TRIANGLE_STRIP),
+ clears_(false),
+ blending_(false),
+ sfactor_(GL_SRC_ALPHA),
+ dfactor_(GL_ONE_MINUS_SRC_ALPHA) {
+ SetDefaultCoords();
+}
+
+ShaderProgram::ShaderProgram(GLEnv* gl_env,
+ const std::string& vertex_shader,
+ const std::string& fragment_shader)
+ : fragment_shader_source_(fragment_shader),
+ vertex_shader_source_(vertex_shader),
+ fragment_shader_(0),
+ vertex_shader_(0),
+ program_(0),
+ gl_env_(gl_env),
+ base_texture_unit_(GL_TEXTURE0),
+ source_coords_(NULL),
+ target_coords_(NULL),
+ manage_coordinates_(false),
+ tile_x_count_(1),
+ tile_y_count_(1),
+ vertex_count_(4),
+ draw_mode_(GL_TRIANGLE_STRIP),
+ clears_(false),
+ blending_(false),
+ sfactor_(GL_SRC_ALPHA),
+ dfactor_(GL_ONE_MINUS_SRC_ALPHA) {
+ SetDefaultCoords();
+}
+
+ShaderProgram::~ShaderProgram() {
+ // Delete our vertex data
+ delete[] source_coords_;
+ delete[] target_coords_;
+
+ // Delete any owned attribute data
+ VertexAttribMap::const_iterator iter;
+ for (iter = attrib_values_.begin(); iter != attrib_values_.end(); ++iter) {
+ const VertexAttrib& attrib = iter->second;
+ if (attrib.owned_data)
+ delete[] attrib.owned_data;
+ }
+}
+
+void ShaderProgram::SetDefaultCoords() {
+ if (!source_coords_)
+ source_coords_ = new float[8];
+ if (!target_coords_)
+ target_coords_ = new float[8];
+
+ source_coords_[0] = 0.0f;
+ source_coords_[1] = 0.0f;
+ source_coords_[2] = 1.0f;
+ source_coords_[3] = 0.0f;
+ source_coords_[4] = 0.0f;
+ source_coords_[5] = 1.0f;
+ source_coords_[6] = 1.0f;
+ source_coords_[7] = 1.0f;
+
+ target_coords_[0] = -1.0f;
+ target_coords_[1] = -1.0f;
+ target_coords_[2] = 1.0f;
+ target_coords_[3] = -1.0f;
+ target_coords_[4] = -1.0f;
+ target_coords_[5] = 1.0f;
+ target_coords_[6] = 1.0f;
+ target_coords_[7] = 1.0f;
+
+}
+
+ShaderProgram* ShaderProgram::CreateIdentity(GLEnv* gl_env) {
+ const char* s_id_fragment_shader =
+ "precision mediump float;\n"
+ "uniform sampler2D tex_sampler_0;\n"
+ "varying vec2 v_texcoord;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n"
+ "}\n";
+ ShaderProgram* result = new ShaderProgram(gl_env, s_id_fragment_shader);
+ result->CompileAndLink();
+ return result;
+}
+
+bool ShaderProgram::IsVarValid(ProgramVar var) {
+ return var != -1;
+}
+
+bool ShaderProgram::Process(const std::vector<const GLTextureHandle*>& input,
+ GLFrameBufferHandle* output) {
+ // TODO: This can be optimized: If the input and output are the same, as in
+ // the last iteration (typical of a multi-pass filter), a lot of this setup
+ // may be skipped.
+
+ // Abort if program did not successfully compile and link
+ if (!IsExecutable()) {
+ ALOGE("ShaderProgram: unexecutable program!");
+ return false;
+ }
+
+ // Focus the FBO of the output
+ if (!output->FocusFrameBuffer()) {
+ ALOGE("Unable to focus frame buffer");
+ return false;
+ }
+
+ // Get all required textures
+ std::vector<GLuint> textures;
+ std::vector<GLenum> targets;
+ for (unsigned i = 0; i < input.size(); ++i) {
+ // Get the current input frame and make sure it is a GL frame
+ if (input[i]) {
+ // Get the texture bound to that frame
+ const GLuint tex_id = input[i]->GetTextureId();
+ const GLenum target = input[i]->GetTextureTarget();
+ if (tex_id == 0) {
+ ALOGE("ShaderProgram: invalid texture id at input: %d!", i);
+ return false;
+ }
+ textures.push_back(tex_id);
+ targets.push_back(target);
+ }
+ }
+
+ // And render!
+ if (!RenderFrame(textures, targets)) {
+ ALOGE("Unable to render frame");
+ return false;
+ }
+ return true;
+}
+
+bool ShaderProgram::Process(const std::vector<const GLFrame*>& input, GLFrame* output) {
+ std::vector<const GLTextureHandle*> textures(input.size());
+ std::copy(input.begin(), input.end(), textures.begin());
+ return Process(textures, output);
+}
+
+void ShaderProgram::SetSourceRect(float x, float y, float width, float height) {
+ Quad quad(Point(x, y),
+ Point(x + width, y),
+ Point(x, y + height),
+ Point(x + width, y + height));
+ SetSourceRegion(quad);
+}
+
+void ShaderProgram::SetSourceRegion(const Quad& quad) {
+ int index = 0;
+ for (int i = 0; i < 4; ++i, index += 2) {
+ source_coords_[index] = quad.point(i).x();
+ source_coords_[index+1] = quad.point(i).y();
+ }
+}
+
+void ShaderProgram::SetTargetRect(float x, float y, float width, float height) {
+ Quad quad(Point(x, y),
+ Point(x + width, y),
+ Point(x, y + height),
+ Point(x + width, y + height));
+ SetTargetRegion(quad);
+}
+
+void ShaderProgram::SetTargetRegion(const Quad& quad) {
+ int index = 0;
+ for (int i = 0; i < 4; ++i, index += 2) {
+ target_coords_[index] = (quad.point(i).x() * 2.0) - 1.0;
+ target_coords_[index+1] = (quad.point(i).y() * 2.0) - 1.0;
+ }
+}
+
+bool ShaderProgram::CompileAndLink() {
+ // Make sure we haven't compiled and linked already
+ if (vertex_shader_ != 0 || fragment_shader_ != 0 || program_ != 0) {
+ ALOGE("Attempting to re-compile shaders!");
+ return false;
+ }
+
+ // Compile vertex shader
+ vertex_shader_ = CompileShader(GL_VERTEX_SHADER,
+ vertex_shader_source_.c_str());
+ if (!vertex_shader_) {
+ ALOGE("Shader compilation failed!");
+ return false;
+ }
+
+ // Compile fragment shader
+ fragment_shader_ = CompileShader(GL_FRAGMENT_SHADER,
+ fragment_shader_source_.c_str());
+ if (!fragment_shader_)
+ return false;
+
+ // Link
+ GLuint shaders[2] = { vertex_shader_, fragment_shader_ };
+ program_ = LinkProgram(shaders, 2);
+
+ // Scan for all uniforms in the program
+ ScanUniforms();
+
+ // Check if we manage all coordinates
+ if (program_ != 0) {
+ ProgramVar tex_coord_attr = glGetAttribLocation(program_, TexCoordAttributeName().c_str());
+ ProgramVar pos_coord_attr = glGetAttribLocation(program_, PositionAttributeName().c_str());
+ manage_coordinates_ = (tex_coord_attr >= 0 && pos_coord_attr >= 0);
+ } else {
+ ALOGE("Could not link shader program!");
+ return false;
+ }
+
+ return true;
+}
+
+GLuint ShaderProgram::CompileShader(GLenum shader_type, const char* source) {
+ LOG_FRAME("Compiling source:\n[%s]", source);
+
+ // Create shader
+ GLuint shader = glCreateShader(shader_type);
+ if (shader) {
+ // Compile source
+ glShaderSource(shader, 1, &source, NULL);
+ glCompileShader(shader);
+
+ // Check for compilation errors
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ // Log the compilation error messages
+ ALOGE("Problem compiling shader! Source:");
+ ALOGE("%s", source);
+ std::string src(source);
+ unsigned int cur_pos = 0;
+ unsigned int next_pos = 0;
+ int line_number = 1;
+ while ( (next_pos = src.find_first_of('\n', cur_pos)) != std::string::npos) {
+ ALOGE("%03d : %s", line_number, src.substr(cur_pos, next_pos-cur_pos).c_str());
+ cur_pos = next_pos + 1;
+ line_number++;
+ }
+ ALOGE("%03d : %s", line_number, src.substr(cur_pos, next_pos-cur_pos).c_str());
+
+ GLint log_length = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
+ if (log_length) {
+ char* error_log = new char[log_length];
+ if (error_log) {
+ glGetShaderInfoLog(shader, log_length, NULL, error_log);
+ ALOGE("Shader compilation error %d:\n%s\n", shader_type, error_log);
+ delete[] error_log;
+ }
+ }
+ glDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ return shader;
+}
+
+GLuint ShaderProgram::LinkProgram(GLuint* shaders, GLuint count) {
+ GLuint program = glCreateProgram();
+ if (program) {
+ // Attach all compiled shaders
+ for (GLuint i = 0; i < count; ++i) {
+ glAttachShader(program, shaders[i]);
+ if (GLEnv::CheckGLError("glAttachShader")) return 0;
+ }
+
+ // Link
+ glLinkProgram(program);
+
+ // Check for linking errors
+ GLint linked = 0;
+ glGetProgramiv(program, GL_LINK_STATUS, &linked);
+ if (linked != GL_TRUE) {
+ // Log the linker error messages
+ GLint log_length = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
+ if (log_length) {
+ char* error_log = new char[log_length];
+ if (error_log) {
+ glGetProgramInfoLog(program, log_length, NULL, error_log);
+ ALOGE("Program Linker Error:\n%s\n", error_log);
+ delete[] error_log;
+ }
+ }
+ glDeleteProgram(program);
+ program = 0;
+ }
+ }
+ return program;
+}
+
+void ShaderProgram::ScanUniforms() {
+ int uniform_count;
+ int buffer_size;
+ GLenum type;
+ GLint capacity;
+ glGetProgramiv(program_, GL_ACTIVE_UNIFORMS, &uniform_count);
+ glGetProgramiv(program_, GL_ACTIVE_UNIFORM_MAX_LENGTH, &buffer_size);
+ std::vector<GLchar> name(buffer_size);
+ for (int i = 0; i < uniform_count; ++i) {
+ glGetActiveUniform(program_, i, buffer_size, NULL, &capacity, &type, &name[0]);
+ ProgramVar uniform_id = glGetUniformLocation(program_, &name[0]);
+ uniform_indices_[uniform_id] = i;
+ }
+}
+
+bool ShaderProgram::PushCoords(ProgramVar attr, float* coords) {
+ // If the shader does not define these, we simply ignore the coordinates, and assume that the
+ // user is managing coordinates.
+ if (attr >= 0) {
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(coords);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glVertexAttribPointer(attr, 2, GL_FLOAT, false, 2 * sizeof(float), data);
+ glEnableVertexAttribArray(attr);
+ return !GLEnv::CheckGLError("Pushing vertex coordinates");
+ }
+ return true;
+}
+
+bool ShaderProgram::PushSourceCoords(float* coords) {
+ ProgramVar tex_coord_attr = glGetAttribLocation(program_, TexCoordAttributeName().c_str());
+ return PushCoords(tex_coord_attr, coords);
+}
+
+bool ShaderProgram::PushTargetCoords(float* coords) {
+ ProgramVar pos_coord_attr = glGetAttribLocation(program_, PositionAttributeName().c_str());
+ return PushCoords(pos_coord_attr, coords);
+}
+
+std::string ShaderProgram::InputTextureUniformName(int index) {
+ std::stringstream tex_name;
+ tex_name << "tex_sampler_" << index;
+ return tex_name.str();
+}
+
+bool ShaderProgram::BindInputTextures(const std::vector<GLuint>& textures,
+ const std::vector<GLenum>& targets) {
+ for (unsigned i = 0; i < textures.size(); ++i) {
+ // Activate texture unit i
+ glActiveTexture(BaseTextureUnit() + i);
+ if (GLEnv::CheckGLError("Activating Texture Unit"))
+ return false;
+
+ // Bind our texture
+ glBindTexture(targets[i], textures[i]);
+ LOG_FRAME("Binding texture %d", textures[i]);
+ if (GLEnv::CheckGLError("Binding Texture"))
+ return false;
+
+ // Set the texture handle in the shader to unit i
+ ProgramVar tex_var = GetUniform(InputTextureUniformName(i));
+ if (tex_var >= 0) {
+ glUniform1i(tex_var, i);
+ } else {
+ ALOGE("ShaderProgram: Shader does not seem to support %d number of "
+ "inputs! Missing uniform 'tex_sampler_%d'!", textures.size(), i);
+ return false;
+ }
+
+ if (GLEnv::CheckGLError("Texture Variable Binding"))
+ return false;
+ }
+
+ return true;
+}
+
+bool ShaderProgram::UseProgram() {
+ if (GLEnv::GetCurrentProgram() != program_) {
+ LOG_FRAME("Using program %d", program_);
+ glUseProgram(program_);
+ return !GLEnv::CheckGLError("Use Program");
+ }
+ return true;
+}
+
+bool ShaderProgram::RenderFrame(const std::vector<GLuint>& textures,
+ const std::vector<GLenum>& targets) {
+ // Make sure we have enough texture units to accomodate the textures
+ if (textures.size() > static_cast<unsigned>(MaxTextureUnits())) {
+ ALOGE("ShaderProgram: Number of input textures is unsupported on this "
+ "platform!");
+ return false;
+ }
+
+ // Prepare to render
+ if (!BeginDraw()) {
+ ALOGE("ShaderProgram: couldn't initialize gl for drawing!");
+ return false;
+ }
+
+ // Bind input textures
+ if (!BindInputTextures(textures, targets)) {
+ ALOGE("BindInputTextures failed");
+ return false;
+ }
+
+ if (LOG_EVERY_FRAME) {
+ int fbo, program, buffer;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
+ glGetIntegerv(GL_CURRENT_PROGRAM, &program);
+ glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &buffer);
+ ALOGV("RenderFrame: fbo %d prog %d buff %d", fbo, program, buffer);
+ }
+
+ // Render!
+ const bool requestTile = (tile_x_count_ != 1 || tile_y_count_ != 1);
+ const bool success = (!requestTile || !manage_coordinates_ || vertex_count_ != 4)
+ ? Draw()
+ : DrawTiled();
+
+ // Pop vertex attributes
+ PopAttributes();
+
+ return success && !GLEnv::CheckGLError("Rendering");
+}
+
+bool ShaderProgram::Draw() {
+ if (PushSourceCoords(source_coords_) && PushTargetCoords(target_coords_)) {
+ glDrawArrays(draw_mode_, 0, vertex_count_);
+ return true;
+ }
+ return false;
+}
+
+bool ShaderProgram::DrawTiled() {
+ // Target coordinate step size
+ float s[8];
+ float t[8];
+
+ // Step sizes
+ const float xs = 1.0f / static_cast<float>(tile_x_count_);
+ const float ys = 1.0f / static_cast<float>(tile_y_count_);
+
+ // Tile drawing loop
+ for (int i = 0; i < tile_x_count_; ++i) {
+ for (int j = 0; j < tile_y_count_; ++j) {
+ // Current coordinates in unit rectangle
+ const float x = i / static_cast<float>(tile_x_count_);
+ const float y = j / static_cast<float>(tile_y_count_);
+
+ // Source coords
+ GetTileCoords(source_coords_, x, y, &s[0], &s[1]);
+ GetTileCoords(source_coords_, x + xs, y, &s[2], &s[3]);
+ GetTileCoords(source_coords_, x, y + ys, &s[4], &s[5]);
+ GetTileCoords(source_coords_, x + xs, y + ys, &s[6], &s[7]);
+
+ // Target coords
+ GetTileCoords(target_coords_, x, y, &t[0], &t[1]);
+ GetTileCoords(target_coords_, x + xs, y, &t[2], &t[3]);
+ GetTileCoords(target_coords_, x, y + ys, &t[4], &t[5]);
+ GetTileCoords(target_coords_, x + xs, y + ys, &t[6], &t[7]);
+
+ if (PushSourceCoords(s) && PushTargetCoords(t)) {
+ glDrawArrays(draw_mode_, 0, vertex_count_);
+ Yield();
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void ShaderProgram::Yield() {
+ glFinish();
+}
+
+bool ShaderProgram::BeginDraw() {
+ // Activate shader program
+ if (!UseProgram())
+ return false;
+
+ // Push vertex attributes
+ PushAttributes();
+
+ // Clear output, if requested
+ if (clears_) {
+ glClearColor(clear_color_.red,
+ clear_color_.green,
+ clear_color_.blue,
+ clear_color_.alpha);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+
+ // Enable/Disable blending
+ if (blending_) {
+ glEnable(GL_BLEND);
+ glBlendFunc(sfactor_, dfactor_);
+ } else glDisable(GL_BLEND);
+
+ return true;
+}
+
+int ShaderProgram::MaxVaryingCount() {
+ GLint result;
+ glGetIntegerv(GL_MAX_VARYING_VECTORS, &result);
+ return result;
+}
+
+int ShaderProgram::MaxTextureUnits() {
+ return GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1;
+}
+
+void ShaderProgram::SetDrawMode(GLenum mode) {
+ draw_mode_ = mode;
+}
+
+void ShaderProgram::SetClearsOutput(bool clears) {
+ clears_ = clears;
+}
+
+void ShaderProgram::SetClearColor(float red, float green, float blue, float alpha) {
+ clear_color_.red = red;
+ clear_color_.green = green;
+ clear_color_.blue = blue;
+ clear_color_.alpha = alpha;
+}
+
+void ShaderProgram::SetTileCounts(int x_count, int y_count) {
+ tile_x_count_ = x_count;
+ tile_y_count_ = y_count;
+}
+
+// Variable Value Setting Helpers //////////////////////////////////////////////
+bool ShaderProgram::CheckValueCount(const std::string& var_type,
+ const std::string& var_name,
+ int expected_count,
+ int components,
+ int value_size) {
+ if (expected_count != (value_size / components)) {
+ ALOGE("Shader Program: %s Value Error (%s): Expected value length %d "
+ "(%d components), but received length of %d (%d components)!",
+ var_type.c_str(), var_name.c_str(),
+ expected_count, components * expected_count,
+ value_size / components, value_size);
+ return false;
+ }
+ return true;
+}
+
+bool ShaderProgram::CheckValueMult(const std::string& var_type,
+ const std::string& var_name,
+ int components,
+ int value_size) {
+ if (value_size % components != 0) {
+ ALOGE("Shader Program: %s Value Error (%s): Value must be multiple of %d, "
+ "but %d elements were passed!", var_type.c_str(), var_name.c_str(),
+ components, value_size);
+ return false;
+ }
+ return true;
+}
+
+bool ShaderProgram::CheckVarValid(ProgramVar var) {
+ if (!IsVarValid(var)) {
+ ALOGE("Shader Program: Attempting to access invalid variable!");
+ return false;
+ }
+ return true;
+}
+
+// Uniforms ////////////////////////////////////////////////////////////////////
+bool ShaderProgram::CheckUniformValid(ProgramVar var) {
+ if (!IsVarValid(var) || uniform_indices_.find(var) == uniform_indices_.end()) {
+ ALOGE("Shader Program: Attempting to access unknown uniform %d!", var);
+ return false;
+ }
+ return true;
+}
+
+int ShaderProgram::MaxUniformCount() {
+ // Here we return the minimum of the max uniform count for fragment and vertex
+ // shaders.
+ GLint count1, count2;
+ glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &count1);
+ glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &count2);
+ return count1 < count2 ? count1 : count2;
+}
+
+ProgramVar ShaderProgram::GetUniform(const std::string& name) const {
+ if (!IsExecutable()) {
+ ALOGE("ShaderProgram: Error: Must link program before querying uniforms!");
+ return -1;
+ }
+ return glGetUniformLocation(program_, name.c_str());
+}
+
+bool ShaderProgram::SetUniformValue(ProgramVar var, int value) {
+ if (!CheckVarValid(var))
+ return false;
+
+ // Uniforms are local to the currently used program.
+ if (UseProgram()) {
+ glUniform1i(var, value);
+ return !GLEnv::CheckGLError("Set Uniform Value (int)");
+ }
+ return false;
+}
+
+bool ShaderProgram::SetUniformValue(ProgramVar var, float value) {
+ if (!CheckVarValid(var))
+ return false;
+
+ // Uniforms are local to the currently used program.
+ if (UseProgram()) {
+ glUniform1f(var, value);
+ return !GLEnv::CheckGLError("Set Uniform Value (float)");
+ }
+ return false;
+}
+
+bool ShaderProgram::SetUniformValue(ProgramVar var,
+ const int* values,
+ int count) {
+ if (!CheckUniformValid(var))
+ return false;
+
+ // Make sure we have values at all
+ if (count == 0)
+ return false;
+
+ // Uniforms are local to the currently used program.
+ if (UseProgram()) {
+ // Get uniform information
+ GLint capacity;
+ GLenum type;
+ char name[128];
+ glGetActiveUniform(program_, IndexOfUniform(var), 128, NULL, &capacity, &type, name);
+
+ // Make sure passed values are compatible
+ const int components = GLEnv::NumberOfComponents(type);
+ if (!CheckValueCount("Uniform (int)", name, capacity, components, count)
+ || !CheckValueMult ("Uniform (int)", name, components, count))
+ return false;
+
+ // Set value based on type
+ const int n = count / components;
+ switch(type) {
+ case GL_INT:
+ glUniform1iv(var, n, values);
+ break;
+
+ case GL_INT_VEC2:
+ glUniform2iv(var, n, values);
+ break;
+
+ case GL_INT_VEC3:
+ glUniform3iv(var, n, values);
+ break;
+
+ case GL_INT_VEC4:
+ glUniform4iv(var, n, values);
+ break;
+
+ default:
+ return false;
+ };
+ return !GLEnv::CheckGLError("Set Uniform Value");
+ }
+ return false;
+}
+
+bool ShaderProgram::SetUniformValue(ProgramVar var,
+ const float* values,
+ int count) {
+ if (!CheckUniformValid(var))
+ return false;
+
+ // Make sure we have values at all
+ if (count == 0)
+ return false;
+
+ // Uniforms are local to the currently used program.
+ if (UseProgram()) {
+ // Get uniform information
+ GLint capacity;
+ GLenum type;
+ char name[128];
+ glGetActiveUniform(program_, IndexOfUniform(var), 128, NULL, &capacity, &type, name);
+
+ // Make sure passed values are compatible
+ const int components = GLEnv::NumberOfComponents(type);
+ if (!CheckValueCount("Uniform (float)", name, capacity, components, count)
+ || !CheckValueMult ("Uniform (float)", name, components, count))
+ return false;
+
+ // Set value based on type
+ const int n = count / components;
+ switch(type) {
+ case GL_FLOAT:
+ glUniform1fv(var, n, values);
+ break;
+
+ case GL_FLOAT_VEC2:
+ glUniform2fv(var, n, values);
+ break;
+
+ case GL_FLOAT_VEC3:
+ glUniform3fv(var, n, values);
+ break;
+
+ case GL_FLOAT_VEC4:
+ glUniform4fv(var, n, values);
+ break;
+
+ case GL_FLOAT_MAT2:
+ glUniformMatrix2fv(var, n, GL_FALSE, values);
+ break;
+
+ case GL_FLOAT_MAT3:
+ glUniformMatrix3fv(var, n, GL_FALSE, values);
+ break;
+
+ case GL_FLOAT_MAT4:
+ glUniformMatrix4fv(var, n, GL_FALSE, values);
+ break;
+
+ default:
+ return false;
+ };
+ return !GLEnv::CheckGLError("Set Uniform Value");
+ }
+ return false;
+}
+
+bool ShaderProgram::SetUniformValue(ProgramVar var, const std::vector<int>& values) {
+ return SetUniformValue(var, &values[0], values.size());
+}
+
+bool ShaderProgram::SetUniformValue(ProgramVar var,
+ const std::vector<float>& values) {
+ return SetUniformValue(var, &values[0], values.size());
+}
+
+bool ShaderProgram::SetUniformValue(const std::string& name, const Value& value) {
+ if (ValueIsFloat(value))
+ return SetUniformValue(GetUniform(name), GetFloatValue(value));
+ else if (ValueIsInt(value))
+ return SetUniformValue(GetUniform(name), GetIntValue(value));
+ else if (ValueIsFloatArray(value))
+ return SetUniformValue(GetUniform(name), GetFloatArrayValue(value), GetValueCount(value));
+ else if (ValueIsIntArray(value))
+ return SetUniformValue(GetUniform(name), GetIntArrayValue(value), GetValueCount(value));
+ else
+ return false;
+}
+
+Value ShaderProgram::GetUniformValue(const std::string& name) {
+ ProgramVar var = GetUniform(name);
+ if (CheckUniformValid(var)) {
+ // Get uniform information
+ GLint capacity;
+ GLenum type;
+ glGetActiveUniform(program_, IndexOfUniform(var), 0, NULL, &capacity, &type, NULL);
+ if (!GLEnv::CheckGLError("Get Active Uniform")) {
+ // Get value based on type, and wrap in value object
+ switch(type) {
+ case GL_INT: {
+ int value;
+ glGetUniformiv(program_, var, &value);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeIntValue(value)
+ : MakeNullValue();
+ } break;
+
+ case GL_INT_VEC2: {
+ int value[2];
+ glGetUniformiv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeIntArrayValue(value, 2)
+ : MakeNullValue();
+ } break;
+
+ case GL_INT_VEC3: {
+ int value[3];
+ glGetUniformiv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeIntArrayValue(value, 3)
+ : MakeNullValue();
+ } break;
+
+ case GL_INT_VEC4: {
+ int value[4];
+ glGetUniformiv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeIntArrayValue(value, 4)
+ : MakeNullValue();
+ } break;
+
+ case GL_FLOAT: {
+ float value;
+ glGetUniformfv(program_, var, &value);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeFloatValue(value)
+ : MakeNullValue();
+ } break;
+
+ case GL_FLOAT_VEC2: {
+ float value[2];
+ glGetUniformfv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeFloatArrayValue(value, 2)
+ : MakeNullValue();
+ } break;
+
+ case GL_FLOAT_VEC3: {
+ float value[3];
+ glGetUniformfv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeFloatArrayValue(value, 3)
+ : MakeNullValue();
+ } break;
+
+ case GL_FLOAT_VEC4: {
+ float value[4];
+ glGetUniformfv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeFloatArrayValue(value, 4)
+ : MakeNullValue();
+ } break;
+
+ case GL_FLOAT_MAT2: {
+ float value[4];
+ glGetUniformfv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeFloatArrayValue(value, 4)
+ : MakeNullValue();
+ } break;
+
+ case GL_FLOAT_MAT3: {
+ float value[9];
+ glGetUniformfv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeFloatArrayValue(value, 9)
+ : MakeNullValue();
+ } break;
+
+ case GL_FLOAT_MAT4: {
+ float value[16];
+ glGetUniformfv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeFloatArrayValue(value, 16)
+ : MakeNullValue();
+ } break;
+ }
+ }
+ }
+ return MakeNullValue();
+}
+
+GLuint ShaderProgram::IndexOfUniform(ProgramVar var) {
+ return uniform_indices_[var];
+}
+
+// Attributes //////////////////////////////////////////////////////////////////////////////////////
+int ShaderProgram::MaxAttributeCount() {
+ GLint result;
+ glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &result);
+ return result;
+}
+
+ProgramVar ShaderProgram::GetAttribute(const std::string& name) const {
+ if (!IsExecutable()) {
+ ALOGE("ShaderProgram: Error: Must link program before querying attributes!");
+ return -1;
+ } else if (name == PositionAttributeName() || name == TexCoordAttributeName()) {
+ ALOGW("ShaderProgram: Attempting to overwrite internal vertex attribute '%s'!", name.c_str());
+ }
+ return glGetAttribLocation(program_, name.c_str());
+}
+
+bool ShaderProgram::SetAttributeValues(ProgramVar var,
+ const VertexFrame* vbo,
+ GLenum type,
+ int components,
+ int stride,
+ int offset,
+ bool normalize) {
+ if (!CheckVarValid(var))
+ return false;
+
+ if (vbo) {
+ VertexAttrib attrib;
+ attrib.is_const = false;
+ attrib.index = var;
+ attrib.components = components;
+ attrib.normalized = normalize;
+ attrib.stride = stride;
+ attrib.type = type;
+ attrib.vbo = vbo->GetVboId();
+ attrib.offset = offset;
+
+ return StoreAttribute(attrib);
+ }
+ return false;
+}
+
+bool ShaderProgram::SetAttributeValues(ProgramVar var,
+ const uint8_t* data,
+ GLenum type,
+ int components,
+ int stride,
+ int offset,
+ bool normalize) {
+ if (!CheckVarValid(var))
+ return false;
+
+ if (data) {
+ VertexAttrib attrib;
+ attrib.is_const = false;
+ attrib.index = var;
+ attrib.components = components;
+ attrib.normalized = normalize;
+ attrib.stride = stride;
+ attrib.type = type;
+ attrib.values = data + offset;
+
+ return StoreAttribute(attrib);
+ }
+ return false;
+}
+
+bool ShaderProgram::SetAttributeValues(ProgramVar var,
+ const std::vector<float>& data,
+ int components) {
+ return SetAttributeValues(var, &data[0], data.size(), components);
+}
+
+bool ShaderProgram::SetAttributeValues(ProgramVar var,
+ const float* data,
+ int total,
+ int components) {
+ if (!CheckVarValid(var))
+ return false;
+
+ // Make sure the passed data vector has a valid size
+ if (total % components != 0) {
+ ALOGE("ShaderProgram: Invalid attribute vector given! Specified a component "
+ "count of %d, but passed a non-multiple vector of size %d!",
+ components, total);
+ return false;
+ }
+
+ // Copy the data to a buffer we own
+ float* data_cpy = new float[total];
+ memcpy(data_cpy, data, sizeof(float) * total);
+
+ // Store the attribute
+ VertexAttrib attrib;
+ attrib.is_const = false;
+ attrib.index = var;
+ attrib.components = components;
+ attrib.normalized = false;
+ attrib.stride = components * sizeof(float);
+ attrib.type = GL_FLOAT;
+ attrib.values = data_cpy;
+ attrib.owned_data = data_cpy; // Marks this for deletion later on
+
+ return StoreAttribute(attrib);
+}
+
+bool ShaderProgram::StoreAttribute(VertexAttrib attrib) {
+ if (attrib.index >= 0) {
+ attrib_values_[attrib.index] = attrib;
+ return true;
+ }
+ return false;
+}
+
+bool ShaderProgram::PushAttributes() {
+ for (VertexAttribMap::const_iterator iter = attrib_values_.begin();
+ iter != attrib_values_.end();
+ ++iter) {
+ const VertexAttrib& attrib = iter->second;
+
+ if (attrib.is_const) {
+ // Set constant attribute values (must be specified as host values)
+ if (!attrib.values)
+ return false;
+
+ const float* values = reinterpret_cast<const float*>(attrib.values);
+ switch (attrib.components) {
+ case 1: glVertexAttrib1fv(attrib.index, values); break;
+ case 2: glVertexAttrib2fv(attrib.index, values); break;
+ case 3: glVertexAttrib3fv(attrib.index, values); break;
+ case 4: glVertexAttrib4fv(attrib.index, values); break;
+ default: return false;
+ }
+ glDisableVertexAttribArray(attrib.index);
+ } else {
+ // Set per-vertex values
+ if (attrib.values) {
+ // Make sure no buffer is bound and set attribute
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glVertexAttribPointer(attrib.index,
+ attrib.components,
+ attrib.type,
+ attrib.normalized,
+ attrib.stride,
+ attrib.values);
+ } else if (attrib.vbo) {
+ // Bind VBO and set attribute
+ glBindBuffer(GL_ARRAY_BUFFER, attrib.vbo);
+
+ glVertexAttribPointer(attrib.index,
+ attrib.components,
+ attrib.type,
+ attrib.normalized,
+ attrib.stride,
+ reinterpret_cast<const void*>(attrib.offset));
+ } else {
+ return false;
+ }
+ glEnableVertexAttribArray(attrib.index);
+ }
+
+ // Make sure everything worked
+ if (GLEnv::CheckGLError("Pushing Vertex Attributes"))
+ return false;
+ }
+
+ return true;
+}
+
+bool ShaderProgram::PopAttributes() {
+ // Disable vertex attributes
+ for (VertexAttribMap::const_iterator iter = attrib_values_.begin();
+ iter != attrib_values_.end();
+ ++iter) {
+ glDisableVertexAttribArray(iter->second.index);
+ }
+ // Unbind buffer: Very important as this greatly affects what glVertexAttribPointer does!
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ return !GLEnv::CheckGLError("Popping Vertex Attributes");
+}
+
+void ShaderProgram::SetVertexCount(int count) {
+ vertex_count_ = count;
+}
+
+} // namespace filterfw
+} // namespace android