| /* |
| * 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 { |
| |
| 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]; |
| } |
| |
| // 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); |
| size_t cur_pos = 0; |
| size_t next_pos = 0; |
| size_t line_number = 1; |
| while ( (next_pos = src.find_first_of('\n', cur_pos)) != std::string::npos) { |
| ALOGE("%03zd : %s", line_number, src.substr(cur_pos, next_pos-cur_pos).c_str()); |
| cur_pos = next_pos + 1; |
| line_number++; |
| } |
| ALOGE("%03zu : %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 %zd 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 |