Chromium Code Reviews| Index: gpu/command_buffer/service/program_manager.cc |
| diff --git a/gpu/command_buffer/service/program_manager.cc b/gpu/command_buffer/service/program_manager.cc |
| index bd7a8802def3fa06b2b1025fba46d5699b0740bd..025b4db6363739fc2967a861259f90b441524103 100644 |
| --- a/gpu/command_buffer/service/program_manager.cc |
| +++ b/gpu/command_buffer/service/program_manager.cc |
| @@ -26,6 +26,7 @@ |
| #include "gpu/command_buffer/service/program_cache.h" |
| #include "gpu/command_buffer/service/shader_manager.h" |
| #include "third_party/re2/re2/re2.h" |
| +#include "ui/gl/gl_version_info.h" |
| using base::TimeDelta; |
| using base::TimeTicks; |
| @@ -241,10 +242,9 @@ Program::UniformInfo::UniformInfo(GLsizei _size, |
| Program::UniformInfo::~UniformInfo() {} |
| -bool ProgramManager::IsInvalidPrefix(const char* name, size_t length) { |
| - static const char kInvalidPrefix[] = { 'g', 'l', '_' }; |
| - return (length >= sizeof(kInvalidPrefix) && |
| - memcmp(name, kInvalidPrefix, sizeof(kInvalidPrefix)) == 0); |
| +bool ProgramManager::HasBuiltInPrefix(const std::string& name) { |
| + return name.length() >= 3 && name[0] == 'g' && name[1] == 'l' && |
| + name[2] == '_'; |
| } |
| Program::Program(ProgramManager* manager, GLuint service_id) |
| @@ -271,6 +271,7 @@ void Program::Reset() { |
| attrib_infos_.clear(); |
| uniform_infos_.clear(); |
| fragment_input_infos_.clear(); |
| + program_output_infos_.clear(); |
| sampler_indices_.clear(); |
| attrib_location_to_index_map_.clear(); |
| } |
| @@ -446,13 +447,15 @@ void Program::ClearUniforms( |
| namespace { |
| struct VariableInfoData { |
| - VariableInfoData() : size(-1), type(GL_NONE), location(0), added(false) {} |
| + VariableInfoData() |
| + : size(-1), type(GL_NONE), location(0), index(-1), added(false) {} |
| std::string queried_name; |
| std::string corrected_name; |
| std::string original_name; |
| GLsizei size; |
| GLenum type; |
| GLint location; |
| + GLint index; |
|
Kimmo Kinnunen
2015/10/08 13:18:12
removed also this stale hunk.
|
| bool added; |
| }; |
| @@ -551,8 +554,7 @@ void Program::Update() { |
| for (size_t ii = 0; ii < uniform_data.size(); ++ii) { |
| VariableInfoData& data = uniform_data[ii]; |
| // Force builtin uniforms (gl_DepthRange) to have invalid location. |
| - if (ProgramManager::IsInvalidPrefix(data.queried_name.c_str(), |
| - data.queried_name.size())) { |
| + if (ProgramManager::HasBuiltInPrefix(data.queried_name)) { |
| data.location = -1; |
| } else { |
| VariableInfoData& data = uniform_data[ii]; |
| @@ -608,7 +610,10 @@ void Program::Update() { |
| // gl_PointCoord built-ins as its input, as well as custom |
| // varyings. We are interested only in custom varyings, |
| // client is allowed to bind only them. |
| - if (!ProgramManager::IsInvalidPrefix(name_buffer.get(), length)) { |
| + std::string queried_name(name_buffer.get(), length); |
| + if (ProgramManager::HasBuiltInPrefix(queried_name)) { |
| + continue; |
| + } |
| length = 0; |
|
Zhenyao Mo
2015/09/30 00:23:48
indentation wrong
Kimmo Kinnunen
2015/10/08 13:18:12
Done.
|
| GLenum props[] = {GL_LOCATION, GL_TYPE, GL_ARRAY_SIZE}; |
| GLint values[arraysize(props)] = { |
| @@ -622,13 +627,12 @@ void Program::Update() { |
| data.location = values[0]; |
| data.type = values[1]; |
| data.size = values[2]; |
| - data.queried_name = std::string(name_buffer.get()); |
| - GetCorrectedVariableData(kVaryingVariableInfo, data.queried_name, |
| + data.queried_name = queried_name; |
| + GetCorrectedVariableData(kFragmentInputVariableInfo, data.queried_name, |
| &data.corrected_name, &data.original_name, |
| &data.size, &data.type); |
| fragment_input_data.push_back(data); |
| - } |
| } |
| next_available_index = 0; |
| @@ -656,6 +660,76 @@ void Program::Update() { |
| } |
| } |
| + if (feature_info().feature_flags().ext_blend_func_extended) { |
| + // Query the fragment output variables from the driver. We expose |
|
Kimmo Kinnunen
2015/10/08 13:18:12
I ended up removing this program interface query u
|
| + // ext_blend_func_extended only |
| + // if service context supports program interface query. |
| + GLint num_fragment_outputs = 0; |
| + max_len = 0; |
| + glGetProgramInterfaceiv(service_id_, GL_PROGRAM_OUTPUT, GL_ACTIVE_RESOURCES, |
| + &num_fragment_outputs); |
| + |
| + glGetProgramInterfaceiv(service_id_, GL_PROGRAM_OUTPUT, GL_MAX_NAME_LENGTH, |
| + &max_len); |
| + DCHECK(num_fragment_outputs <= 0 || max_len > 0); |
| + name_buffer.reset(new char[max_len]); |
| + |
| + for (GLint ii = 0; ii < num_fragment_outputs; ++ii) { |
| + GLsizei length = 0; |
| + glGetProgramResourceName(service_id_, GL_PROGRAM_OUTPUT, ii, max_len, |
| + &length, name_buffer.get()); |
| + DCHECK(length < max_len); |
| + DCHECK(length == 0 || name_buffer[length] == '\0'); |
| + // A fragment shader can have gl_FragColor, gl_SecondaryFragColor, etc |
| + // built-ins as its output, |
| + // as well as custom varyings. We are interested only in custom varyings, |
| + // client is allowed to |
| + // bind only them. |
| + std::string queried_name(name_buffer.get(), length); |
| + if (ProgramManager::HasBuiltInPrefix(queried_name)) { |
| + continue; |
| + } |
| + length = 0; |
| + GLenum props[] = {GL_LOCATION, GL_LOCATION_INDEX, GL_TYPE, GL_ARRAY_SIZE}; |
| + GLint values[arraysize(props)] = { |
| + 0, |
| + }; |
| + glGetProgramResourceiv(service_id_, GL_PROGRAM_OUTPUT, ii, |
| + arraysize(props), props, arraysize(values), |
| + &length, values); |
| + DCHECK(length == arraysize(props)); |
| + |
| + const GLint& colorName = values[0]; |
| + const GLint& index = values[1]; |
| + GLenum type = values[2]; |
| + GLsizei size = values[3]; |
| + std::string original_name; |
| + std::string corrected_name; |
| + GetCorrectedVariableData(kProgramOutputVariableInfo, queried_name, |
| + &corrected_name, &original_name, &size, &type); |
| + program_output_infos_.push_back( |
| + ProgramOutputInfo(size, type, colorName, index, original_name)); |
| + } |
| + } else if (feature_info().gl_version_info().IsES3Capable() && |
| + !feature_info().disable_shader_translator()) { |
| + Shader* fragment_shader = |
| + attached_shaders_[ShaderTypeToIndex(GL_FRAGMENT_SHADER)].get(); |
| + if (fragment_shader && fragment_shader->valid()) { |
| + for (auto const& output_var : fragment_shader->output_variable_list()) { |
| + const std::string& name = output_var.mappedName; |
|
Kimmo Kinnunen
2015/10/08 13:18:12
So the code would always still have the translator
|
| + GLenum type = output_var.type; |
| + GLsizei size = output_var.arraySize; |
| + std::string original_name; |
| + std::string corrected_name; |
| + GetCorrectedVariableData(kProgramOutputVariableInfo, name, |
| + &corrected_name, &original_name, &size, &type); |
| + GLint colorName = glGetFragDataLocation(service_id_, name.c_str()); |
| + program_output_infos_.push_back( |
| + ProgramOutputInfo(size, type, colorName, 0, original_name)); |
| + } |
| + } |
| + } |
| + |
| #if !defined(NDEBUG) |
| if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kEnableGPUServiceLoggingGPU)) { |
| @@ -714,6 +788,83 @@ bool Program::ExecuteTransformFeedbackVaryingsCall() { |
| return true; |
| } |
| +void Program::ExecuteProgramOutputBindCalls() { |
| + Shader* fragment_shader = |
| + attached_shaders_[ShaderTypeToIndex(GL_FRAGMENT_SHADER)].get(); |
| + if (!fragment_shader || !fragment_shader->valid()) { |
| + return; |
| + } |
| + |
| + if (fragment_shader->shader_version() != 100) { |
| + // ES SL 1.00 does not have mechanism for introducing variables that could |
| + // be bound. This means |
| + // that ES SL 1.00 binding calls would be to non-existing variable names. |
| + // Binding calls are only |
| + // executed with ES SL 3.00 and higher. |
| + |
| + for (const auto& key_value : bind_program_output_location_index_map_) { |
| + const std::string* mapped_name = |
| + fragment_shader->GetVaryingMappedName(key_value.first); |
| + if (mapped_name) { |
| + const auto& binding = key_value.second; |
|
Zhenyao Mo
2015/09/30 00:23:48
It's worth adding a comment here that glBindFragDa
Kimmo Kinnunen
2015/10/08 13:18:12
Done.
|
| + if (binding.second == 0) { |
| + glBindFragDataLocation(service_id_, binding.first, |
| + mapped_name->c_str()); |
| + } else { |
| + DCHECK(feature_info().feature_flags().ext_blend_func_extended); |
| + glBindFragDataLocationIndexed(service_id_, binding.first, |
| + binding.second, mapped_name->c_str()); |
| + } |
| + } |
| + } |
| + return; |
| + } |
| + |
| + // Support for EXT_blend_func_extended when used with ES SL 1.00 client |
| + // shader. |
| + |
| + if (feature_info().gl_version_info().is_es || |
| + !feature_info().feature_flags().ext_blend_func_extended) { |
| + return; |
| + } |
| + // The underlying context does not support EXT_blend_func_extended |
| + // natively, need to emulate it. |
| + |
| + // Translator is needed for the emulation. Should be handled already by the |
| + // FeatureInfo. |
| + DCHECK(!feature_info().disable_shader_translator()); |
| + |
| + // ES SL 1.00 is the only language which contains GLSL built-ins |
| + // that need to be bound to color indices. If clients use other |
| + // languages, they also bind the output variables themselves. |
| + // Map gl_SecondaryFragColorEXT / gl_SecondaryFragDataEXT of |
| + // EXT_blend_func_extended to real color indexes. |
| + for (auto const& output_var : fragment_shader->output_variable_list()) { |
| + const std::string& name = output_var.mappedName; |
| + if (name == "gl_FragColor") { |
| + DCHECK_EQ(-1, output_var.location); |
| + DCHECK_EQ(0u, output_var.arraySize); |
| + // We leave these unbound by not giving a binding name. The driver will |
| + // bind this. |
| + } else if (name == "gl_FragData") { |
| + DCHECK_EQ(-1, output_var.location); |
| + DCHECK_NE(0u, output_var.arraySize); |
| + // We leave these unbound by not giving a binding name. The driver will |
| + // bind this. |
| + } else if (name == "gl_SecondaryFragColorEXT") { |
| + DCHECK_EQ(-1, output_var.location); |
| + DCHECK_EQ(0u, output_var.arraySize); |
| + glBindFragDataLocationIndexed(service_id_, 0, 1, |
| + "angle_SecondaryFragColor"); |
| + } else if (name == "gl_SecondaryFragDataEXT") { |
| + DCHECK_EQ(-1, output_var.location); |
| + DCHECK_NE(0u, output_var.arraySize); |
| + glBindFragDataLocationIndexed(service_id_, 0, 1, |
| + "angle_SecondaryFragData"); |
| + } |
| + } |
| +} |
| + |
| bool Program::Link(ShaderManager* manager, |
| Program::VaryingsPackingOption varyings_packing_option, |
| const ShaderCacheCallback& shader_callback) { |
| @@ -792,6 +943,10 @@ bool Program::Link(ShaderManager* manager, |
| set_log_info("glBindFragmentInputLocationCHROMIUM() conflicts"); |
| return false; |
| } |
| + if (DetectProgramOutputLocationBindingConflicts()) { |
| + set_log_info("glBindFragDataLocation() conflicts"); |
| + return false; |
| + } |
| if (DetectBuiltInInvariantConflicts()) { |
| set_log_info("Invariant settings for certain built-in varyings " |
| "have to match"); |
| @@ -812,6 +967,9 @@ bool Program::Link(ShaderManager* manager, |
| if (!ExecuteTransformFeedbackVaryingsCall()) { |
| return false; |
| } |
| + |
| + ExecuteProgramOutputBindCalls(); |
| + |
| before_time = TimeTicks::Now(); |
| if (cache && gfx::g_driver_gl.ext.b_GL_ARB_get_program_binary) { |
| glProgramParameteri(service_id(), |
| @@ -1022,6 +1180,20 @@ bool Program::SetFragmentInputLocationBinding(const std::string& name, |
| return true; |
| } |
| +bool Program::SetProgramOutputLocationBinding(const std::string& name, |
| + GLuint colorName, |
| + GLuint index) { |
| + std::string short_name; |
| + int element_index = 0; |
| + if (!GetProgramVariableNameSansElement(name, &element_index, &short_name) || |
| + element_index != 0) { |
| + return false; |
| + } |
| + bind_program_output_location_index_map_[short_name] = |
| + std::make_pair(colorName, index); |
| + return true; |
| +} |
| + |
| // Note: This is only valid to call right after a program has been linked |
| // successfully. |
| void Program::GetCorrectedVariableData(VariableInfoType variable_info_type, |
| @@ -1031,34 +1203,47 @@ void Program::GetCorrectedVariableData(VariableInfoType variable_info_type, |
| GLsizei* size, |
| GLenum* type) const { |
| DCHECK(corrected_name && original_name && size && type); |
| - for (auto shader : attached_shaders_) { |
| - if (!shader) |
| - continue; |
| - const sh::ShaderVariable* info = NULL; |
| - bool found = false; |
| - if (variable_info_type == kUniformVariableInfo) { |
| + const sh::ShaderVariable* info = NULL; |
| + bool found = false; |
| + |
| + if (variable_info_type == kUniformVariableInfo) { |
| + for (auto shader : attached_shaders_) { |
| + if (!shader) |
| + continue; |
| const sh::Uniform* uniform = shader->GetUniformInfo(name); |
| if (uniform) |
| found = uniform->findInfoByMappedName(name, &info, original_name); |
| - } else if (variable_info_type == kVaryingVariableInfo) { |
| - const sh::Varying* varying = shader->GetVaryingInfo(name); |
| + } |
| + } else if (variable_info_type == kFragmentInputVariableInfo) { |
| + Shader* shader = |
| + attached_shaders_[ShaderTypeToIndex(GL_FRAGMENT_SHADER)].get(); |
| + if (shader) { |
| + const sh::Varying* varying = shader->GetInputVariableInfo(name); |
| if (varying) |
| found = varying->findInfoByMappedName(name, &info, original_name); |
| } |
| + } else if (variable_info_type == kProgramOutputVariableInfo) { |
| + Shader* shader = |
| + attached_shaders_[ShaderTypeToIndex(GL_FRAGMENT_SHADER)].get(); |
| + if (shader) { |
| + const sh::OutputVariable* output = shader->GetOutputVariableInfo(name); |
| + if (output) |
| + found = output->findInfoByMappedName(name, &info, original_name); |
| + } |
| + } |
| - if (found) { |
| - const std::string kArraySpec("[0]"); |
| - if (info->arraySize > 0 && |
| - !base::EndsWith(name, kArraySpec, base::CompareCase::SENSITIVE)) { |
| - *corrected_name = name + kArraySpec; |
| - *original_name += kArraySpec; |
| - } else { |
| - *corrected_name = name; |
| - } |
| - *type = info->type; |
| - *size = std::max(1u, info->arraySize); |
| - return; |
| + if (found) { |
| + const std::string kArraySpec("[0]"); |
| + if (info->arraySize > 0 && |
| + !base::EndsWith(name, kArraySpec, base::CompareCase::SENSITIVE)) { |
| + *corrected_name = name + kArraySpec; |
| + *original_name += kArraySpec; |
| + } else { |
| + *corrected_name = name; |
| } |
| + *type = info->type; |
| + *size = std::max(1u, info->arraySize); |
| + return; |
| } |
| // TODO(zmo): this path should never be reached unless there is a serious |
| // bug in the driver or in ANGLE translator. |
| @@ -1483,7 +1668,8 @@ bool Program::DetectFragmentInputLocationBindingConflicts() const { |
| if (!mapped_name) { |
| continue; |
| } |
| - const sh::Varying* fragment_input = shader->GetVaryingInfo(*mapped_name); |
| + const sh::Varying* fragment_input = |
| + shader->GetInputVariableInfo(*mapped_name); |
| if (fragment_input && fragment_input->staticUse) { |
| auto result = location_binding_used.insert(it.second); |
| if (!result.second) { |
| @@ -1494,6 +1680,38 @@ bool Program::DetectFragmentInputLocationBindingConflicts() const { |
| return false; |
| } |
| +bool Program::DetectProgramOutputLocationBindingConflicts() const { |
| + Shader* shader = |
| + attached_shaders_[ShaderTypeToIndex(GL_FRAGMENT_SHADER)].get(); |
| + if (!shader || !shader->valid()) |
| + return false; |
| + |
| + std::set<LocationIndexMap::mapped_type> location_binding_used; |
| + for (auto it : bind_program_output_location_index_map_) { |
| + // Find out if an program output is statically used in this program's |
| + // shaders. |
| + bool is_used = false; |
| + if (feature_info().disable_shader_translator()) { |
| + is_used = true; |
| + } else { |
| + const std::string* mapped_name = shader->GetVaryingMappedName(it.first); |
| + if (mapped_name) { |
| + const sh::OutputVariable* output = |
| + shader->GetOutputVariableInfo(*mapped_name); |
| + is_used = output && output->staticUse; |
| + } |
| + } |
| + if (!is_used) { |
| + continue; |
| + } |
| + auto result = location_binding_used.insert(it.second); |
| + if (!result.second) { |
| + return true; |
| + } |
| + } |
| + return false; |
| +} |
| + |
| bool Program::DetectBuiltInInvariantConflicts() const { |
| DCHECK(attached_shaders_[0].get() && |
| attached_shaders_[0]->shader_type() == GL_VERTEX_SHADER && |
| @@ -1975,6 +2193,43 @@ bool Program::GetUniformsES3(CommonDecoder::Bucket* bucket) const { |
| return true; |
| } |
| +const Program::ProgramOutputInfo* Program::GetProgramOutputInfo( |
| + const std::string& original_name) const { |
| + std::string short_name; |
| + int element_index = 0; |
| + if (!GetProgramVariableNameSansElement(original_name, &element_index, |
| + &short_name) || |
| + element_index != 0) { |
| + return nullptr; |
| + } |
| + |
| + auto iter = |
| + std::find_if(program_output_infos_.begin(), program_output_infos_.end(), |
| + [short_name](const ProgramOutputInfo& info) { |
| + return info.name == short_name; |
| + }); |
| + return iter != program_output_infos_.end() ? &(*iter) : nullptr; |
| +} |
| + |
| +GLint Program::GetFragDataLocation(const std::string& original_name) const { |
| + DCHECK(IsValid()); |
| + const ProgramOutputInfo* info = GetProgramOutputInfo(original_name); |
| + if (!info) { |
| + return -1; |
| + } |
| + return info->colorName; |
| +} |
| + |
| +GLint Program::GetFragDataIndex(const std::string& original_name) const { |
| + DCHECK(IsValid()); |
| + // return glGetFragDataIndex(service_id_, info->mappedName.c_str()); |
| + const ProgramOutputInfo* info = GetProgramOutputInfo(original_name); |
| + if (!info) { |
| + return -1; |
| + } |
| + return info->index; |
| +} |
| + |
| void Program::TransformFeedbackVaryings(GLsizei count, |
| const char* const* varyings, |
| GLenum buffer_mode) { |
| @@ -1997,11 +2252,13 @@ Program::~Program() { |
| ProgramManager::ProgramManager(ProgramCache* program_cache, |
| uint32 max_varying_vectors, |
| + uint32 max_dual_source_draw_buffers, |
| FeatureInfo* feature_info) |
| : program_count_(0), |
| have_context_(true), |
| program_cache_(program_cache), |
| max_varying_vectors_(max_varying_vectors), |
| + max_dual_source_draw_buffers_(max_dual_source_draw_buffers), |
| feature_info_(feature_info) {} |
| ProgramManager::~ProgramManager() { |