| /* |
| * Copyright 2023 The Cobalt Authors. All Rights Reserved. |
| * Copyright 2015 Google Inc. All Rights Reserved. |
| * |
| * 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 "glimp/gles/program.h" |
| |
| #include <utility> |
| |
| #include "starboard/common/string.h" |
| #include "starboard/memory.h" |
| |
| namespace glimp { |
| namespace gles { |
| |
| Program::Program(std::unique_ptr<ProgramImpl> impl) |
| : impl_(std::move(impl)), link_results_(false) {} |
| |
| bool Program::AttachShader(const nb::scoped_refptr<Shader>& shader) { |
| if (shader->type() == GL_VERTEX_SHADER) { |
| if (vertex_shader_) { |
| return false; |
| } |
| vertex_shader_ = shader; |
| } else if (shader->type() == GL_FRAGMENT_SHADER) { |
| if (fragment_shader_) { |
| return false; |
| } |
| fragment_shader_ = shader; |
| } else { |
| SB_DLOG(FATAL) << "Invalid shader type."; |
| } |
| |
| return true; |
| } |
| |
| void Program::Link() { |
| if (!vertex_shader_ || !fragment_shader_) { |
| // We cannot successfully link if both a vertex and fragment shader |
| // have not yet been attached. |
| link_results_ = ProgramImpl::LinkResults( |
| false, "A fragment or vertex shader is not attached."); |
| return; |
| } |
| |
| link_results_ = impl_->Link(vertex_shader_, fragment_shader_); |
| if (link_results_.success) { |
| linked_vertex_shader_ = vertex_shader_; |
| linked_fragment_shader_ = fragment_shader_; |
| |
| ClearUniforms(); |
| |
| // Re-issue any binding attributes that are defined for this program. |
| for (BoundAttributes::const_iterator iter = bound_attrib_locations_.begin(); |
| iter != bound_attrib_locations_.end(); ++iter) { |
| impl_->BindAttribLocation(iter->first, iter->second.c_str()); |
| } |
| } |
| } |
| |
| void Program::BindAttribLocation(GLuint index, const GLchar* name) { |
| bound_attrib_locations_[index] = std::string(name); |
| |
| if (linked()) { |
| // If we are linked, then immediately pass this new binding information |
| // on to the platform-specific implementation. Otherwise, this information |
| // will all be communicated upon linking. |
| impl_->BindAttribLocation(index, name); |
| } |
| } |
| |
| GLenum Program::GetProgramiv(GLenum pname, GLint* params) { |
| switch (pname) { |
| case GL_LINK_STATUS: |
| *params = (link_results_.success ? 1 : 0); |
| break; |
| case GL_INFO_LOG_LENGTH: |
| *params = link_results_.info_log.size(); |
| break; |
| case GL_DELETE_STATUS: |
| case GL_VALIDATE_STATUS: |
| case GL_ATTACHED_SHADERS: |
| case GL_ACTIVE_ATTRIBUTES: |
| case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: |
| case GL_ACTIVE_UNIFORMS: |
| case GL_ACTIVE_UNIFORM_MAX_LENGTH: |
| SB_NOTIMPLEMENTED(); |
| break; |
| default: |
| return GL_INVALID_ENUM; |
| } |
| |
| return GL_NO_ERROR; |
| } |
| |
| void Program::GetProgramInfoLog(GLsizei bufsize, |
| GLsizei* length, |
| GLchar* infolog) { |
| *length = |
| starboard::strlcpy(infolog, link_results_.info_log.c_str(), bufsize); |
| } |
| |
| GLint Program::GetUniformLocation(const GLchar* name) { |
| SB_DCHECK(linked()); |
| int location = impl_->GetUniformLocation(name); |
| if (location != -1) { |
| if (std::find(active_uniform_locations_.begin(), |
| active_uniform_locations_.end(), |
| location) == active_uniform_locations_.end()) { |
| active_uniform_locations_.push_back(location); |
| } |
| } |
| return location; |
| } |
| |
| GLenum Program::Uniformiv(GLint location, |
| GLsizei count, |
| GLsizei elem_size, |
| const GLint* v) { |
| return UpdateUniform(location, count, elem_size, v, |
| UniformInfo::kTypeInteger); |
| } |
| |
| GLenum Program::Uniformfv(GLint location, |
| GLsizei count, |
| GLsizei elem_size, |
| const GLfloat* v) { |
| return UpdateUniform(location, count, elem_size, v, UniformInfo::kTypeFloat); |
| } |
| |
| GLenum Program::UniformMatrixfv(GLint location, |
| GLsizei count, |
| GLsizei dim_size, |
| const GLfloat* value) { |
| return UpdateUniform(location, count, dim_size, value, |
| UniformInfo::kTypeMatrix); |
| } |
| |
| Program::Uniform* Program::FindOrMakeUniform(int location) { |
| if (std::find(active_uniform_locations_.begin(), |
| active_uniform_locations_.end(), |
| location) == active_uniform_locations_.end()) { |
| return NULL; |
| } |
| |
| for (size_t i = 0; i < uniforms_.size(); ++i) { |
| if (uniforms_[i].location == location) { |
| return &uniforms_[i]; |
| } |
| } |
| uniforms_.push_back(Uniform()); |
| uniforms_.back().location = location; |
| return &uniforms_.back(); |
| } |
| |
| // Clear all stored uniform information and values. |
| void Program::ClearUniforms() { |
| for (size_t i = 0; i < uniforms_.size(); ++i) { |
| free(uniforms_[i].data); |
| } |
| uniforms_.clear(); |
| active_uniform_locations_.clear(); |
| } |
| |
| namespace { |
| int DataSizeForType(GLsizei count, GLsizei elem_size, UniformInfo::Type type) { |
| switch (type) { |
| case UniformInfo::kTypeInteger: |
| return sizeof(int) * count * elem_size; |
| case UniformInfo::kTypeFloat: |
| return sizeof(float) * count * elem_size; |
| case UniformInfo::kTypeMatrix: |
| return sizeof(float) * count * elem_size * elem_size; |
| default: |
| SB_NOTREACHED(); |
| return NULL; |
| } |
| } |
| } // namespace |
| |
| // Assign the specified data to the specified uniform, so that it is available |
| // to the next draw call. |
| GLenum Program::UpdateUniform(GLint location, |
| GLsizei count, |
| GLsizei elem_size, |
| const void* v, |
| UniformInfo::Type type) { |
| // TODO: It would be nice to be able to query the ProgramImpl object for |
| // UniformInfo information so that we can check it against incoming |
| // glUniform() calls to ensure consistency. As it is currently, we are |
| // defining this information through these glUniform() calls. |
| Uniform* uniform = FindOrMakeUniform(location); |
| if (uniform == NULL) { |
| return GL_INVALID_OPERATION; |
| } |
| |
| UniformInfo new_info = UniformInfo(type, count, elem_size); |
| if (new_info != uniform->info) { |
| // We need to reallocate data if the information has changed. |
| uniform->info = new_info; |
| |
| free(uniform->data); |
| uniform->data = malloc(DataSizeForType(count, elem_size, type)); |
| } |
| memcpy(uniform->data, v, DataSizeForType(count, elem_size, type)); |
| |
| return GL_NO_ERROR; |
| } |
| |
| } // namespace gles |
| } // namespace glimp |