summaryrefslogtreecommitdiffstats
path: root/gpu/command_buffer/service/program_manager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gpu/command_buffer/service/program_manager.cc')
-rw-r--r--gpu/command_buffer/service/program_manager.cc245
1 files changed, 237 insertions, 8 deletions
diff --git a/gpu/command_buffer/service/program_manager.cc b/gpu/command_buffer/service/program_manager.cc
index bfff34c..45b0c55 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() {