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 bfff34c15c700a0c4ca15653de32dbde108b05b7..45b0c5548a4b81468d4eee55a39b107fd19fbe77 100644 |
--- a/gpu/command_buffer/service/program_manager.cc |
+++ b/gpu/command_buffer/service/program_manager.cc |
@@ -28,6 +28,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; |
@@ -249,10 +250,9 @@ Program::UniformInfo::UniformInfo(const std::string& client_name, |
} |
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) |
@@ -279,6 +279,7 @@ void Program::Reset() { |
uniform_locations_.clear(); |
fragment_input_infos_.clear(); |
fragment_input_locations_.clear(); |
+ program_output_infos_.clear(); |
sampler_indices_.clear(); |
attrib_location_to_index_map_.clear(); |
} |
@@ -519,6 +520,7 @@ void Program::Update() { |
#endif |
UpdateFragmentInputs(); |
+ UpdateProgramOutputs(); |
valid_ = true; |
} |
@@ -563,8 +565,7 @@ void Program::UpdateUniforms() { |
GLint service_location = -1; |
// Force builtin uniforms (gl_DepthRange) to have invalid location. |
- if (!ProgramManager::IsInvalidPrefix(service_name.c_str(), |
- service_name.size())) { |
+ if (!ProgramManager::HasBuiltInPrefix(service_name)) { |
service_location = |
glGetUniformLocation(service_id_, service_name.c_str()); |
} |
@@ -701,9 +702,9 @@ void Program::UpdateFragmentInputs() { |
// A fragment shader can have gl_FragCoord, gl_FrontFacing or gl_PointCoord |
// built-ins as its input, as well as custom varyings. We are interested in |
// custom varyings, client is allowed to bind only them. |
- if (ProgramManager::IsInvalidPrefix(name_buffer.get(), name_length)) |
- continue; |
std::string service_name(name_buffer.get(), name_length); |
+ if (ProgramManager::HasBuiltInPrefix(service_name)) |
+ continue; |
// Unlike when binding uniforms, we expect the driver to give correct |
// names: "name" for simple variable, "name[0]" for an array. |
GLsizei query_length = 0; |
@@ -788,6 +789,55 @@ void Program::UpdateFragmentInputs() { |
} |
} |
+void Program::UpdateProgramOutputs() { |
+ if (!feature_info().gl_version_info().IsES3Capable() || |
+ feature_info().disable_shader_translator()) |
+ return; |
+ |
+ Shader* fragment_shader = |
+ attached_shaders_[ShaderTypeToIndex(GL_FRAGMENT_SHADER)].get(); |
+ |
+ for (auto const& output_var : fragment_shader->output_variable_list()) { |
+ const std::string& service_name = output_var.mappedName; |
+ // 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. |
+ if (ProgramManager::HasBuiltInPrefix(service_name)) |
+ continue; |
+ |
+ std::string client_name = output_var.name; |
+ if (output_var.arraySize == 0) { |
+ GLint color_name = |
+ glGetFragDataLocation(service_id_, service_name.c_str()); |
+ if (color_name < 0) |
+ continue; |
+ GLint index = 0; |
+ if (feature_info().feature_flags().ext_blend_func_extended) |
+ index = glGetFragDataIndex(service_id_, service_name.c_str()); |
+ if (index < 0) |
+ continue; |
+ program_output_infos_.push_back( |
+ ProgramOutputInfo(color_name, index, client_name)); |
+ } else { |
+ for (size_t ii = 0; ii < output_var.arraySize; ++ii) { |
+ std::string array_spec(std::string("[") + base::IntToString(ii) + "]"); |
+ std::string service_element_name(service_name + array_spec); |
+ GLint color_name = |
+ glGetFragDataLocation(service_id_, service_element_name.c_str()); |
+ if (color_name < 0) |
+ continue; |
+ GLint index = 0; |
+ if (feature_info().feature_flags().ext_blend_func_extended) |
+ index = glGetFragDataIndex(service_id_, service_element_name.c_str()); |
+ if (index < 0) |
+ continue; |
+ program_output_infos_.push_back( |
+ ProgramOutputInfo(color_name, index, client_name + array_spec)); |
+ } |
+ } |
+ } |
+} |
+ |
void Program::ExecuteBindAttribLocationCalls() { |
for (const auto& key_value : bind_attrib_location_map_) { |
const std::string* mapped_name = GetAttribMappedName(key_value.first); |
@@ -827,6 +877,96 @@ bool Program::ExecuteTransformFeedbackVaryingsCall() { |
return true; |
} |
+void Program::ExecuteProgramOutputBindCalls() { |
+ if (feature_info().disable_shader_translator()) { |
+ return; |
+ } |
+ |
+ Shader* fragment_shader = |
+ attached_shaders_[ShaderTypeToIndex(GL_FRAGMENT_SHADER)].get(); |
+ DCHECK(fragment_shader && fragment_shader->valid()); |
+ |
+ 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 (auto const& output_var : fragment_shader->output_variable_list()) { |
+ size_t count = std::max(output_var.arraySize, 1u); |
+ bool is_array = output_var.arraySize > 0; |
+ |
+ for (size_t jj = 0; jj < count; ++jj) { |
+ std::string name = output_var.name; |
+ std::string array_spec; |
+ if (is_array) { |
+ array_spec = std::string("[") + base::IntToString(jj) + "]"; |
+ name += array_spec; |
+ } |
+ auto it = bind_program_output_location_index_map_.find(name); |
+ if (it == bind_program_output_location_index_map_.end()) |
+ continue; |
+ |
+ std::string mapped_name = output_var.mappedName; |
+ if (is_array) { |
+ mapped_name += array_spec; |
+ } |
+ const auto& binding = it->second; |
+ if (binding.second == 0) { |
+ // Handles the cases where client called glBindFragDataLocation as |
+ // well as glBindFragDataLocationIndexed with index == 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. |
+ |
+ // 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) { |
@@ -905,6 +1045,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"); |
@@ -925,6 +1069,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(), |
@@ -1148,6 +1295,20 @@ void Program::SetFragmentInputLocationBinding(const std::string& name, |
bind_fragment_input_location_map_[name + "[0]"] = location; |
} |
+void Program::SetProgramOutputLocationBinding(const std::string& name, |
+ GLuint color_name) { |
+ SetProgramOutputLocationIndexedBinding(name, color_name, 0); |
+} |
+ |
+void Program::SetProgramOutputLocationIndexedBinding(const std::string& name, |
+ GLuint color_name, |
+ GLuint index) { |
+ bind_program_output_location_index_map_[name] = |
+ std::make_pair(color_name, index); |
+ bind_program_output_location_index_map_[name + "[0]"] = |
+ std::make_pair(color_name, index); |
+} |
+ |
void Program::GetVertexAttribData( |
const std::string& name, std::string* original_name, GLenum* type) const { |
DCHECK(original_name); |
@@ -1541,6 +1702,42 @@ bool Program::DetectFragmentInputLocationBindingConflicts() const { |
return false; |
} |
+bool Program::DetectProgramOutputLocationBindingConflicts() const { |
+ if (feature_info().disable_shader_translator()) { |
+ return false; |
+ } |
+ |
+ Shader* shader = |
+ attached_shaders_[ShaderTypeToIndex(GL_FRAGMENT_SHADER)].get(); |
+ DCHECK(shader && shader->valid()); |
+ |
+ if (shader->shader_version() == 100) |
+ return false; |
+ |
+ std::set<LocationIndexMap::mapped_type> location_binding_used; |
+ for (auto const& output_var : shader->output_variable_list()) { |
+ if (!output_var.staticUse) |
+ continue; |
+ |
+ size_t count = std::max(output_var.arraySize, 1u); |
+ bool is_array = output_var.arraySize > 0; |
+ |
+ for (size_t jj = 0; jj < count; ++jj) { |
+ std::string name = output_var.name; |
+ if (is_array) |
+ name += std::string("[") + base::IntToString(jj) + "]"; |
+ |
+ auto it = bind_program_output_location_index_map_.find(name); |
+ if (it == bind_program_output_location_index_map_.end()) |
+ 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 && |
@@ -2019,6 +2216,36 @@ bool Program::GetUniformsES3(CommonDecoder::Bucket* bucket) const { |
return true; |
} |
+const Program::ProgramOutputInfo* Program::GetProgramOutputInfo( |
+ const std::string& name) const { |
+ for (const auto& info : program_output_infos_) { |
+ if (info.name == name) { |
+ return &info; |
+ } |
+ } |
+ return nullptr; |
+} |
+ |
+GLint Program::GetFragDataLocation(const std::string& original_name) const { |
+ DCHECK(IsValid()); |
+ const ProgramOutputInfo* info = GetProgramOutputInfo(original_name); |
+ if (!info) |
+ info = GetProgramOutputInfo(original_name + "[0]"); |
+ if (!info) |
+ return -1; |
+ return info->color_name; |
+} |
+ |
+GLint Program::GetFragDataIndex(const std::string& original_name) const { |
+ DCHECK(IsValid()); |
+ const ProgramOutputInfo* info = GetProgramOutputInfo(original_name); |
+ if (!info) |
+ info = GetProgramOutputInfo(original_name + "[0]"); |
+ if (!info) |
+ return -1; |
+ return info->index; |
+} |
+ |
void Program::TransformFeedbackVaryings(GLsizei count, |
const char* const* varyings, |
GLenum buffer_mode) { |
@@ -2041,11 +2268,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() { |