summaryrefslogtreecommitdiffstats
path: root/gpu/command_buffer
diff options
context:
space:
mode:
authorkkinnunen <kkinnunen@nvidia.com>2015-12-04 01:36:31 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-04 09:37:12 +0000
commit8cefb231216c2777281f37a30e3fb8e67513ee2b (patch)
tree2d0163eed0729e5f7a8fb55fb03b034eddcb5305 /gpu/command_buffer
parent91db68e8cb2080d1e9e07858efea3f7e198112a9 (diff)
downloadchromium_src-8cefb231216c2777281f37a30e3fb8e67513ee2b.zip
chromium_src-8cefb231216c2777281f37a30e3fb8e67513ee2b.tar.gz
chromium_src-8cefb231216c2777281f37a30e3fb8e67513ee2b.tar.bz2
command_buffer: Implement EXT_blend_func_extended
Implement EXT_blend_func_extended for command buffer ES 2.0 and ES 3.0 contexts. For the ES 2.0 context, the extension supports dual-source blending with pre-defined gl_SecondaryFragColorEXT and gl_SecondaryFragDataEXT variables. Currently EXT_blend_func_extended is only exposed if the service context supports program interface query. This means OpenGL context or OpenGL ES 3.1 context. This is to simplify the unit test expectation conditions. Theoretically also ES 2.0 and ES 3.0 service contexts could support EXT_blend_func_extended, but probably there will never be such a driver. BUG=506765 Review URL: https://codereview.chromium.org/1309743005 Cr-Commit-Position: refs/heads/master@{#363175}
Diffstat (limited to 'gpu/command_buffer')
-rwxr-xr-xgpu/command_buffer/build_gles2_cmd_buffer.py26
-rw-r--r--gpu/command_buffer/client/gles2_c_lib_autogen.h28
-rw-r--r--gpu/command_buffer/client/gles2_cmd_helper_autogen.h32
-rw-r--r--gpu/command_buffer/client/gles2_implementation.cc56
-rw-r--r--gpu/command_buffer/client/gles2_implementation.h1
-rw-r--r--gpu/command_buffer/client/gles2_implementation_autogen.h11
-rw-r--r--gpu/command_buffer/client/gles2_interface_autogen.h8
-rw-r--r--gpu/command_buffer/client/gles2_interface_stub_autogen.h8
-rw-r--r--gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h12
-rw-r--r--gpu/command_buffer/client/gles2_trace_implementation_autogen.h8
-rw-r--r--gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h23
-rw-r--r--gpu/command_buffer/client/program_info_manager.cc38
-rw-r--r--gpu/command_buffer/client/program_info_manager.h8
-rw-r--r--gpu/command_buffer/cmd_buffer_functions.txt6
-rw-r--r--gpu/command_buffer/common/gles2_cmd_format_autogen.h160
-rw-r--r--gpu/command_buffer/common/gles2_cmd_format_test_autogen.h46
-rw-r--r--gpu/command_buffer/common/gles2_cmd_ids_autogen.h5
-rw-r--r--gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h19
-rw-r--r--gpu/command_buffer/service/context_group.cc11
-rw-r--r--gpu/command_buffer/service/context_group.h5
-rw-r--r--gpu/command_buffer/service/disk_cache_proto.proto6
-rw-r--r--gpu/command_buffer/service/feature_info.cc36
-rw-r--r--gpu/command_buffer/service/feature_info.h1
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc254
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc15
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest.h6
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc50
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h9
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc20
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_programs.cc13
-rw-r--r--gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h6
-rw-r--r--gpu/command_buffer/service/memory_program_cache.cc87
-rw-r--r--gpu/command_buffer/service/memory_program_cache.h12
-rw-r--r--gpu/command_buffer/service/memory_program_cache_unittest.cc39
-rw-r--r--gpu/command_buffer/service/mocks.h21
-rw-r--r--gpu/command_buffer/service/program_manager.cc245
-rw-r--r--gpu/command_buffer/service/program_manager.h59
-rw-r--r--gpu/command_buffer/service/program_manager_unittest.cc237
-rw-r--r--gpu/command_buffer/service/shader_manager.cc34
-rw-r--r--gpu/command_buffer/service/shader_manager.h17
-rw-r--r--gpu/command_buffer/service/shader_manager_unittest.cc36
-rw-r--r--gpu/command_buffer/service/shader_translator.cc9
-rw-r--r--gpu/command_buffer/service/shader_translator.h3
-rw-r--r--gpu/command_buffer/service/shader_translator_unittest.cc136
-rw-r--r--gpu/command_buffer/service/test_helper.cc110
-rw-r--r--gpu/command_buffer/service/test_helper.h39
-rw-r--r--gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc653
47 files changed, 2350 insertions, 314 deletions
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index eb5724a..76708f5 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -2864,6 +2864,16 @@ _FUNCTION_INFO = {
'result': ['GLint'],
'error_return': -1,
},
+ 'GetFragDataIndexEXT': {
+ 'type': 'Custom',
+ 'data_transfer_methods': ['shm'],
+ 'cmd_args':
+ 'GLidProgram program, uint32_t name_bucket_id, GLint* index',
+ 'result': ['GLint'],
+ 'error_return': -1,
+ 'extension': 'EXT_blend_func_extended',
+ 'extension_flag': 'ext_blend_func_extended',
+ },
'GetFragDataLocation': {
'type': 'Custom',
'data_transfer_methods': ['shm'],
@@ -4017,6 +4027,22 @@ _FUNCTION_INFO = {
'extension': True,
'chromium': True,
},
+ 'BindFragDataLocationEXT': {
+ 'type': 'GLchar',
+ 'data_transfer_methods': ['bucket'],
+ 'needs_size': True,
+ 'gl_test_func': 'DoBindFragDataLocationEXT',
+ 'extension': 'EXT_blend_func_extended',
+ 'extension_flag': 'ext_blend_func_extended',
+ },
+ 'BindFragDataLocationIndexedEXT': {
+ 'type': 'GLchar',
+ 'data_transfer_methods': ['bucket'],
+ 'needs_size': True,
+ 'gl_test_func': 'DoBindFragDataLocationIndexedEXT',
+ 'extension': 'EXT_blend_func_extended',
+ 'extension_flag': 'ext_blend_func_extended',
+ },
'BindUniformLocationCHROMIUM': {
'type': 'GLchar',
'extension': 'CHROMIUM_bind_uniform_location',
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h
index b274a6b..8cb59a9 100644
--- a/gpu/command_buffer/client/gles2_c_lib_autogen.h
+++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -1704,6 +1704,21 @@ void GL_APIENTRY GLES2BlendBarrierKHR() {
void GL_APIENTRY GLES2ApplyScreenSpaceAntialiasingCHROMIUM() {
gles2::GetGLContext()->ApplyScreenSpaceAntialiasingCHROMIUM();
}
+void GL_APIENTRY GLES2BindFragDataLocationIndexedEXT(GLuint program,
+ GLuint colorNumber,
+ GLuint index,
+ const char* name) {
+ gles2::GetGLContext()->BindFragDataLocationIndexedEXT(program, colorNumber,
+ index, name);
+}
+void GL_APIENTRY GLES2BindFragDataLocationEXT(GLuint program,
+ GLuint colorNumber,
+ const char* name) {
+ gles2::GetGLContext()->BindFragDataLocationEXT(program, colorNumber, name);
+}
+GLint GL_APIENTRY GLES2GetFragDataIndexEXT(GLuint program, const char* name) {
+ return gles2::GetGLContext()->GetFragDataIndexEXT(program, name);
+}
namespace gles2 {
@@ -2994,6 +3009,19 @@ extern const NameToFunc g_gles2_function_table[] = {
glApplyScreenSpaceAntialiasingCHROMIUM),
},
{
+ "glBindFragDataLocationIndexedEXT",
+ reinterpret_cast<GLES2FunctionPointer>(
+ glBindFragDataLocationIndexedEXT),
+ },
+ {
+ "glBindFragDataLocationEXT",
+ reinterpret_cast<GLES2FunctionPointer>(glBindFragDataLocationEXT),
+ },
+ {
+ "glGetFragDataIndexEXT",
+ reinterpret_cast<GLES2FunctionPointer>(glGetFragDataIndexEXT),
+ },
+ {
NULL, NULL,
},
};
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
index b5cf24c..b87750a 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -3166,4 +3166,36 @@ void ApplyScreenSpaceAntialiasingCHROMIUM() {
}
}
+void BindFragDataLocationIndexedEXTBucket(GLuint program,
+ GLuint colorNumber,
+ GLuint index,
+ uint32_t name_bucket_id) {
+ gles2::cmds::BindFragDataLocationIndexedEXTBucket* c =
+ GetCmdSpace<gles2::cmds::BindFragDataLocationIndexedEXTBucket>();
+ if (c) {
+ c->Init(program, colorNumber, index, name_bucket_id);
+ }
+}
+
+void BindFragDataLocationEXTBucket(GLuint program,
+ GLuint colorNumber,
+ uint32_t name_bucket_id) {
+ gles2::cmds::BindFragDataLocationEXTBucket* c =
+ GetCmdSpace<gles2::cmds::BindFragDataLocationEXTBucket>();
+ if (c) {
+ c->Init(program, colorNumber, name_bucket_id);
+ }
+}
+
+void GetFragDataIndexEXT(GLuint program,
+ uint32_t name_bucket_id,
+ uint32_t index_shm_id,
+ uint32_t index_shm_offset) {
+ gles2::cmds::GetFragDataIndexEXT* c =
+ GetCmdSpace<gles2::cmds::GetFragDataIndexEXT>();
+ if (c) {
+ c->Init(program, name_bucket_id, index_shm_id, index_shm_offset);
+ }
+}
+
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_CMD_HELPER_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc
index 6a00426..7c7f99d 100644
--- a/gpu/command_buffer/client/gles2_implementation.cc
+++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -1396,6 +1396,33 @@ void GLES2Implementation::BindAttribLocation(
CheckGLError();
}
+void GLES2Implementation::BindFragDataLocationEXT(GLuint program,
+ GLuint colorName,
+ const char* name) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBindFragDataLocationEXT("
+ << program << ", " << colorName << ", " << name << ")");
+ SetBucketAsString(kResultBucketId, name);
+ helper_->BindFragDataLocationEXTBucket(program, colorName, kResultBucketId);
+ helper_->SetBucketSize(kResultBucketId, 0);
+ CheckGLError();
+}
+
+void GLES2Implementation::BindFragDataLocationIndexedEXT(GLuint program,
+ GLuint colorName,
+ GLuint index,
+ const char* name) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBindFragDataLocationEXT("
+ << program << ", " << colorName << ", " << index << ", "
+ << name << ")");
+ SetBucketAsString(kResultBucketId, name);
+ helper_->BindFragDataLocationIndexedEXTBucket(program, colorName, index,
+ kResultBucketId);
+ helper_->SetBucketSize(kResultBucketId, 0);
+ CheckGLError();
+}
+
void GLES2Implementation::BindUniformLocationCHROMIUM(
GLuint program, GLint location, const char* name) {
GPU_CLIENT_SINGLE_THREAD_CHECK();
@@ -1607,6 +1634,35 @@ bool GLES2Implementation::GetProgramivHelper(
return got_value;
}
+GLint GLES2Implementation::GetFragDataIndexEXTHelper(GLuint program,
+ const char* name) {
+ typedef cmds::GetFragDataIndexEXT::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return -1;
+ }
+ *result = -1;
+ SetBucketAsCString(kResultBucketId, name);
+ helper_->GetFragDataIndexEXT(program, kResultBucketId, GetResultShmId(),
+ GetResultShmOffset());
+ WaitForCmd();
+ helper_->SetBucketSize(kResultBucketId, 0);
+ return *result;
+}
+
+GLint GLES2Implementation::GetFragDataIndexEXT(GLuint program,
+ const char* name) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetFragDataIndexEXT(" << program
+ << ", " << name << ")");
+ TRACE_EVENT0("gpu", "GLES2::GetFragDataIndexEXT");
+ GLint loc = share_group_->program_info_manager()->GetFragDataIndex(
+ this, program, name);
+ GPU_CLIENT_LOG("returned " << loc);
+ CheckGLError();
+ return loc;
+}
+
GLint GLES2Implementation::GetFragDataLocationHelper(
GLuint program, const char* name) {
typedef cmds::GetFragDataLocation::Result Result;
diff --git a/gpu/command_buffer/client/gles2_implementation.h b/gpu/command_buffer/client/gles2_implementation.h
index 1c2eba9..1c55b83 100644
--- a/gpu/command_buffer/client/gles2_implementation.h
+++ b/gpu/command_buffer/client/gles2_implementation.h
@@ -215,6 +215,7 @@ class GLES2_IMPL_EXPORT GLES2Implementation
void GetProgramInfoCHROMIUMHelper(GLuint program, std::vector<int8>* result);
GLint GetAttribLocationHelper(GLuint program, const char* name);
GLint GetUniformLocationHelper(GLuint program, const char* name);
+ GLint GetFragDataIndexEXTHelper(GLuint program, const char* name);
GLint GetFragDataLocationHelper(GLuint program, const char* name);
bool GetActiveAttribHelper(
GLuint program, GLuint index, GLsizei bufsize, GLsizei* length,
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h
index 0a4f886..10ebcc3 100644
--- a/gpu/command_buffer/client/gles2_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -1184,4 +1184,15 @@ void BlendBarrierKHR() override;
void ApplyScreenSpaceAntialiasingCHROMIUM() override;
+void BindFragDataLocationIndexedEXT(GLuint program,
+ GLuint colorNumber,
+ GLuint index,
+ const char* name) override;
+
+void BindFragDataLocationEXT(GLuint program,
+ GLuint colorNumber,
+ const char* name) override;
+
+GLint GetFragDataIndexEXT(GLuint program, const char* name) override;
+
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_IMPLEMENTATION_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h
index 77872e7..374cb26 100644
--- a/gpu/command_buffer/client/gles2_interface_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_autogen.h
@@ -885,4 +885,12 @@ virtual void ProgramPathFragmentInputGenCHROMIUM(GLuint program,
virtual GLenum GetGraphicsResetStatusKHR() = 0;
virtual void BlendBarrierKHR() = 0;
virtual void ApplyScreenSpaceAntialiasingCHROMIUM() = 0;
+virtual void BindFragDataLocationIndexedEXT(GLuint program,
+ GLuint colorNumber,
+ GLuint index,
+ const char* name) = 0;
+virtual void BindFragDataLocationEXT(GLuint program,
+ GLuint colorNumber,
+ const char* name) = 0;
+virtual GLint GetFragDataIndexEXT(GLuint program, const char* name) = 0;
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_INTERFACE_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
index 5469525..489afa2 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
@@ -859,4 +859,12 @@ void ProgramPathFragmentInputGenCHROMIUM(GLuint program,
GLenum GetGraphicsResetStatusKHR() override;
void BlendBarrierKHR() override;
void ApplyScreenSpaceAntialiasingCHROMIUM() override;
+void BindFragDataLocationIndexedEXT(GLuint program,
+ GLuint colorNumber,
+ GLuint index,
+ const char* name) override;
+void BindFragDataLocationEXT(GLuint program,
+ GLuint colorNumber,
+ const char* name) override;
+GLint GetFragDataIndexEXT(GLuint program, const char* name) override;
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_INTERFACE_STUB_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
index 867652f..148f3af 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
@@ -1159,4 +1159,16 @@ GLenum GLES2InterfaceStub::GetGraphicsResetStatusKHR() {
}
void GLES2InterfaceStub::BlendBarrierKHR() {}
void GLES2InterfaceStub::ApplyScreenSpaceAntialiasingCHROMIUM() {}
+void GLES2InterfaceStub::BindFragDataLocationIndexedEXT(
+ GLuint /* program */,
+ GLuint /* colorNumber */,
+ GLuint /* index */,
+ const char* /* name */) {}
+void GLES2InterfaceStub::BindFragDataLocationEXT(GLuint /* program */,
+ GLuint /* colorNumber */,
+ const char* /* name */) {}
+GLint GLES2InterfaceStub::GetFragDataIndexEXT(GLuint /* program */,
+ const char* /* name */) {
+ return 0;
+}
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_INTERFACE_STUB_IMPL_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
index 209215e..8347572 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
@@ -859,4 +859,12 @@ void ProgramPathFragmentInputGenCHROMIUM(GLuint program,
GLenum GetGraphicsResetStatusKHR() override;
void BlendBarrierKHR() override;
void ApplyScreenSpaceAntialiasingCHROMIUM() override;
+void BindFragDataLocationIndexedEXT(GLuint program,
+ GLuint colorNumber,
+ GLuint index,
+ const char* name) override;
+void BindFragDataLocationEXT(GLuint program,
+ GLuint colorNumber,
+ const char* name) override;
+GLint GetFragDataIndexEXT(GLuint program, const char* name) override;
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_TRACE_IMPLEMENTATION_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
index 77c6ab6..5f67456 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
@@ -2475,4 +2475,27 @@ void GLES2TraceImplementation::ApplyScreenSpaceAntialiasingCHROMIUM() {
gl_->ApplyScreenSpaceAntialiasingCHROMIUM();
}
+void GLES2TraceImplementation::BindFragDataLocationIndexedEXT(
+ GLuint program,
+ GLuint colorNumber,
+ GLuint index,
+ const char* name) {
+ TRACE_EVENT_BINARY_EFFICIENT0("gpu",
+ "GLES2Trace::BindFragDataLocationIndexedEXT");
+ gl_->BindFragDataLocationIndexedEXT(program, colorNumber, index, name);
+}
+
+void GLES2TraceImplementation::BindFragDataLocationEXT(GLuint program,
+ GLuint colorNumber,
+ const char* name) {
+ TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::BindFragDataLocationEXT");
+ gl_->BindFragDataLocationEXT(program, colorNumber, name);
+}
+
+GLint GLES2TraceImplementation::GetFragDataIndexEXT(GLuint program,
+ const char* name) {
+ TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::GetFragDataIndexEXT");
+ return gl_->GetFragDataIndexEXT(program, name);
+}
+
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_TRACE_IMPLEMENTATION_IMPL_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/program_info_manager.cc b/gpu/command_buffer/client/program_info_manager.cc
index 939473a..d047b57 100644
--- a/gpu/command_buffer/client/program_info_manager.cc
+++ b/gpu/command_buffer/client/program_info_manager.cc
@@ -160,6 +160,19 @@ GLuint ProgramInfoManager::Program::GetUniformIndex(
return GL_INVALID_INDEX;
}
+GLint ProgramInfoManager::Program::GetFragDataIndex(
+ const std::string& name) const {
+ auto iter = frag_data_indices_.find(name);
+ if (iter == frag_data_indices_.end())
+ return -1;
+ return iter->second;
+}
+
+void ProgramInfoManager::Program::CacheFragDataIndex(const std::string& name,
+ GLint index) {
+ frag_data_indices_[name] = index;
+}
+
GLint ProgramInfoManager::Program::GetFragDataLocation(
const std::string& name) const {
base::hash_map<std::string, GLint>::const_iterator iter =
@@ -725,6 +738,31 @@ GLint ProgramInfoManager::GetUniformLocation(
return gl->GetUniformLocationHelper(program, name);
}
+GLint ProgramInfoManager::GetFragDataIndex(GLES2Implementation* gl,
+ GLuint program,
+ const char* name) {
+ // TODO(zmo): make FragData indexes part of the ProgramInfo that are
+ // fetched from the service side. See crbug.com/452104.
+ {
+ base::AutoLock auto_lock(lock_);
+ Program* info = GetProgramInfo(gl, program, kNone);
+ if (info) {
+ GLint possible_index = info->GetFragDataIndex(name);
+ if (possible_index != -1)
+ return possible_index;
+ }
+ }
+ GLint index = gl->GetFragDataIndexEXTHelper(program, name);
+ if (index != -1) {
+ base::AutoLock auto_lock(lock_);
+ Program* info = GetProgramInfo(gl, program, kNone);
+ if (info) {
+ info->CacheFragDataIndex(name, index);
+ }
+ }
+ return index;
+}
+
GLint ProgramInfoManager::GetFragDataLocation(
GLES2Implementation* gl, GLuint program, const char* name) {
// TODO(zmo): make FragData locations part of the ProgramInfo that are
diff --git a/gpu/command_buffer/client/program_info_manager.h b/gpu/command_buffer/client/program_info_manager.h
index fa74ea5..fbae19a 100644
--- a/gpu/command_buffer/client/program_info_manager.h
+++ b/gpu/command_buffer/client/program_info_manager.h
@@ -38,6 +38,10 @@ class GLES2_IMPL_EXPORT ProgramInfoManager {
GLint GetUniformLocation(
GLES2Implementation* gl, GLuint program, const char* name);
+ GLint GetFragDataIndex(GLES2Implementation* gl,
+ GLuint program,
+ const char* name);
+
GLint GetFragDataLocation(
GLES2Implementation* gl, GLuint program, const char* name);
@@ -166,6 +170,9 @@ class GLES2_IMPL_EXPORT ProgramInfoManager {
bool GetUniformsiv(
GLsizei count, const GLuint* indices, GLenum pname, GLint* params);
+ GLint GetFragDataIndex(const std::string& name) const;
+ void CacheFragDataIndex(const std::string& name, GLint index);
+
GLint GetFragDataLocation(const std::string& name) const;
void CacheFragDataLocation(const std::string& name, GLint loc);
@@ -232,6 +239,7 @@ class GLES2_IMPL_EXPORT ProgramInfoManager {
std::vector<UniformES3> uniforms_es3_;
base::hash_map<std::string, GLint> frag_data_locations_;
+ base::hash_map<std::string, GLint> frag_data_indices_;
};
Program* GetProgramInfo(
diff --git a/gpu/command_buffer/cmd_buffer_functions.txt b/gpu/command_buffer/cmd_buffer_functions.txt
index 42ed5f5..8c9baf0 100644
--- a/gpu/command_buffer/cmd_buffer_functions.txt
+++ b/gpu/command_buffer/cmd_buffer_functions.txt
@@ -352,3 +352,9 @@ GL_APICALL void GL_APIENTRY glBlendBarrierKHR (void);
// Extension GL_CHROMIUM_screen_space_antialiasing
GL_APICALL void GL_APIENTRY glApplyScreenSpaceAntialiasingCHROMIUM (void);
+
+// Extension EXT_blend_func_extended
+GL_APICALL void GL_APIENTRY glBindFragDataLocationIndexedEXT (GLidProgram program, GLuint colorNumber, GLuint index, const char* name);
+GL_APICALL void GL_APIENTRY glBindFragDataLocationEXT (GLidProgram program, GLuint colorNumber, const char* name);
+GL_APICALL GLint GL_APIENTRY glGetFragDataIndexEXT (GLidProgram program, const char* name);
+
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
index b26cea0..ec3a09b 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -15434,4 +15434,164 @@ static_assert(
offsetof(ApplyScreenSpaceAntialiasingCHROMIUM, header) == 0,
"offset of ApplyScreenSpaceAntialiasingCHROMIUM header should be 0");
+struct BindFragDataLocationIndexedEXTBucket {
+ typedef BindFragDataLocationIndexedEXTBucket ValueType;
+ static const CommandId kCmdId = kBindFragDataLocationIndexedEXTBucket;
+ static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+ static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+ static uint32_t ComputeSize() {
+ return static_cast<uint32_t>(sizeof(ValueType)); // NOLINT
+ }
+
+ void SetHeader() { header.SetCmd<ValueType>(); }
+
+ void Init(GLuint _program,
+ GLuint _colorNumber,
+ GLuint _index,
+ uint32_t _name_bucket_id) {
+ SetHeader();
+ program = _program;
+ colorNumber = _colorNumber;
+ index = _index;
+ name_bucket_id = _name_bucket_id;
+ }
+
+ void* Set(void* cmd,
+ GLuint _program,
+ GLuint _colorNumber,
+ GLuint _index,
+ uint32_t _name_bucket_id) {
+ static_cast<ValueType*>(cmd)
+ ->Init(_program, _colorNumber, _index, _name_bucket_id);
+ return NextCmdAddress<ValueType>(cmd);
+ }
+
+ gpu::CommandHeader header;
+ uint32_t program;
+ uint32_t colorNumber;
+ uint32_t index;
+ uint32_t name_bucket_id;
+};
+
+static_assert(sizeof(BindFragDataLocationIndexedEXTBucket) == 20,
+ "size of BindFragDataLocationIndexedEXTBucket should be 20");
+static_assert(
+ offsetof(BindFragDataLocationIndexedEXTBucket, header) == 0,
+ "offset of BindFragDataLocationIndexedEXTBucket header should be 0");
+static_assert(
+ offsetof(BindFragDataLocationIndexedEXTBucket, program) == 4,
+ "offset of BindFragDataLocationIndexedEXTBucket program should be 4");
+static_assert(
+ offsetof(BindFragDataLocationIndexedEXTBucket, colorNumber) == 8,
+ "offset of BindFragDataLocationIndexedEXTBucket colorNumber should be 8");
+static_assert(
+ offsetof(BindFragDataLocationIndexedEXTBucket, index) == 12,
+ "offset of BindFragDataLocationIndexedEXTBucket index should be 12");
+static_assert(offsetof(BindFragDataLocationIndexedEXTBucket, name_bucket_id) ==
+ 16,
+ "offset of BindFragDataLocationIndexedEXTBucket name_bucket_id "
+ "should be 16");
+
+struct BindFragDataLocationEXTBucket {
+ typedef BindFragDataLocationEXTBucket ValueType;
+ static const CommandId kCmdId = kBindFragDataLocationEXTBucket;
+ static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+ static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+ static uint32_t ComputeSize() {
+ return static_cast<uint32_t>(sizeof(ValueType)); // NOLINT
+ }
+
+ void SetHeader() { header.SetCmd<ValueType>(); }
+
+ void Init(GLuint _program, GLuint _colorNumber, uint32_t _name_bucket_id) {
+ SetHeader();
+ program = _program;
+ colorNumber = _colorNumber;
+ name_bucket_id = _name_bucket_id;
+ }
+
+ void* Set(void* cmd,
+ GLuint _program,
+ GLuint _colorNumber,
+ uint32_t _name_bucket_id) {
+ static_cast<ValueType*>(cmd)->Init(_program, _colorNumber, _name_bucket_id);
+ return NextCmdAddress<ValueType>(cmd);
+ }
+
+ gpu::CommandHeader header;
+ uint32_t program;
+ uint32_t colorNumber;
+ uint32_t name_bucket_id;
+};
+
+static_assert(sizeof(BindFragDataLocationEXTBucket) == 16,
+ "size of BindFragDataLocationEXTBucket should be 16");
+static_assert(offsetof(BindFragDataLocationEXTBucket, header) == 0,
+ "offset of BindFragDataLocationEXTBucket header should be 0");
+static_assert(offsetof(BindFragDataLocationEXTBucket, program) == 4,
+ "offset of BindFragDataLocationEXTBucket program should be 4");
+static_assert(
+ offsetof(BindFragDataLocationEXTBucket, colorNumber) == 8,
+ "offset of BindFragDataLocationEXTBucket colorNumber should be 8");
+static_assert(
+ offsetof(BindFragDataLocationEXTBucket, name_bucket_id) == 12,
+ "offset of BindFragDataLocationEXTBucket name_bucket_id should be 12");
+
+struct GetFragDataIndexEXT {
+ typedef GetFragDataIndexEXT ValueType;
+ static const CommandId kCmdId = kGetFragDataIndexEXT;
+ static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+ static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+ typedef GLint Result;
+
+ static uint32_t ComputeSize() {
+ return static_cast<uint32_t>(sizeof(ValueType)); // NOLINT
+ }
+
+ void SetHeader() { header.SetCmd<ValueType>(); }
+
+ void Init(GLuint _program,
+ uint32_t _name_bucket_id,
+ uint32_t _index_shm_id,
+ uint32_t _index_shm_offset) {
+ SetHeader();
+ program = _program;
+ name_bucket_id = _name_bucket_id;
+ index_shm_id = _index_shm_id;
+ index_shm_offset = _index_shm_offset;
+ }
+
+ void* Set(void* cmd,
+ GLuint _program,
+ uint32_t _name_bucket_id,
+ uint32_t _index_shm_id,
+ uint32_t _index_shm_offset) {
+ static_cast<ValueType*>(cmd)
+ ->Init(_program, _name_bucket_id, _index_shm_id, _index_shm_offset);
+ return NextCmdAddress<ValueType>(cmd);
+ }
+
+ gpu::CommandHeader header;
+ uint32_t program;
+ uint32_t name_bucket_id;
+ uint32_t index_shm_id;
+ uint32_t index_shm_offset;
+};
+
+static_assert(sizeof(GetFragDataIndexEXT) == 20,
+ "size of GetFragDataIndexEXT should be 20");
+static_assert(offsetof(GetFragDataIndexEXT, header) == 0,
+ "offset of GetFragDataIndexEXT header should be 0");
+static_assert(offsetof(GetFragDataIndexEXT, program) == 4,
+ "offset of GetFragDataIndexEXT program should be 4");
+static_assert(offsetof(GetFragDataIndexEXT, name_bucket_id) == 8,
+ "offset of GetFragDataIndexEXT name_bucket_id should be 8");
+static_assert(offsetof(GetFragDataIndexEXT, index_shm_id) == 12,
+ "offset of GetFragDataIndexEXT index_shm_id should be 12");
+static_assert(offsetof(GetFragDataIndexEXT, index_shm_offset) == 16,
+ "offset of GetFragDataIndexEXT index_shm_offset should be 16");
+
#endif // GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_FORMAT_AUTOGEN_H_
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
index 95a8ab8..939ea4e 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -5298,4 +5298,50 @@ TEST_F(GLES2FormatTest, ApplyScreenSpaceAntialiasingCHROMIUM) {
CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
}
+TEST_F(GLES2FormatTest, BindFragDataLocationIndexedEXTBucket) {
+ cmds::BindFragDataLocationIndexedEXTBucket& cmd =
+ *GetBufferAs<cmds::BindFragDataLocationIndexedEXTBucket>();
+ void* next_cmd =
+ cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<GLuint>(12),
+ static_cast<GLuint>(13), static_cast<uint32_t>(14));
+ EXPECT_EQ(
+ static_cast<uint32_t>(cmds::BindFragDataLocationIndexedEXTBucket::kCmdId),
+ cmd.header.command);
+ EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+ EXPECT_EQ(static_cast<GLuint>(11), cmd.program);
+ EXPECT_EQ(static_cast<GLuint>(12), cmd.colorNumber);
+ EXPECT_EQ(static_cast<GLuint>(13), cmd.index);
+ EXPECT_EQ(static_cast<uint32_t>(14), cmd.name_bucket_id);
+ CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
+TEST_F(GLES2FormatTest, BindFragDataLocationEXTBucket) {
+ cmds::BindFragDataLocationEXTBucket& cmd =
+ *GetBufferAs<cmds::BindFragDataLocationEXTBucket>();
+ void* next_cmd = cmd.Set(&cmd, static_cast<GLuint>(11),
+ static_cast<GLuint>(12), static_cast<uint32_t>(13));
+ EXPECT_EQ(static_cast<uint32_t>(cmds::BindFragDataLocationEXTBucket::kCmdId),
+ cmd.header.command);
+ EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+ EXPECT_EQ(static_cast<GLuint>(11), cmd.program);
+ EXPECT_EQ(static_cast<GLuint>(12), cmd.colorNumber);
+ EXPECT_EQ(static_cast<uint32_t>(13), cmd.name_bucket_id);
+ CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
+TEST_F(GLES2FormatTest, GetFragDataIndexEXT) {
+ cmds::GetFragDataIndexEXT& cmd = *GetBufferAs<cmds::GetFragDataIndexEXT>();
+ void* next_cmd =
+ cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<uint32_t>(12),
+ static_cast<uint32_t>(13), static_cast<uint32_t>(14));
+ EXPECT_EQ(static_cast<uint32_t>(cmds::GetFragDataIndexEXT::kCmdId),
+ cmd.header.command);
+ EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+ EXPECT_EQ(static_cast<GLuint>(11), cmd.program);
+ EXPECT_EQ(static_cast<uint32_t>(12), cmd.name_bucket_id);
+ EXPECT_EQ(static_cast<uint32_t>(13), cmd.index_shm_id);
+ EXPECT_EQ(static_cast<uint32_t>(14), cmd.index_shm_offset);
+ CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
#endif // GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_FORMAT_TEST_AUTOGEN_H_
diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
index 22e5b0a..38dad79 100644
--- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
@@ -331,7 +331,10 @@
OP(BindFragmentInputLocationCHROMIUMBucket) /* 572 */ \
OP(ProgramPathFragmentInputGenCHROMIUM) /* 573 */ \
OP(BlendBarrierKHR) /* 574 */ \
- OP(ApplyScreenSpaceAntialiasingCHROMIUM) /* 575 */
+ OP(ApplyScreenSpaceAntialiasingCHROMIUM) /* 575 */ \
+ OP(BindFragDataLocationIndexedEXTBucket) /* 576 */ \
+ OP(BindFragDataLocationEXTBucket) /* 577 */ \
+ OP(GetFragDataIndexEXT) /* 578 */
enum CommandId {
kStartPoint = cmd::kLastCommonId, // All GLES2 commands start after this.
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
index b84fbb0..af31a51 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
@@ -643,6 +643,9 @@ static const GLES2Util::EnumToString enum_to_string_table[] = {
0x78FB, "GL_RGB_YCBCR_422_CHROMIUM",
},
{
+ 0x78FC, "GL_RGB_YCBCR_420V_CHROMIUM",
+ },
+ {
0x80000000, "GL_MULTISAMPLE_BUFFER_BIT7_QCOM",
},
{
@@ -1279,6 +1282,9 @@ static const GLES2Util::EnumToString enum_to_string_table[] = {
0x8576, "GL_CONSTANT_CHROMIUM",
},
{
+ 0x8589, "GL_SRC1_ALPHA_EXT",
+ },
+ {
0x85B5, "GL_VERTEX_ARRAY_BINDING_OES",
},
{
@@ -1555,6 +1561,18 @@ static const GLES2Util::EnumToString enum_to_string_table[] = {
0x88F0, "GL_DEPTH24_STENCIL8_OES",
},
{
+ 0x88F9, "GL_SRC1_COLOR_EXT",
+ },
+ {
+ 0x88FA, "GL_ONE_MINUS_SRC1_COLOR_EXT",
+ },
+ {
+ 0x88FB, "GL_ONE_MINUS_SRC1_ALPHA_EXT",
+ },
+ {
+ 0x88FC, "GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT",
+ },
+ {
0x88FD, "GL_VERTEX_ATTRIB_ARRAY_INTEGER",
},
{
@@ -3956,6 +3974,7 @@ std::string GLES2Util::GetStringImageInternalFormat(uint32_t value) {
{GL_RGB, "GL_RGB"},
{GL_RGB_YUV_420_CHROMIUM, "GL_RGB_YUV_420_CHROMIUM"},
{GL_RGB_YCBCR_422_CHROMIUM, "GL_RGB_YCBCR_422_CHROMIUM"},
+ {GL_RGB_YCBCR_420V_CHROMIUM, "GL_RGB_YCBCR_420V_CHROMIUM"},
{GL_RGBA, "GL_RGBA"},
};
return GLES2Util::GetQualifiedEnumString(string_table,
diff --git a/gpu/command_buffer/service/context_group.cc b/gpu/command_buffer/service/context_group.cc
index aeae5c6..cdda09f 100644
--- a/gpu/command_buffer/service/context_group.cc
+++ b/gpu/command_buffer/service/context_group.cc
@@ -67,6 +67,7 @@ ContextGroup::ContextGroup(
max_vertex_uniform_vectors_(0u),
max_color_attachments_(1u),
max_draw_buffers_(1u),
+ max_dual_source_draw_buffers_(0u),
program_cache_(NULL),
feature_info_(feature_info) {
{
@@ -139,6 +140,11 @@ bool ContextGroup::Initialize(GLES2Decoder* decoder,
if (max_draw_buffers_ < 1)
max_draw_buffers_ = 1;
}
+ if (feature_info_->feature_flags().ext_blend_func_extended) {
+ GetIntegerv(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT,
+ &max_dual_source_draw_buffers_);
+ DCHECK(max_dual_source_draw_buffers_ >= 1);
+ }
buffer_manager_.reset(
new BufferManager(memory_tracker_.get(), feature_info_.get()));
@@ -285,8 +291,9 @@ bool ContextGroup::Initialize(GLES2Decoder* decoder,
path_manager_.reset(new PathManager());
- program_manager_.reset(new ProgramManager(
- program_cache_, max_varying_vectors_, feature_info_.get()));
+ program_manager_.reset(
+ new ProgramManager(program_cache_, max_varying_vectors_,
+ max_dual_source_draw_buffers_, feature_info_.get()));
if (!texture_manager_->Initialize()) {
LOG(ERROR) << "Context::Group::Initialize failed because texture manager "
diff --git a/gpu/command_buffer/service/context_group.h b/gpu/command_buffer/service/context_group.h
index ad2c5df..fa9d3ac 100644
--- a/gpu/command_buffer/service/context_group.h
+++ b/gpu/command_buffer/service/context_group.h
@@ -123,6 +123,10 @@ class GPU_EXPORT ContextGroup : public base::RefCounted<ContextGroup> {
return max_draw_buffers_;
}
+ uint32 max_dual_source_draw_buffers() const {
+ return max_dual_source_draw_buffers_;
+ }
+
FeatureInfo* feature_info() {
return feature_info_.get();
}
@@ -271,6 +275,7 @@ class GPU_EXPORT ContextGroup : public base::RefCounted<ContextGroup> {
uint32 max_vertex_uniform_vectors_;
uint32 max_color_attachments_;
uint32 max_draw_buffers_;
+ uint32 max_dual_source_draw_buffers_;
ProgramCache* program_cache_;
diff --git a/gpu/command_buffer/service/disk_cache_proto.proto b/gpu/command_buffer/service/disk_cache_proto.proto
index b1059a6..6d78f4b 100644
--- a/gpu/command_buffer/service/disk_cache_proto.proto
+++ b/gpu/command_buffer/service/disk_cache_proto.proto
@@ -27,11 +27,17 @@ message ShaderVaryingProto {
optional bool is_invariant = 3;
}
+message ShaderOutputVariableProto {
+ optional ShaderVariableProto basic = 1;
+ optional int32 location = 2;
+}
+
message ShaderProto {
optional bytes sha = 1;
repeated ShaderAttributeProto attribs = 2;
repeated ShaderUniformProto uniforms = 3;
repeated ShaderVaryingProto varyings = 4;
+ repeated ShaderOutputVariableProto output_variables = 5;
}
message GpuProgramProto {
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index 472fcb6..76494a6 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -176,7 +176,8 @@ FeatureInfo::FeatureFlags::FeatureFlags()
enable_subscribe_uniform(false),
emulate_primitive_restart_fixed_index(false),
ext_render_buffer_format_bgra8888(false),
- ext_multisample_compatibility(false) {}
+ ext_multisample_compatibility(false),
+ ext_blend_func_extended(false) {}
FeatureInfo::Workarounds::Workarounds() :
#define GPU_OP(type, name) name(false),
@@ -226,7 +227,8 @@ void FeatureInfo::InitializeBasicState(const base::CommandLine* command_line) {
// The shader translator is needed to translate from WebGL-conformant GLES SL
// to normal GLES SL, enforce WebGL conformance, translate from GLES SL 1.0 to
- // target context GLSL, etc.
+ // target context GLSL, implement emulation of OpenGL ES features on OpenGL,
+ // etc.
// The flag here is for testing only.
disable_shader_translator_ =
command_line->HasSwitch(switches::kDisableGLSLTranslator);
@@ -1186,6 +1188,36 @@ void FeatureInfo::InitializeFeatures() {
}
UMA_HISTOGRAM_BOOLEAN("GPU.TextureRG", feature_flags_.ext_texture_rg);
+ bool has_opengl_dual_source_blending =
+ gl_version_info_->IsAtLeastGL(3, 3) ||
+ (gl_version_info_->IsAtLeastGL(3, 2) &&
+ extensions.Contains("GL_ARB_blend_func_extended"));
+ if (!disable_shader_translator_ &&
+ ((gl_version_info_->IsAtLeastGL(3, 2) &&
+ has_opengl_dual_source_blending) ||
+ (gl_version_info_->IsAtLeastGLES(3, 0) &&
+ extensions.Contains("GL_EXT_blend_func_extended")))) {
+ // Note: to simplify the code, we do not expose EXT_blend_func_extended
+ // unless the service context supports ES 3.0. This means the theoretical ES
+ // 2.0 implementation with EXT_blend_func_extended is not sufficient.
+ feature_flags_.ext_blend_func_extended = true;
+ AddExtensionString("GL_EXT_blend_func_extended");
+
+ // NOTE: SRC_ALPHA_SATURATE is valid for ES2 src blend factor.
+ // SRC_ALPHA_SATURATE is valid for ES3 src and dst blend factor.
+ validators_.dst_blend_factor.AddValue(GL_SRC_ALPHA_SATURATE_EXT);
+
+ validators_.src_blend_factor.AddValue(GL_SRC1_ALPHA_EXT);
+ validators_.dst_blend_factor.AddValue(GL_SRC1_ALPHA_EXT);
+ validators_.src_blend_factor.AddValue(GL_SRC1_COLOR_EXT);
+ validators_.dst_blend_factor.AddValue(GL_SRC1_COLOR_EXT);
+ validators_.src_blend_factor.AddValue(GL_ONE_MINUS_SRC1_COLOR_EXT);
+ validators_.dst_blend_factor.AddValue(GL_ONE_MINUS_SRC1_COLOR_EXT);
+ validators_.src_blend_factor.AddValue(GL_ONE_MINUS_SRC1_ALPHA_EXT);
+ validators_.dst_blend_factor.AddValue(GL_ONE_MINUS_SRC1_ALPHA_EXT);
+ validators_.g_l_state.AddValue(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT);
+ }
+
#if !defined(OS_MACOSX)
if (workarounds_.ignore_egl_sync_failures) {
gfx::GLFenceEGL::SetIgnoreFailures();
diff --git a/gpu/command_buffer/service/feature_info.h b/gpu/command_buffer/service/feature_info.h
index c958315..f372f02 100644
--- a/gpu/command_buffer/service/feature_info.h
+++ b/gpu/command_buffer/service/feature_info.h
@@ -87,6 +87,7 @@ class GPU_EXPORT FeatureInfo : public base::RefCounted<FeatureInfo> {
bool emulate_primitive_restart_fixed_index;
bool ext_render_buffer_format_bgra8888;
bool ext_multisample_compatibility;
+ bool ext_blend_func_extended;
};
struct Workarounds {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 34ddc02..1dec5cf 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -294,13 +294,10 @@ static bool CharacterIsValidForGLES(unsigned char c) {
return false;
}
-static bool StringIsValidForGLES(const char* str) {
- for (; *str; ++str) {
- if (!CharacterIsValidForGLES(*str)) {
- return false;
- }
- }
- return true;
+static bool StringIsValidForGLES(const std::string& str) {
+ return str.length() == 0 ||
+ std::find_if_not(str.begin(), str.end(), CharacterIsValidForGLES) ==
+ str.end();
}
// This class prevents any GL errors that occur when it is in scope from
@@ -1200,9 +1197,22 @@ class GLES2DecoderImpl : public GLES2Decoder, public ErrorStateClient {
client_id, service_id, group_->max_vertex_attribs(), client_visible);
}
- void DoBindAttribLocation(GLuint client_id, GLuint index, const char* name);
- void DoBindUniformLocationCHROMIUM(
- GLuint client_id, GLint location, const char* name);
+ void DoBindAttribLocation(GLuint client_id,
+ GLuint index,
+ const std::string& name);
+
+ error::Error DoBindFragDataLocation(GLuint program_id,
+ GLuint colorName,
+ const std::string& name);
+
+ error::Error DoBindFragDataLocationIndexed(GLuint program_id,
+ GLuint colorName,
+ GLuint index,
+ const std::string& name);
+
+ void DoBindUniformLocationCHROMIUM(GLuint client_id,
+ GLint location,
+ const std::string& name);
error::Error GetAttribLocationHelper(
GLuint client_id, uint32 location_shm_id, uint32 location_shm_offset,
@@ -1216,6 +1226,11 @@ class GLES2DecoderImpl : public GLES2Decoder, public ErrorStateClient {
GLuint client_id, uint32 location_shm_id, uint32 location_shm_offset,
const std::string& name_str);
+ error::Error GetFragDataIndexHelper(GLuint program_id,
+ uint32 index_shm_id,
+ uint32 index_shm_offset,
+ const std::string& name_str);
+
// Wrapper for glShaderSource.
void DoShaderSource(
GLuint client_id, GLsizei count, const char** data, const GLint* length);
@@ -1859,7 +1874,8 @@ class GLES2DecoderImpl : public GLES2Decoder, public ErrorStateClient {
void DoBindFragmentInputLocationCHROMIUM(GLuint program_id,
GLint location,
- const char* name);
+ const std::string& name);
+
// Generate a member function prototype for each command in an automated and
// typesafe way.
#define GLES2_CMD_OP(name) \
@@ -3178,6 +3194,7 @@ bool GLES2DecoderImpl::InitializeShaderTranslator() {
resources.MaxDrawBuffers = group_->max_draw_buffers();
resources.MaxExpressionComplexity = 256;
resources.MaxCallStackDepth = 256;
+ resources.MaxDualSourceDrawBuffers = group_->max_dual_source_draw_buffers();
GLint range[2] = { 0, 0 };
GLint precision = 0;
@@ -3210,6 +3227,8 @@ bool GLES2DecoderImpl::InitializeShaderTranslator() {
features().ext_shader_texture_lod ? 1 : 0;
resources.NV_draw_buffers =
features().nv_draw_buffers ? 1 : 0;
+ resources.EXT_blend_func_extended =
+ features().ext_blend_func_extended ? 1 : 0;
}
ShShaderSpec shader_spec;
@@ -5485,6 +5504,12 @@ bool GLES2DecoderImpl::GetHelper(
params[0] = group_->bind_generates_resource() ? 1 : 0;
}
return true;
+ case GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT:
+ *num_written = 1;
+ if (params) {
+ params[0] = group_->max_dual_source_draw_buffers();
+ }
+ return true;
default:
if (pname >= GL_DRAW_BUFFER0_ARB &&
pname < GL_DRAW_BUFFER0_ARB + group_->max_draw_buffers()) {
@@ -5614,14 +5639,15 @@ void GLES2DecoderImpl::DoGetBufferParameteriv(
&state_, target, pname, params);
}
-void GLES2DecoderImpl::DoBindAttribLocation(
- GLuint program_id, GLuint index, const char* name) {
+void GLES2DecoderImpl::DoBindAttribLocation(GLuint program_id,
+ GLuint index,
+ const std::string& name) {
if (!StringIsValidForGLES(name)) {
LOCAL_SET_GL_ERROR(
GL_INVALID_VALUE, "glBindAttribLocation", "Invalid character");
return;
}
- if (ProgramManager::IsInvalidPrefix(name, strlen(name))) {
+ if (ProgramManager::HasBuiltInPrefix(name)) {
LOCAL_SET_GL_ERROR(
GL_INVALID_OPERATION, "glBindAttribLocation", "reserved prefix");
return;
@@ -5643,7 +5669,7 @@ void GLES2DecoderImpl::DoBindAttribLocation(
// Program::ExecuteBindAttribLocationCalls() right before link.
program->SetAttribLocationBinding(name, static_cast<GLint>(index));
// TODO(zmo): Get rid of the following glBindAttribLocation call.
- glBindAttribLocation(program->service_id(), index, name);
+ glBindAttribLocation(program->service_id(), index, name.c_str());
}
error::Error GLES2DecoderImpl::HandleBindAttribLocationBucket(
@@ -5661,19 +5687,121 @@ error::Error GLES2DecoderImpl::HandleBindAttribLocationBucket(
if (!bucket->GetAsString(&name_str)) {
return error::kInvalidArguments;
}
- DoBindAttribLocation(program, index, name_str.c_str());
+ DoBindAttribLocation(program, index, name_str);
+ return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::DoBindFragDataLocation(GLuint program_id,
+ GLuint colorName,
+ const std::string& name) {
+ const char kFunctionName[] = "glBindFragDataLocationEXT";
+ if (!StringIsValidForGLES(name)) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "invalid character");
+ return error::kNoError;
+ }
+ if (ProgramManager::HasBuiltInPrefix(name)) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, "reserved prefix");
+ return error::kNoError;
+ }
+ if (colorName >= group_->max_draw_buffers()) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName,
+ "colorName out of range");
+ return error::kNoError;
+ }
+ Program* program = GetProgramInfoNotShader(program_id, kFunctionName);
+ if (!program) {
+ return error::kNoError;
+ }
+ program->SetProgramOutputLocationBinding(name, colorName);
return error::kNoError;
}
-void GLES2DecoderImpl::DoBindUniformLocationCHROMIUM(
- GLuint program_id, GLint location, const char* name) {
+error::Error GLES2DecoderImpl::HandleBindFragDataLocationEXTBucket(
+ uint32 immediate_data_size,
+ const void* cmd_data) {
+ if (!features().ext_blend_func_extended) {
+ return error::kUnknownCommand;
+ }
+ const gles2::cmds::BindFragDataLocationEXTBucket& c =
+ *static_cast<const gles2::cmds::BindFragDataLocationEXTBucket*>(cmd_data);
+ GLuint program = static_cast<GLuint>(c.program);
+ GLuint colorNumber = static_cast<GLuint>(c.colorNumber);
+ Bucket* bucket = GetBucket(c.name_bucket_id);
+ if (!bucket || bucket->size() == 0) {
+ return error::kInvalidArguments;
+ }
+ std::string name_str;
+ if (!bucket->GetAsString(&name_str)) {
+ return error::kInvalidArguments;
+ }
+ return DoBindFragDataLocation(program, colorNumber, name_str);
+}
+
+error::Error GLES2DecoderImpl::DoBindFragDataLocationIndexed(
+ GLuint program_id,
+ GLuint colorName,
+ GLuint index,
+ const std::string& name) {
+ const char kFunctionName[] = "glBindFragDataLocationIndexEXT";
+ if (!StringIsValidForGLES(name)) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "invalid character");
+ return error::kNoError;
+ }
+ if (ProgramManager::HasBuiltInPrefix(name)) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, "reserved prefix");
+ return error::kNoError;
+ }
+ if (index != 0 && index != 1) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "index out of range");
+ return error::kNoError;
+ }
+ if ((index == 0 && colorName >= group_->max_draw_buffers()) ||
+ (index == 1 && colorName >= group_->max_dual_source_draw_buffers())) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName,
+ "colorName out of range for the color index");
+ return error::kNoError;
+ }
+ Program* program = GetProgramInfoNotShader(program_id, kFunctionName);
+ if (!program) {
+ return error::kNoError;
+ }
+ program->SetProgramOutputLocationIndexedBinding(name, colorName, index);
+ return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandleBindFragDataLocationIndexedEXTBucket(
+ uint32 immediate_data_size,
+ const void* cmd_data) {
+ if (!features().ext_blend_func_extended) {
+ return error::kUnknownCommand;
+ }
+ const gles2::cmds::BindFragDataLocationIndexedEXTBucket& c =
+ *static_cast<const gles2::cmds::BindFragDataLocationIndexedEXTBucket*>(
+ cmd_data);
+ GLuint program = static_cast<GLuint>(c.program);
+ GLuint colorNumber = static_cast<GLuint>(c.colorNumber);
+ GLuint index = static_cast<GLuint>(c.index);
+ Bucket* bucket = GetBucket(c.name_bucket_id);
+ if (!bucket || bucket->size() == 0) {
+ return error::kInvalidArguments;
+ }
+ std::string name_str;
+ if (!bucket->GetAsString(&name_str)) {
+ return error::kInvalidArguments;
+ }
+ return DoBindFragDataLocationIndexed(program, colorNumber, index, name_str);
+}
+
+void GLES2DecoderImpl::DoBindUniformLocationCHROMIUM(GLuint program_id,
+ GLint location,
+ const std::string& name) {
if (!StringIsValidForGLES(name)) {
LOCAL_SET_GL_ERROR(
GL_INVALID_VALUE,
"glBindUniformLocationCHROMIUM", "Invalid character");
return;
}
- if (ProgramManager::IsInvalidPrefix(name, strlen(name))) {
+ if (ProgramManager::HasBuiltInPrefix(name)) {
LOCAL_SET_GL_ERROR(
GL_INVALID_OPERATION,
"glBindUniformLocationCHROMIUM", "reserved prefix");
@@ -5715,7 +5843,7 @@ error::Error GLES2DecoderImpl::HandleBindUniformLocationCHROMIUMBucket(
if (!bucket->GetAsString(&name_str)) {
return error::kInvalidArguments;
}
- DoBindUniformLocationCHROMIUM(program, location, name_str.c_str());
+ DoBindUniformLocationCHROMIUM(program, location, name_str);
return error::kNoError;
}
@@ -9360,7 +9488,7 @@ error::Error GLES2DecoderImpl::HandleScheduleCALayerCHROMIUM(
error::Error GLES2DecoderImpl::GetAttribLocationHelper(
GLuint client_id, uint32 location_shm_id, uint32 location_shm_offset,
const std::string& name_str) {
- if (!StringIsValidForGLES(name_str.c_str())) {
+ if (!StringIsValidForGLES(name_str)) {
LOCAL_SET_GL_ERROR(
GL_INVALID_VALUE, "glGetAttribLocation", "Invalid character");
return error::kNoError;
@@ -9408,7 +9536,7 @@ error::Error GLES2DecoderImpl::HandleGetAttribLocation(
error::Error GLES2DecoderImpl::GetUniformLocationHelper(
GLuint client_id, uint32 location_shm_id, uint32 location_shm_offset,
const std::string& name_str) {
- if (!StringIsValidForGLES(name_str.c_str())) {
+ if (!StringIsValidForGLES(name_str)) {
LOCAL_SET_GL_ERROR(
GL_INVALID_VALUE, "glGetUniformLocation", "Invalid character");
return error::kNoError;
@@ -9508,6 +9636,7 @@ error::Error GLES2DecoderImpl::HandleGetUniformIndices(
error::Error GLES2DecoderImpl::GetFragDataLocationHelper(
GLuint client_id, uint32 location_shm_id, uint32 location_shm_offset,
const std::string& name_str) {
+ const char kFunctionName[] = "glGetFragDataLocation";
GLint* location = GetSharedMemoryAs<GLint*>(
location_shm_id, location_shm_offset, sizeof(GLint));
if (!location) {
@@ -9517,12 +9646,17 @@ error::Error GLES2DecoderImpl::GetFragDataLocationHelper(
if (*location != -1) {
return error::kInvalidArguments;
}
- Program* program = GetProgramInfoNotShader(
- client_id, "glGetFragDataLocation");
+ Program* program = GetProgramInfoNotShader(client_id, kFunctionName);
if (!program) {
return error::kNoError;
}
- *location = glGetFragDataLocation(program->service_id(), name_str.c_str());
+ if (!program->IsValid()) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "program not linked");
+ return error::kNoError;
+ }
+
+ *location = program->GetFragDataLocation(name_str);
return error::kNoError;
}
@@ -9545,6 +9679,55 @@ error::Error GLES2DecoderImpl::HandleGetFragDataLocation(
c.program, c.location_shm_id, c.location_shm_offset, name_str);
}
+error::Error GLES2DecoderImpl::GetFragDataIndexHelper(
+ GLuint program_id,
+ uint32 index_shm_id,
+ uint32 index_shm_offset,
+ const std::string& name_str) {
+ const char kFunctionName[] = "glGetFragDataIndexEXT";
+ GLint* index =
+ GetSharedMemoryAs<GLint*>(index_shm_id, index_shm_offset, sizeof(GLint));
+ if (!index) {
+ return error::kOutOfBounds;
+ }
+ // Check that the client initialized the result.
+ if (*index != -1) {
+ return error::kInvalidArguments;
+ }
+ Program* program = GetProgramInfoNotShader(program_id, kFunctionName);
+ if (!program) {
+ return error::kNoError;
+ }
+ if (!program->IsValid()) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "program not linked");
+ return error::kNoError;
+ }
+
+ *index = program->GetFragDataIndex(name_str);
+ return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandleGetFragDataIndexEXT(
+ uint32 immediate_data_size,
+ const void* cmd_data) {
+ if (!features().ext_blend_func_extended) {
+ return error::kUnknownCommand;
+ }
+ const gles2::cmds::GetFragDataIndexEXT& c =
+ *static_cast<const gles2::cmds::GetFragDataIndexEXT*>(cmd_data);
+ Bucket* bucket = GetBucket(c.name_bucket_id);
+ if (!bucket) {
+ return error::kInvalidArguments;
+ }
+ std::string name_str;
+ if (!bucket->GetAsString(&name_str)) {
+ return error::kInvalidArguments;
+ }
+ return GetFragDataIndexHelper(c.program, c.index_shm_id, c.index_shm_offset,
+ name_str);
+}
+
error::Error GLES2DecoderImpl::HandleGetUniformBlockIndex(
uint32 immediate_data_size, const void* cmd_data) {
if (!unsafe_es3_apis_enabled())
@@ -15288,23 +15471,24 @@ GLES2DecoderImpl::HandleStencilThenCoverStrokePathInstancedCHROMIUM(
return error::kNoError;
}
-void GLES2DecoderImpl::DoBindFragmentInputLocationCHROMIUM(GLuint program_id,
- GLint location,
- const char* name) {
+void GLES2DecoderImpl::DoBindFragmentInputLocationCHROMIUM(
+ GLuint program_id,
+ GLint location,
+ const std::string& name) {
static const char kFunctionName[] = "glBindFragmentInputLocationCHROMIUM";
- Program* program = GetProgram(program_id);
- if (!program || program->IsDeleted()) {
- LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, "invalid program");
- return;
- }
if (!StringIsValidForGLES(name)) {
LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "invalid character");
return;
}
- if (ProgramManager::IsInvalidPrefix(name, strlen(name))) {
+ if (ProgramManager::HasBuiltInPrefix(name)) {
LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, "reserved prefix");
return;
}
+ Program* program = GetProgram(program_id);
+ if (!program || program->IsDeleted()) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, "invalid program");
+ return;
+ }
if (location < 0 ||
static_cast<uint32>(location) >= group_->max_varying_vectors() * 4) {
LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName,
@@ -15335,7 +15519,7 @@ error::Error GLES2DecoderImpl::HandleBindFragmentInputLocationCHROMIUMBucket(
if (!bucket->GetAsString(&name_str)) {
return error::kInvalidArguments;
}
- DoBindFragmentInputLocationCHROMIUM(program, location, name_str.c_str());
+ DoBindFragmentInputLocationCHROMIUM(program, location, name_str);
return error::kNoError;
}
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
index 3f6d20d..c09c658 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
@@ -1533,6 +1533,17 @@ TEST_P(GLES2DecoderDoCommandsTest, DoCommandsBadArgSize) {
EXPECT_EQ(entries_per_cmd_ + cmds_[1].header.size, num_processed);
}
+void GLES3DecoderWithESSL3ShaderTest::SetUp() {
+ base::CommandLine command_line(0, nullptr);
+ command_line.AppendSwitch(switches::kEnableUnsafeES3APIs);
+ InitState init;
+ init.gl_version = "OpenGL ES 3.0";
+ init.bind_generates_resource = true;
+ init.context_type = CONTEXT_TYPE_OPENGLES3;
+ InitDecoderWithCommandLine(init, &command_line);
+ SetupDefaultProgram();
+}
+
INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderTest, ::testing::Bool());
INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderWithShaderTest, ::testing::Bool());
@@ -1547,5 +1558,9 @@ INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderDoCommandsTest, ::testing::Bool());
INSTANTIATE_TEST_CASE_P(Service, GLES3DecoderTest, ::testing::Bool());
+INSTANTIATE_TEST_CASE_P(Service,
+ GLES3DecoderWithESSL3ShaderTest,
+ ::testing::Bool());
+
} // namespace gles2
} // namespace gpu
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.h
index a57903c..4cab05c 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.h
@@ -82,6 +82,12 @@ class GLES3DecoderTest : public GLES2DecoderTest {
void SetUp() override;
};
+class GLES3DecoderWithESSL3ShaderTest : public GLES2DecoderWithShaderTestBase {
+ public:
+ GLES3DecoderWithESSL3ShaderTest() { shader_language_version_ = 300; }
+ void SetUp() override;
+};
+
} // namespace gles2
} // namespace gpu
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index c704c28..b149135 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -120,7 +120,8 @@ GLES2DecoderTestBase::GLES2DecoderTestBase()
cached_color_mask_alpha_(true),
cached_depth_mask_(true),
cached_stencil_front_mask_(static_cast<GLuint>(-1)),
- cached_stencil_back_mask_(static_cast<GLuint>(-1)) {
+ cached_stencil_back_mask_(static_cast<GLuint>(-1)),
+ shader_language_version_(100) {
memset(immediate_buffer_, 0xEE, sizeof(immediate_buffer_));
}
@@ -1498,7 +1499,10 @@ const GLenum GLES2DecoderTestBase::kUniform7Type;
const GLenum GLES2DecoderTestBase::kUniformCubemapType;
const GLint GLES2DecoderTestBase::kInvalidUniformLocation;
const GLint GLES2DecoderTestBase::kBadUniformIndex;
-
+const GLint GLES2DecoderTestBase::kOutputVariable1Size;
+const GLenum GLES2DecoderTestBase::kOutputVariable1Type;
+const GLuint GLES2DecoderTestBase::kOutputVariable1ColorName;
+const GLuint GLES2DecoderTestBase::kOutputVariable1Index;
#endif
const char* GLES2DecoderTestBase::kAttrib1Name = "attrib1";
@@ -1512,6 +1516,9 @@ const char* GLES2DecoderTestBase::kUniform5Name = "uniform5";
const char* GLES2DecoderTestBase::kUniform6Name = "uniform6";
const char* GLES2DecoderTestBase::kUniform7Name = "uniform7";
+const char* GLES2DecoderTestBase::kOutputVariable1Name = "gl_FragColor";
+const char* GLES2DecoderTestBase::kOutputVariable1NameESSL3 = "color";
+
void GLES2DecoderTestBase::SetupDefaultProgram() {
{
static AttribInfo attribs[] = {
@@ -1660,6 +1667,19 @@ void GLES2DecoderTestBase::SetupShader(
GLuint program_client_id, GLuint program_service_id,
GLuint vertex_shader_client_id, GLuint vertex_shader_service_id,
GLuint fragment_shader_client_id, GLuint fragment_shader_service_id) {
+ static TestHelper::ProgramOutputInfo kProgramOutputsESSL1[] = {{
+ kOutputVariable1Name, kOutputVariable1Size, kOutputVariable1Type,
+ kOutputVariable1ColorName, kOutputVariable1Index,
+ }};
+ static TestHelper::ProgramOutputInfo kProgramOutputsESSL3[] = {{
+ kOutputVariable1NameESSL3, kOutputVariable1Size, kOutputVariable1Type,
+ kOutputVariable1ColorName, kOutputVariable1Index,
+ }};
+ TestHelper::ProgramOutputInfo* program_outputs =
+ shader_language_version_ == 100 ? kProgramOutputsESSL1
+ : kProgramOutputsESSL3;
+ const size_t kNumProgramOutputs = 1;
+
{
InSequence s;
@@ -1671,9 +1691,11 @@ void GLES2DecoderTestBase::SetupShader(
AttachShader(program_service_id, fragment_shader_service_id))
.Times(1)
.RetiresOnSaturation();
- TestHelper::SetupShaderExpectations(gl_.get(), group_->feature_info(),
- attribs, num_attribs, uniforms,
- num_uniforms, program_service_id);
+
+ TestHelper::SetupShaderExpectationsWithVaryings(
+ gl_.get(), group_->feature_info(), attribs, num_attribs, uniforms,
+ num_uniforms, nullptr, 0, program_outputs, kNumProgramOutputs,
+ program_service_id);
}
DoCreateShader(
@@ -1682,10 +1704,20 @@ void GLES2DecoderTestBase::SetupShader(
GL_FRAGMENT_SHADER, fragment_shader_client_id,
fragment_shader_service_id);
- TestHelper::SetShaderStates(
- gl_.get(), GetShader(vertex_shader_client_id), true);
- TestHelper::SetShaderStates(
- gl_.get(), GetShader(fragment_shader_client_id), true);
+ TestHelper::SetShaderStates(gl_.get(), GetShader(vertex_shader_client_id),
+ true, nullptr, nullptr, &shader_language_version_,
+ nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr);
+
+ OutputVariableList frag_output_variable_list;
+ frag_output_variable_list.push_back(TestHelper::ConstructOutputVariable(
+ program_outputs[0].type, program_outputs[0].size, GL_MEDIUM_FLOAT, true,
+ program_outputs[0].name));
+
+ TestHelper::SetShaderStates(gl_.get(), GetShader(fragment_shader_client_id),
+ true, nullptr, nullptr, &shader_language_version_,
+ nullptr, nullptr, nullptr, nullptr,
+ &frag_output_variable_list, nullptr);
cmds::AttachShader attach_cmd;
attach_cmd.Init(program_client_id, vertex_shader_client_id);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
index d19937d..7746969 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
@@ -584,6 +584,13 @@ class GLES2DecoderTestBase : public ::testing::TestWithParam<bool> {
static const GLint kInvalidUniformLocation = 30;
static const GLint kBadUniformIndex = 1000;
+ static const GLint kOutputVariable1Size = 0;
+ static const GLenum kOutputVariable1Type = GL_FLOAT_VEC4;
+ static const GLuint kOutputVariable1ColorName = 7;
+ static const GLuint kOutputVariable1Index = 0;
+ static const char* kOutputVariable1Name;
+ static const char* kOutputVariable1NameESSL3;
+
// Use StrictMock to make 100% sure we know how GL will be called.
scoped_ptr< ::testing::StrictMock< ::gfx::MockGLInterface> > gl_;
scoped_refptr<gfx::GLSurfaceStub> surface_;
@@ -642,6 +649,8 @@ class GLES2DecoderTestBase : public ::testing::TestWithParam<bool> {
EnableFlags enable_flags_;
+ int shader_language_version_;
+
private:
class MockCommandBufferEngine : public CommandBufferEngine {
public:
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
index c0b6de5..d295183 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
@@ -520,6 +520,26 @@ INSTANTIATE_TEST_CASE_P(Service,
GLES2DecoderTestWithEXTMultisampleCompatibility,
::testing::Bool());
+class GLES2DecoderTestWithBlendFuncExtended : public GLES2DecoderTest {
+ public:
+ GLES2DecoderTestWithBlendFuncExtended() {}
+ void SetUp() override {
+ InitState init;
+ init.gl_version = "opengl es 3.0";
+ init.has_alpha = true;
+ init.has_depth = true;
+ init.request_alpha = true;
+ init.request_depth = true;
+ init.bind_generates_resource = true;
+ init.extensions = "GL_EXT_blend_func_extended";
+ InitDecoder(init);
+ }
+};
+INSTANTIATE_TEST_CASE_P(Service,
+ GLES2DecoderTestWithBlendFuncExtended,
+ ::testing::Bool());
+
+
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, GenDeletePaths) {
static GLuint kFirstClientID = client_path_id_ + 88;
static GLsizei kPathCount = 58;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_programs.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_programs.cc
index 8d67537..cb49ae9 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_programs.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_programs.cc
@@ -2016,27 +2016,22 @@ TEST_P(GLES2DecoderWithShaderTest, GetAttribLocationInvalidArgs) {
EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
}
-TEST_P(GLES2DecoderWithShaderTest, GetFragDataLocation) {
+TEST_P(GLES3DecoderWithESSL3ShaderTest, GetFragDataLocation) {
const uint32 kBucketId = 123;
- const GLint kLocation = 10;
- const char* kName = "color";
typedef GetFragDataLocation::Result Result;
Result* result = GetSharedMemoryAs<Result*>();
- SetBucketAsCString(kBucketId, kName);
+ SetBucketAsCString(kBucketId, kOutputVariable1NameESSL3);
*result = -1;
GetFragDataLocation cmd;
cmd.Init(client_program_id_, kBucketId, kSharedMemoryId, kSharedMemoryOffset);
- EXPECT_CALL(*gl_, GetFragDataLocation(kServiceProgramId, StrEq(kName)))
- .WillOnce(Return(kLocation))
- .RetiresOnSaturation();
decoder_->set_unsafe_es3_apis_enabled(true);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
- EXPECT_EQ(kLocation, *result);
+ EXPECT_EQ(static_cast<GLint>(kOutputVariable1ColorName), *result);
decoder_->set_unsafe_es3_apis_enabled(false);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
-TEST_P(GLES2DecoderWithShaderTest, GetFragDataLocationInvalidArgs) {
+TEST_P(GLES3DecoderWithESSL3ShaderTest, GetFragDataLocationInvalidArgs) {
const uint32 kBucketId = 123;
typedef GetFragDataLocation::Result Result;
Result* result = GetSharedMemoryAs<Result*>();
diff --git a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
index 981d100..7a201b7 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
@@ -363,7 +363,11 @@ static const GLenum valid_hint_target_table_es3[] = {
};
static const GLenum valid_image_internal_format_table[] = {
- GL_RGB, GL_RGB_YUV_420_CHROMIUM, GL_RGB_YCBCR_422_CHROMIUM, GL_RGBA,
+ GL_RGB,
+ GL_RGB_YUV_420_CHROMIUM,
+ GL_RGB_YCBCR_422_CHROMIUM,
+ GL_RGB_YCBCR_420V_CHROMIUM,
+ GL_RGBA,
};
static const GLenum valid_image_usage_table[] = {
diff --git a/gpu/command_buffer/service/memory_program_cache.cc b/gpu/command_buffer/service/memory_program_cache.cc
index 74809a9..1841348 100644
--- a/gpu/command_buffer/service/memory_program_cache.cc
+++ b/gpu/command_buffer/service/memory_program_cache.cc
@@ -40,12 +40,6 @@ namespace gles2 {
namespace {
-enum ShaderMapType {
- ATTRIB_MAP = 0,
- UNIFORM_MAP,
- VARYING_MAP
-};
-
void FillShaderVariableProto(
ShaderVariableProto* proto, const sh::ShaderVariable& variable) {
proto->set_type(variable.type);
@@ -79,6 +73,12 @@ void FillShaderVaryingProto(
proto->set_is_invariant(varying.isInvariant);
}
+void FillShaderOutputVariableProto(ShaderOutputVariableProto* proto,
+ const sh::OutputVariable& attrib) {
+ FillShaderVariableProto(proto->mutable_basic(), attrib);
+ proto->set_location(attrib.location);
+}
+
void FillShaderProto(ShaderProto* proto, const char* sha,
const Shader* shader) {
proto->set_sha(sha, gpu::gles2::ProgramCache::kHashLength);
@@ -97,6 +97,11 @@ void FillShaderProto(ShaderProto* proto, const char* sha,
ShaderVaryingProto* info = proto->add_varyings();
FillShaderVaryingProto(info, iter->second);
}
+ for (auto iter = shader->output_variable_list().begin();
+ iter != shader->output_variable_list().end(); ++iter) {
+ ShaderOutputVariableProto* info = proto->add_output_variables();
+ FillShaderOutputVariableProto(info, *iter);
+ }
}
void RetrieveShaderVariableInfo(
@@ -138,6 +143,14 @@ void RetrieveShaderVaryingInfo(
(*map)[proto.basic().mapped_name()] = varying;
}
+void RetrieveShaderOutputVariableInfo(const ShaderOutputVariableProto& proto,
+ OutputVariableList* list) {
+ sh::OutputVariable output_variable;
+ RetrieveShaderVariableInfo(proto.basic(), &output_variable);
+ output_variable.location = proto.location();
+ list->push_back(output_variable);
+}
+
void RunShaderCallback(const ShaderCacheCallback& callback,
GpuProgramProto* proto,
std::string sha_string) {
@@ -213,9 +226,11 @@ ProgramCache::ProgramLoadResult MemoryProgramCache::LoadLinkedProgram(
shader_a->set_attrib_map(value->attrib_map_0());
shader_a->set_uniform_map(value->uniform_map_0());
shader_a->set_varying_map(value->varying_map_0());
+ shader_a->set_output_variable_list(value->output_variable_list_0());
shader_b->set_attrib_map(value->attrib_map_1());
shader_b->set_uniform_map(value->uniform_map_1());
shader_b->set_varying_map(value->varying_map_1());
+ shader_b->set_output_variable_list(value->output_variable_list_1());
if (!shader_callback.is_null() &&
!base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -302,20 +317,14 @@ void MemoryProgramCache::SaveLinkedProgram(
RunShaderCallback(shader_callback, proto.get(), sha_string);
}
- store_.Put(sha_string,
- new ProgramCacheValue(length,
- format,
- binary.release(),
- sha_string,
- a_sha,
- shader_a->attrib_map(),
- shader_a->uniform_map(),
- shader_a->varying_map(),
- b_sha,
- shader_b->attrib_map(),
- shader_b->uniform_map(),
- shader_b->varying_map(),
- this));
+ store_.Put(
+ sha_string,
+ new ProgramCacheValue(
+ length, format, binary.release(), sha_string, a_sha,
+ shader_a->attrib_map(), shader_a->uniform_map(),
+ shader_a->varying_map(), shader_a->output_variable_list(), b_sha,
+ shader_b->attrib_map(), shader_b->uniform_map(),
+ shader_b->varying_map(), shader_b->output_variable_list(), this));
UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb",
curr_size_bytes_ / 1024);
@@ -327,6 +336,7 @@ void MemoryProgramCache::LoadProgram(const std::string& program) {
AttributeMap vertex_attribs;
UniformMap vertex_uniforms;
VaryingMap vertex_varyings;
+ OutputVariableList vertex_output_variables;
for (int i = 0; i < proto->vertex_shader().attribs_size(); i++) {
RetrieveShaderAttributeInfo(proto->vertex_shader().attribs(i),
&vertex_attribs);
@@ -339,10 +349,15 @@ void MemoryProgramCache::LoadProgram(const std::string& program) {
RetrieveShaderVaryingInfo(proto->vertex_shader().varyings(i),
&vertex_varyings);
}
+ for (int i = 0; i < proto->vertex_shader().output_variables_size(); i++) {
+ RetrieveShaderOutputVariableInfo(
+ proto->vertex_shader().output_variables(i), &vertex_output_variables);
+ }
AttributeMap fragment_attribs;
UniformMap fragment_uniforms;
VaryingMap fragment_varyings;
+ OutputVariableList fragment_output_variables;
for (int i = 0; i < proto->fragment_shader().attribs_size(); i++) {
RetrieveShaderAttributeInfo(proto->fragment_shader().attribs(i),
&fragment_attribs);
@@ -355,24 +370,24 @@ void MemoryProgramCache::LoadProgram(const std::string& program) {
RetrieveShaderVaryingInfo(proto->fragment_shader().varyings(i),
&fragment_varyings);
}
+ for (int i = 0; i < proto->fragment_shader().output_variables_size(); i++) {
+ RetrieveShaderOutputVariableInfo(
+ proto->fragment_shader().output_variables(i),
+ &fragment_output_variables);
+ }
scoped_ptr<char[]> binary(new char[proto->program().length()]);
memcpy(binary.get(), proto->program().c_str(), proto->program().length());
- store_.Put(proto->sha(),
- new ProgramCacheValue(proto->program().length(),
- proto->format(),
- binary.release(),
- proto->sha(),
- proto->vertex_shader().sha().c_str(),
- vertex_attribs,
- vertex_uniforms,
- vertex_varyings,
- proto->fragment_shader().sha().c_str(),
- fragment_attribs,
- fragment_uniforms,
- fragment_varyings,
- this));
+ store_.Put(
+ proto->sha(),
+ new ProgramCacheValue(
+ proto->program().length(), proto->format(), binary.release(),
+ proto->sha(), proto->vertex_shader().sha().c_str(), vertex_attribs,
+ vertex_uniforms, vertex_varyings, vertex_output_variables,
+ proto->fragment_shader().sha().c_str(), fragment_attribs,
+ fragment_uniforms, fragment_varyings, fragment_output_variables,
+ this));
UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb",
curr_size_bytes_ / 1024);
@@ -390,10 +405,12 @@ MemoryProgramCache::ProgramCacheValue::ProgramCacheValue(
const AttributeMap& attrib_map_0,
const UniformMap& uniform_map_0,
const VaryingMap& varying_map_0,
+ const OutputVariableList& output_variable_list_0,
const char* shader_1_hash,
const AttributeMap& attrib_map_1,
const UniformMap& uniform_map_1,
const VaryingMap& varying_map_1,
+ const OutputVariableList& output_variable_list_1,
MemoryProgramCache* program_cache)
: length_(length),
format_(format),
@@ -403,10 +420,12 @@ MemoryProgramCache::ProgramCacheValue::ProgramCacheValue(
attrib_map_0_(attrib_map_0),
uniform_map_0_(uniform_map_0),
varying_map_0_(varying_map_0),
+ output_variable_list_0_(output_variable_list_0),
shader_1_hash_(shader_1_hash, kHashLength),
attrib_map_1_(attrib_map_1),
uniform_map_1_(uniform_map_1),
varying_map_1_(varying_map_1),
+ output_variable_list_1_(output_variable_list_1),
program_cache_(program_cache) {
program_cache_->curr_size_bytes_ += length_;
program_cache_->LinkedProgramCacheSuccess(program_hash);
diff --git a/gpu/command_buffer/service/memory_program_cache.h b/gpu/command_buffer/service/memory_program_cache.h
index 593625d..7e9b83d 100644
--- a/gpu/command_buffer/service/memory_program_cache.h
+++ b/gpu/command_buffer/service/memory_program_cache.h
@@ -57,10 +57,12 @@ class GPU_EXPORT MemoryProgramCache : public ProgramCache {
const AttributeMap& attrib_map_0,
const UniformMap& uniform_map_0,
const VaryingMap& varying_map_0,
+ const OutputVariableList& output_variable_list_0,
const char* shader_1_hash,
const AttributeMap& attrib_map_1,
const UniformMap& uniform_map_1,
const VaryingMap& varying_map_1,
+ const OutputVariableList& output_variable_list_1,
MemoryProgramCache* program_cache);
GLsizei length() const {
@@ -91,6 +93,10 @@ class GPU_EXPORT MemoryProgramCache : public ProgramCache {
return varying_map_0_;
}
+ const OutputVariableList& output_variable_list_0() const {
+ return output_variable_list_0_;
+ }
+
const std::string& shader_1_hash() const {
return shader_1_hash_;
}
@@ -107,6 +113,10 @@ class GPU_EXPORT MemoryProgramCache : public ProgramCache {
return varying_map_1_;
}
+ const OutputVariableList& output_variable_list_1() const {
+ return output_variable_list_1_;
+ }
+
private:
friend class base::RefCounted<ProgramCacheValue>;
@@ -120,10 +130,12 @@ class GPU_EXPORT MemoryProgramCache : public ProgramCache {
const AttributeMap attrib_map_0_;
const UniformMap uniform_map_0_;
const VaryingMap varying_map_0_;
+ const OutputVariableList output_variable_list_0_;
const std::string shader_1_hash_;
const AttributeMap attrib_map_1_;
const UniformMap uniform_map_1_;
const VaryingMap varying_map_1_;
+ const OutputVariableList output_variable_list_1_;
MemoryProgramCache* const program_cache_;
DISALLOW_COPY_AND_ASSIGN(ProgramCacheValue);
diff --git a/gpu/command_buffer/service/memory_program_cache_unittest.cc b/gpu/command_buffer/service/memory_program_cache_unittest.cc
index a005db9..f92860f 100644
--- a/gpu/command_buffer/service/memory_program_cache_unittest.cc
+++ b/gpu/command_buffer/service/memory_program_cache_unittest.cc
@@ -100,9 +100,11 @@ class MemoryProgramCacheTest : public GpuServiceTest {
AttributeMap vertex_attrib_map;
UniformMap vertex_uniform_map;
VaryingMap vertex_varying_map;
+ OutputVariableList vertex_output_variable_list;
AttributeMap fragment_attrib_map;
UniformMap fragment_uniform_map;
VaryingMap fragment_varying_map;
+ OutputVariableList fragment_output_variable_list;
vertex_attrib_map["a"] = TestHelper::ConstructAttribute(
GL_FLOAT_VEC2, 34, GL_LOW_FLOAT, false, "a");
@@ -112,24 +114,28 @@ class MemoryProgramCacheTest : public GpuServiceTest {
GL_FLOAT_VEC3, 3114, GL_HIGH_FLOAT, true, "b");
vertex_varying_map["c"] = TestHelper::ConstructVarying(
GL_FLOAT_VEC4, 2, GL_HIGH_FLOAT, true, "c");
+ vertex_output_variable_list.push_back(TestHelper::ConstructOutputVariable(
+ GL_FLOAT, 0, GL_HIGH_FLOAT, true, "d"));
fragment_attrib_map["jjjbb"] = TestHelper::ConstructAttribute(
GL_FLOAT_MAT4, 1114, GL_MEDIUM_FLOAT, false, "jjjbb");
fragment_uniform_map["k"] = TestHelper::ConstructUniform(
GL_FLOAT_MAT2, 34413, GL_MEDIUM_FLOAT, true, "k");
fragment_varying_map["c"] = TestHelper::ConstructVarying(
GL_FLOAT_VEC4, 2, GL_HIGH_FLOAT, true, "c");
+ fragment_output_variable_list.push_back(TestHelper::ConstructOutputVariable(
+ GL_FLOAT, 0, GL_HIGH_FLOAT, true, "d"));
vertex_shader_->set_source("bbbalsldkdkdkd");
fragment_shader_->set_source("bbbal sldkdkdkas 134 ad");
+ TestHelper::SetShaderStates(gl_.get(), vertex_shader_, true, nullptr,
+ nullptr, nullptr, &vertex_attrib_map,
+ &vertex_uniform_map, &vertex_varying_map,
+ nullptr, &vertex_output_variable_list, nullptr);
TestHelper::SetShaderStates(
- gl_.get(), vertex_shader_, true, NULL, NULL, NULL,
- &vertex_attrib_map, &vertex_uniform_map, &vertex_varying_map,
- NULL, NULL);
- TestHelper::SetShaderStates(
- gl_.get(), fragment_shader_, true, NULL, NULL, NULL,
+ gl_.get(), fragment_shader_, true, nullptr, nullptr, nullptr,
&fragment_attrib_map, &fragment_uniform_map, &fragment_varying_map,
- NULL, NULL);
+ nullptr, &fragment_output_variable_list, nullptr);
}
void SetExpectationsForSaveLinkedProgram(
@@ -254,17 +260,22 @@ TEST_F(MemoryProgramCacheTest, CacheLoadMatchesSave) {
AttributeMap vertex_attrib_map = vertex_shader_->attrib_map();
UniformMap vertex_uniform_map = vertex_shader_->uniform_map();
VaryingMap vertex_varying_map = vertex_shader_->varying_map();
+ OutputVariableList vertex_output_variable_list =
+ vertex_shader_->output_variable_list();
AttributeMap fragment_attrib_map = fragment_shader_->attrib_map();
UniformMap fragment_uniform_map = fragment_shader_->uniform_map();
VaryingMap fragment_varying_map = fragment_shader_->varying_map();
+ OutputVariableList fragment_output_variable_list =
+ fragment_shader_->output_variable_list();
vertex_shader_->set_attrib_map(AttributeMap());
vertex_shader_->set_uniform_map(UniformMap());
vertex_shader_->set_varying_map(VaryingMap());
+ vertex_shader_->set_output_variable_list(OutputVariableList());
fragment_shader_->set_attrib_map(AttributeMap());
fragment_shader_->set_uniform_map(UniformMap());
fragment_shader_->set_varying_map(VaryingMap());
-
+ fragment_shader_->set_output_variable_list(OutputVariableList());
SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
@@ -283,9 +294,13 @@ TEST_F(MemoryProgramCacheTest, CacheLoadMatchesSave) {
EXPECT_EQ(vertex_attrib_map, vertex_shader_->attrib_map());
EXPECT_EQ(vertex_uniform_map, vertex_shader_->uniform_map());
EXPECT_EQ(vertex_varying_map, vertex_shader_->varying_map());
+ EXPECT_EQ(vertex_output_variable_list,
+ vertex_shader_->output_variable_list());
EXPECT_EQ(fragment_attrib_map, fragment_shader_->attrib_map());
EXPECT_EQ(fragment_uniform_map, fragment_shader_->uniform_map());
EXPECT_EQ(fragment_varying_map, fragment_shader_->varying_map());
+ EXPECT_EQ(fragment_output_variable_list,
+ fragment_shader_->output_variable_list());
#endif
}
@@ -309,16 +324,22 @@ TEST_F(MemoryProgramCacheTest, LoadProgramMatchesSave) {
AttributeMap vertex_attrib_map = vertex_shader_->attrib_map();
UniformMap vertex_uniform_map = vertex_shader_->uniform_map();
VaryingMap vertex_varying_map = vertex_shader_->varying_map();
+ OutputVariableList vertex_output_variable_list =
+ vertex_shader_->output_variable_list();
AttributeMap fragment_attrib_map = fragment_shader_->attrib_map();
UniformMap fragment_uniform_map = fragment_shader_->uniform_map();
VaryingMap fragment_varying_map = fragment_shader_->varying_map();
+ OutputVariableList fragment_output_variable_list =
+ fragment_shader_->output_variable_list();
vertex_shader_->set_attrib_map(AttributeMap());
vertex_shader_->set_uniform_map(UniformMap());
vertex_shader_->set_varying_map(VaryingMap());
+ vertex_shader_->set_output_variable_list(OutputVariableList());
fragment_shader_->set_attrib_map(AttributeMap());
fragment_shader_->set_uniform_map(UniformMap());
fragment_shader_->set_varying_map(VaryingMap());
+ fragment_shader_->set_output_variable_list(OutputVariableList());
SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
@@ -341,9 +362,13 @@ TEST_F(MemoryProgramCacheTest, LoadProgramMatchesSave) {
EXPECT_EQ(vertex_attrib_map, vertex_shader_->attrib_map());
EXPECT_EQ(vertex_uniform_map, vertex_shader_->uniform_map());
EXPECT_EQ(vertex_varying_map, vertex_shader_->varying_map());
+ EXPECT_EQ(vertex_output_variable_list,
+ vertex_shader_->output_variable_list());
EXPECT_EQ(fragment_attrib_map, fragment_shader_->attrib_map());
EXPECT_EQ(fragment_uniform_map, fragment_shader_->uniform_map());
EXPECT_EQ(fragment_varying_map, fragment_shader_->varying_map());
+ EXPECT_EQ(fragment_output_variable_list,
+ fragment_shader_->output_variable_list());
#endif
}
diff --git a/gpu/command_buffer/service/mocks.h b/gpu/command_buffer/service/mocks.h
index 2ef657f..a9a7efb 100644
--- a/gpu/command_buffer/service/mocks.h
+++ b/gpu/command_buffer/service/mocks.h
@@ -95,16 +95,17 @@ class MockShaderTranslator : public ShaderTranslatorInterface {
const ShBuiltInResources* resources,
ShShaderOutput shader_output_language,
ShCompileOptions driver_bug_workarounds));
- MOCK_CONST_METHOD9(Translate, bool(
- const std::string& shader_source,
- std::string* info_log,
- std::string* translated_source,
- int* shader_version,
- AttributeMap* attrib_map,
- UniformMap* uniform_map,
- VaryingMap* varying_map,
- InterfaceBlockMap* interface_block_map,
- NameMap* name_map));
+ MOCK_CONST_METHOD10(Translate,
+ bool(const std::string& shader_source,
+ std::string* info_log,
+ std::string* translated_source,
+ int* shader_version,
+ AttributeMap* attrib_map,
+ UniformMap* uniform_map,
+ VaryingMap* varying_map,
+ InterfaceBlockMap* interface_block_map,
+ OutputVariableList* output_variable_list,
+ NameMap* name_map));
MOCK_CONST_METHOD0(
GetStringForOptionsThatWouldAffectCompilation, std::string());
private:
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() {
diff --git a/gpu/command_buffer/service/program_manager.h b/gpu/command_buffer/service/program_manager.h
index 942659d..eb3be7c 100644
--- a/gpu/command_buffer/service/program_manager.h
+++ b/gpu/command_buffer/service/program_manager.h
@@ -20,6 +20,7 @@
namespace gpu {
namespace gles2 {
+class FeatureInfo;
class ProgramCache;
class ProgramManager;
class Shader;
@@ -69,6 +70,16 @@ class GPU_EXPORT Program : public base::RefCounted<Program> {
GLenum type;
GLuint location;
};
+ struct ProgramOutputInfo {
+ ProgramOutputInfo(GLuint _color_name,
+ GLuint _index,
+ const std::string& _name)
+ : color_name(_color_name), index(_index), name(_name) {}
+ ProgramOutputInfo() : color_name(0), index(0) {}
+ GLuint color_name;
+ GLuint index;
+ std::string name;
+ };
struct UniformInfo {
UniformInfo();
@@ -144,8 +155,10 @@ class GPU_EXPORT Program : public base::RefCounted<Program> {
typedef std::vector<FragmentInputInfo> FragmentInputInfoVector;
typedef std::vector<ShaderVariableLocationEntry<FragmentInputInfo>>
FragmentInputLocationVector;
+ typedef std::vector<ProgramOutputInfo> ProgramOutputInfoVector;
typedef std::vector<int> SamplerIndices;
typedef std::map<std::string, GLint> LocationMap;
+ typedef std::map<std::string, std::pair<GLuint, GLuint>> LocationIndexMap;
typedef std::vector<std::string> StringVector;
Program(ProgramManager* manager, GLuint service_id);
@@ -209,6 +222,10 @@ class GPU_EXPORT Program : public base::RefCounted<Program> {
// -1 for bound, non-existing uniform.
bool IsInactiveUniformLocationByFakeLocation(GLint fake_location) const;
+ // Gets the ProgramOutputInfo of a fragment output by name.
+ const ProgramOutputInfo* GetProgramOutputInfo(
+ const std::string& original_name) const;
+
// Gets all the program info.
void GetProgramInfo(
ProgramManager* manager, CommonDecoder::Bucket* bucket) const;
@@ -226,6 +243,14 @@ class GPU_EXPORT Program : public base::RefCounted<Program> {
// glGetProgramInfoCHROMIUM.
bool GetUniformsES3(CommonDecoder::Bucket* bucket) const;
+ // Returns the fragment shader output variable color name binding.
+ // Returns -1 if |original_name| is not an out variable or error.
+ GLint GetFragDataLocation(const std::string& original_name) const;
+
+ // Returns the fragment shader output variable color index binding.
+ // Returns -1 if |original_name| is not an out variable or error.
+ GLint GetFragDataIndex(const std::string& original_name) const;
+
// Sets the sampler values for a uniform.
// This is safe to call for any location. If the location is not
// a sampler uniform nothing will happen.
@@ -285,6 +310,15 @@ class GPU_EXPORT Program : public base::RefCounted<Program> {
// glBindFragmentInputLocationCHROMIUM() call.
void SetFragmentInputLocationBinding(const std::string& name, GLint location);
+ // Sets program output variable location. Also sets color index to zero.
+ void SetProgramOutputLocationBinding(const std::string& name,
+ GLuint colorName);
+
+ // Sets program output variable location and color index.
+ void SetProgramOutputLocationIndexedBinding(const std::string& name,
+ GLuint colorName,
+ GLuint index);
+
// Detects if there are attribute location conflicts from
// glBindAttribLocation() calls.
// We only consider the declared attributes in the program.
@@ -310,6 +344,11 @@ class GPU_EXPORT Program : public base::RefCounted<Program> {
// We only consider the statically used fragment inputs in the program.
bool DetectFragmentInputLocationBindingConflicts() const;
+ // Detects if there are program output location conflicts from
+ // glBindFragDataLocation and ..LocationIndexedEXT calls.
+ // We only consider the statically used program outputs in the program.
+ bool DetectProgramOutputLocationBindingConflicts() const;
+
// Return true if any built-in invariant matching rules are broken as in
// GLSL ES spec 1.00.17, section 4.6.4, Invariance and Linkage.
bool DetectBuiltInInvariantConflicts() const;
@@ -372,6 +411,7 @@ class GPU_EXPORT Program : public base::RefCounted<Program> {
void Update();
void UpdateUniforms();
void UpdateFragmentInputs();
+ void UpdateProgramOutputs();
// Process the program log, replacing the hashed names with original names.
std::string ProcessLogInfo(const std::string& log);
@@ -405,6 +445,8 @@ class GPU_EXPORT Program : public base::RefCounted<Program> {
// Returns false upon failure.
bool ExecuteTransformFeedbackVaryingsCall();
+ void ExecuteProgramOutputBindCalls();
+
// Query VertexAttrib data returned by ANGLE translator by the mapped name.
void GetVertexAttribData(
const std::string& name, std::string* original_name, GLenum* type) const;
@@ -447,6 +489,8 @@ class GPU_EXPORT Program : public base::RefCounted<Program> {
FragmentInputInfoVector fragment_input_infos_;
FragmentInputLocationVector fragment_input_locations_;
+ ProgramOutputInfoVector program_output_infos_;
+
// The program this Program is tracking.
GLuint service_id_;
@@ -482,6 +526,10 @@ class GPU_EXPORT Program : public base::RefCounted<Program> {
// Fragment input-location binding map from
// glBindFragmentInputLocationCHROMIUM() calls.
LocationMap bind_fragment_input_location_map_;
+
+ // output variable - (location,index) binding map from
+ // glBindFragDataLocation() and ..IndexedEXT() calls.
+ LocationIndexMap bind_program_output_location_index_map_;
};
// Tracks the Programs.
@@ -492,6 +540,7 @@ class GPU_EXPORT ProgramManager {
public:
explicit ProgramManager(ProgramCache* program_cache,
uint32 max_varying_vectors,
+ uint32 max_dual_source_draw_buffers,
FeatureInfo* feature_info);
~ProgramManager();
@@ -522,8 +571,9 @@ class GPU_EXPORT ProgramManager {
// Clears the uniforms for this program.
void ClearUniforms(Program* program);
- // Returns true if prefix is invalid for gl.
- static bool IsInvalidPrefix(const char* name, size_t length);
+ // Returns true if |name| has a prefix that is intended for GL built-in shader
+ // variables.
+ static bool HasBuiltInPrefix(const std::string& name);
// Check if a Program is owned by this ProgramManager.
bool IsOwned(Program* program) const;
@@ -534,6 +584,10 @@ class GPU_EXPORT ProgramManager {
return max_varying_vectors_;
}
+ uint32 max_dual_source_draw_buffers() const {
+ return max_dual_source_draw_buffers_;
+ }
+
private:
friend class Program;
@@ -560,6 +614,7 @@ class GPU_EXPORT ProgramManager {
ProgramCache* program_cache_;
uint32 max_varying_vectors_;
+ uint32 max_dual_source_draw_buffers_;
scoped_refptr<FeatureInfo> feature_info_;
diff --git a/gpu/command_buffer/service/program_manager_unittest.cc b/gpu/command_buffer/service/program_manager_unittest.cc
index 404017f..8343e56 100644
--- a/gpu/command_buffer/service/program_manager_unittest.cc
+++ b/gpu/command_buffer/service/program_manager_unittest.cc
@@ -21,6 +21,7 @@
#include "gpu/command_buffer/service/test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_mock.h"
+#include "ui/gl/gl_version_info.h"
using ::testing::_;
using ::testing::DoAll;
@@ -38,6 +39,8 @@ namespace gles2 {
namespace {
const uint32 kMaxVaryingVectors = 8;
+const uint32 kMaxDrawBuffers = 8;
+const uint32 kMaxDualSourceDrawBuffers = 8;
void ShaderCacheCb(const std::string& key, const std::string& shader) {}
@@ -51,8 +54,9 @@ uint32 ComputeOffset(const void* start, const void* position) {
class ProgramManagerTestBase : public GpuServiceTest {
protected:
virtual void SetupProgramManager() {
- manager_.reset(
- new ProgramManager(NULL, kMaxVaryingVectors, feature_info_.get()));
+ manager_.reset(new ProgramManager(nullptr, kMaxVaryingVectors,
+ kMaxDualSourceDrawBuffers,
+ feature_info_.get()));
}
void SetUpBase(const char* gl_version,
const char* gl_extensions,
@@ -215,6 +219,13 @@ class ProgramManagerWithShaderTest : public ProgramManagerTestBase {
static const GLint kInvalidUniformLocation = 30;
static const GLint kBadUniformIndex = 1000;
+ static const char* kOutputVariable1Name;
+ static const GLint kOutputVariable1Size = 1;
+ static const GLenum kOutputVariable1Precision = GL_MEDIUM_FLOAT;
+ static const bool kOutputVariable1StaticUse = true;
+ static const GLint kOutputVariable1Location = -1;
+ static const GLenum kOutputVariable1Type = GL_FLOAT_VEC4;
+
static const size_t kNumAttribs;
static const size_t kNumUniforms;
@@ -225,7 +236,8 @@ class ProgramManagerWithShaderTest : public ProgramManagerTestBase {
typedef enum {
kVarUniform,
kVarVarying,
- kVarAttribute
+ kVarAttribute,
+ kVarOutput,
} VarCategory;
typedef struct {
@@ -299,14 +311,16 @@ class ProgramManagerWithShaderTest : public ProgramManagerTestBase {
return (static_cast<bool>(link_status) == expected_link_status);
}
- Program* SetupShaderVariableTest(const VarInfo* vertex_variables,
- size_t vertex_variable_size,
- const VarInfo* fragment_variables,
- size_t fragment_variable_size) {
+ Program* SetupProgramForVariables(const VarInfo* vertex_variables,
+ size_t vertex_variable_size,
+ const VarInfo* fragment_variables,
+ size_t fragment_variable_size,
+ const int* const shader_version = nullptr) {
// Set up shader
AttributeMap vertex_attrib_map;
UniformMap vertex_uniform_map;
VaryingMap vertex_varying_map;
+ OutputVariableList vertex_output_variable_list;
for (size_t ii = 0; ii < vertex_variable_size; ++ii) {
switch (vertex_variables[ii].category) {
case kVarAttribute:
@@ -336,6 +350,13 @@ class ProgramManagerWithShaderTest : public ProgramManagerTestBase {
vertex_variables[ii].static_use,
vertex_variables[ii].name);
break;
+ case kVarOutput:
+ vertex_output_variable_list.push_back(
+ TestHelper::ConstructOutputVariable(
+ vertex_variables[ii].type, vertex_variables[ii].size,
+ vertex_variables[ii].precision,
+ vertex_variables[ii].static_use, vertex_variables[ii].name));
+ break;
default:
NOTREACHED();
}
@@ -344,6 +365,7 @@ class ProgramManagerWithShaderTest : public ProgramManagerTestBase {
AttributeMap frag_attrib_map;
UniformMap frag_uniform_map;
VaryingMap frag_varying_map;
+ OutputVariableList frag_output_variable_list;
for (size_t ii = 0; ii < fragment_variable_size; ++ii) {
switch (fragment_variables[ii].category) {
case kVarAttribute:
@@ -373,6 +395,14 @@ class ProgramManagerWithShaderTest : public ProgramManagerTestBase {
fragment_variables[ii].static_use,
fragment_variables[ii].name);
break;
+ case kVarOutput:
+ frag_output_variable_list.push_back(
+ TestHelper::ConstructOutputVariable(
+ fragment_variables[ii].type, fragment_variables[ii].size,
+ fragment_variables[ii].precision,
+ fragment_variables[ii].static_use,
+ fragment_variables[ii].name));
+ break;
default:
NOTREACHED();
}
@@ -386,12 +416,14 @@ class ProgramManagerWithShaderTest : public ProgramManagerTestBase {
// Check shader got created.
EXPECT_TRUE(vshader != NULL && fshader != NULL);
// Set Status
- TestHelper::SetShaderStates(
- gl_.get(), vshader, true, NULL, NULL, NULL, &vertex_attrib_map,
- &vertex_uniform_map, &vertex_varying_map, NULL, NULL);
- TestHelper::SetShaderStates(
- gl_.get(), fshader, true, NULL, NULL, NULL,
- &frag_attrib_map, &frag_uniform_map, &frag_varying_map, NULL, NULL);
+ TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr,
+ shader_version, &vertex_attrib_map,
+ &vertex_uniform_map, &vertex_varying_map,
+ nullptr, &vertex_output_variable_list, nullptr);
+ TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
+ shader_version, &frag_attrib_map,
+ &frag_uniform_map, &frag_varying_map, nullptr,
+ &frag_output_variable_list, nullptr);
// Set up program
Program* program =
@@ -502,6 +534,7 @@ const char* ProgramManagerWithShaderTest::kUniform2NameWithArrayIndex =
const char* ProgramManagerWithShaderTest::kUniform3Name = "uniform3";
const char* ProgramManagerWithShaderTest::kUniform3NameWithArrayIndex =
"uniform3[0]";
+const char* ProgramManagerWithShaderTest::kOutputVariable1Name = "outputVar1";
TEST_F(ProgramManagerWithShaderTest, GetAttribInfos) {
const Program* program = SetupDefaultProgram();
@@ -805,6 +838,7 @@ TEST_F(ProgramManagerWithShaderTest, GLDriverReturnsWrongTypeInfo) {
AttributeMap attrib_map;
UniformMap uniform_map;
VaryingMap varying_map;
+ OutputVariableList output_variable_list;
attrib_map[kAttrib1Name] = TestHelper::ConstructAttribute(
kAttrib1Type, kAttrib1Size, kAttrib1Precision,
kAttribStaticUse, kAttrib1Name);
@@ -823,18 +857,22 @@ TEST_F(ProgramManagerWithShaderTest, GLDriverReturnsWrongTypeInfo) {
uniform_map[kUniform3Name] = TestHelper::ConstructUniform(
kUniform3Type, kUniform3Size, kUniform3Precision,
kUniform3StaticUse, kUniform3Name);
+ output_variable_list.push_back(TestHelper::ConstructOutputVariable(
+ kOutputVariable1Type, kOutputVariable1Size, kOutputVariable1Precision,
+ kOutputVariable1StaticUse, kOutputVariable1Name));
+
Shader* vshader = shader_manager_.CreateShader(
kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER);
ASSERT_TRUE(vshader != NULL);
- TestHelper::SetShaderStates(
- gl_.get(), vshader, true, NULL, NULL, NULL,
- &attrib_map, &uniform_map, &varying_map, NULL, NULL);
+ TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr,
+ nullptr, &attrib_map, &uniform_map, &varying_map,
+ nullptr, &output_variable_list, nullptr);
Shader* fshader = shader_manager_.CreateShader(
kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER);
ASSERT_TRUE(fshader != NULL);
- TestHelper::SetShaderStates(
- gl_.get(), fshader, true, NULL, NULL, NULL,
- &attrib_map, &uniform_map, &varying_map, NULL, NULL);
+ TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
+ nullptr, &attrib_map, &uniform_map, &varying_map,
+ nullptr, &output_variable_list, nullptr);
static ProgramManagerWithShaderTest::AttribInfo kAttribs[] = {
{ kAttrib1Name, kAttrib1Size, kAttrib1Type, kAttrib1Location, },
{ kAttrib2Name, kAttrib2Size, kAttrib2BadType, kAttrib2Location, },
@@ -1497,9 +1535,9 @@ TEST_F(ProgramManagerWithShaderTest, BindAttribLocationConflicts) {
// Check shader got created.
ASSERT_TRUE(vshader != NULL && fshader != NULL);
// Set Status
- TestHelper::SetShaderStates(
- gl_.get(), vshader, true, NULL, NULL, NULL, &attrib_map, NULL, NULL,
- NULL, NULL);
+ TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr,
+ nullptr, &attrib_map, nullptr, nullptr, nullptr,
+ nullptr, nullptr);
// Check attrib infos got copied.
for (AttributeMap::const_iterator it = attrib_map.begin();
it != attrib_map.end(); ++it) {
@@ -1512,10 +1550,9 @@ TEST_F(ProgramManagerWithShaderTest, BindAttribLocationConflicts) {
EXPECT_EQ(it->second.staticUse, variable_info->staticUse);
EXPECT_EQ(it->second.name, variable_info->name);
}
- TestHelper::SetShaderStates(
- gl_.get(), fshader, true, NULL, NULL, NULL, &attrib_map, NULL, NULL,
- NULL, NULL);
-
+ TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
+ nullptr, &attrib_map, nullptr, nullptr, nullptr,
+ nullptr, nullptr);
// Set up program
Program* program =
manager_->CreateProgram(kClientProgramId, kServiceProgramId);
@@ -1581,13 +1618,12 @@ TEST_F(ProgramManagerWithShaderTest, UniformsPrecisionMismatch) {
// Check shader got created.
ASSERT_TRUE(vshader != NULL && fshader != NULL);
// Set Status
- TestHelper::SetShaderStates(
- gl_.get(), vshader, true, NULL, NULL, NULL, NULL,
- &vertex_uniform_map, NULL, NULL, NULL);
- TestHelper::SetShaderStates(
- gl_.get(), fshader, true, NULL, NULL, NULL, NULL,
- &frag_uniform_map, NULL, NULL, NULL);
-
+ TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr,
+ nullptr, nullptr, &vertex_uniform_map, nullptr,
+ nullptr, nullptr, nullptr);
+ TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
+ nullptr, nullptr, &frag_uniform_map, nullptr,
+ nullptr, nullptr, nullptr);
// Set up program
Program* program =
manager_->CreateProgram(kClientProgramId, kServiceProgramId);
@@ -1609,8 +1645,8 @@ TEST_F(ProgramManagerWithShaderTest, VaryingTypeMismatch) {
{ GL_FLOAT_VEC3, 1, GL_MEDIUM_FLOAT, true, "a", kVarVarying };
const VarInfo kFragmentVarying =
{ GL_FLOAT_VEC4, 1, GL_MEDIUM_FLOAT, true, "a", kVarVarying };
- Program* program = SetupShaderVariableTest(
- &kVertexVarying, 1, &kFragmentVarying, 1);
+ Program* program =
+ SetupProgramForVariables(&kVertexVarying, 1, &kFragmentVarying, 1);
std::string conflicting_name;
@@ -1626,8 +1662,8 @@ TEST_F(ProgramManagerWithShaderTest, VaryingArraySizeMismatch) {
{ GL_FLOAT, 2, GL_MEDIUM_FLOAT, true, "a", kVarVarying };
const VarInfo kFragmentVarying =
{ GL_FLOAT, 3, GL_MEDIUM_FLOAT, true, "a", kVarVarying };
- Program* program = SetupShaderVariableTest(
- &kVertexVarying, 1, &kFragmentVarying, 1);
+ Program* program =
+ SetupProgramForVariables(&kVertexVarying, 1, &kFragmentVarying, 1);
std::string conflicting_name;
@@ -1643,8 +1679,8 @@ TEST_F(ProgramManagerWithShaderTest, VaryingPrecisionMismatch) {
{ GL_FLOAT, 2, GL_HIGH_FLOAT, true, "a", kVarVarying };
const VarInfo kFragmentVarying =
{ GL_FLOAT, 2, GL_MEDIUM_FLOAT, true, "a", kVarVarying };
- Program* program = SetupShaderVariableTest(
- &kVertexVarying, 1, &kFragmentVarying, 1);
+ Program* program =
+ SetupProgramForVariables(&kVertexVarying, 1, &kFragmentVarying, 1);
std::string conflicting_name;
@@ -1658,8 +1694,7 @@ TEST_F(ProgramManagerWithShaderTest, VaryingPrecisionMismatch) {
TEST_F(ProgramManagerWithShaderTest, VaryingMissing) {
const VarInfo kFragmentVarying =
{ GL_FLOAT, 3, GL_MEDIUM_FLOAT, true, "a", kVarVarying };
- Program* program = SetupShaderVariableTest(
- NULL, 0, &kFragmentVarying, 1);
+ Program* program = SetupProgramForVariables(nullptr, 0, &kFragmentVarying, 1);
std::string conflicting_name;
@@ -1674,8 +1709,7 @@ TEST_F(ProgramManagerWithShaderTest, VaryingMissing) {
TEST_F(ProgramManagerWithShaderTest, InactiveVarying) {
const VarInfo kFragmentVarying =
{ GL_FLOAT, 3, GL_MEDIUM_FLOAT, false, "a", kVarVarying };
- Program* program = SetupShaderVariableTest(
- NULL, 0, &kFragmentVarying, 1);
+ Program* program = SetupProgramForVariables(nullptr, 0, &kFragmentVarying, 1);
std::string conflicting_name;
@@ -1692,8 +1726,8 @@ TEST_F(ProgramManagerWithShaderTest, AttribUniformNameConflict) {
{ GL_FLOAT_VEC4, 1, GL_MEDIUM_FLOAT, true, "a", kVarAttribute };
const VarInfo kFragmentUniform =
{ GL_FLOAT_VEC4, 1, GL_MEDIUM_FLOAT, true, "a", kVarUniform };
- Program* program = SetupShaderVariableTest(
- &kVertexAttribute, 1, &kFragmentUniform, 1);
+ Program* program =
+ SetupProgramForVariables(&kVertexAttribute, 1, &kFragmentUniform, 1);
std::string conflicting_name;
@@ -1712,8 +1746,8 @@ TEST_F(ProgramManagerWithShaderTest, TooManyVaryings) {
{ GL_FLOAT_VEC4, 4, GL_MEDIUM_FLOAT, true, "a", kVarVarying },
{ GL_FLOAT_VEC4, 5, GL_MEDIUM_FLOAT, true, "b", kVarVarying }
};
- Program* program = SetupShaderVariableTest(
- kVertexVaryings, 2, kFragmentVaryings, 2);
+ Program* program =
+ SetupProgramForVariables(kVertexVaryings, 2, kFragmentVaryings, 2);
EXPECT_FALSE(
program->CheckVaryingsPacking(Program::kCountOnlyStaticallyUsed));
@@ -1730,8 +1764,8 @@ TEST_F(ProgramManagerWithShaderTest, TooManyInactiveVaryings) {
{ GL_FLOAT_VEC4, 4, GL_MEDIUM_FLOAT, false, "a", kVarVarying },
{ GL_FLOAT_VEC4, 5, GL_MEDIUM_FLOAT, true, "b", kVarVarying }
};
- Program* program = SetupShaderVariableTest(
- kVertexVaryings, 2, kFragmentVaryings, 2);
+ Program* program =
+ SetupProgramForVariables(kVertexVaryings, 2, kFragmentVaryings, 2);
EXPECT_TRUE(
program->CheckVaryingsPacking(Program::kCountOnlyStaticallyUsed));
@@ -1749,8 +1783,8 @@ TEST_F(ProgramManagerWithShaderTest, CountAllVaryingsInPacking) {
{ GL_FLOAT_VEC4, 4, GL_MEDIUM_FLOAT, false, "a", kVarVarying },
{ GL_FLOAT_VEC4, 5, GL_MEDIUM_FLOAT, true, "b", kVarVarying }
};
- Program* program = SetupShaderVariableTest(
- kVertexVaryings, 2, kFragmentVaryings, 2);
+ Program* program =
+ SetupProgramForVariables(kVertexVaryings, 2, kFragmentVaryings, 2);
EXPECT_FALSE(program->CheckVaryingsPacking(Program::kCountAll));
}
@@ -1910,6 +1944,7 @@ class ProgramManagerWithCacheTest : public ProgramManagerTestBase {
protected:
void SetupProgramManager() override {
manager_.reset(new ProgramManager(cache_.get(), kMaxVaryingVectors,
+ kMaxDualSourceDrawBuffers,
feature_info_.get()));
}
@@ -1931,10 +1966,12 @@ class ProgramManagerWithCacheTest : public ProgramManagerTestBase {
program_->AttachShader(&shader_manager_, vertex_shader_);
program_->AttachShader(&shader_manager_, fragment_shader_);
}
+
void TearDown() override {
shader_manager_.Destroy(false);
ProgramManagerTestBase::TearDown();
}
+
void SetShadersCompiled() {
TestHelper::SetShaderStates(gl_.get(), vertex_shader_, true);
TestHelper::SetShaderStates(gl_.get(), fragment_shader_, true);
@@ -2019,9 +2056,9 @@ class ProgramManagerWithCacheTest : public ProgramManagerTestBase {
}
void SetExpectationsForProgramLoadSuccess(GLuint service_program_id) {
- TestHelper::SetupProgramSuccessExpectations(gl_.get(), feature_info_.get(),
- nullptr, 0, nullptr, 0, nullptr,
- 0, service_program_id);
+ TestHelper::SetupProgramSuccessExpectations(
+ gl_.get(), feature_info_.get(), nullptr, 0, nullptr, 0, nullptr, 0,
+ nullptr, 0, service_program_id);
}
void SetExpectationsForProgramLink() {
@@ -2177,7 +2214,6 @@ const char* ProgramManagerWithPathRenderingTest::kFragmentInput1Name = "color1";
const char* ProgramManagerWithPathRenderingTest::kFragmentInput2Name = "color2";
const char* ProgramManagerWithPathRenderingTest::kFragmentInput2GLName =
"color2[0]";
-
const char* ProgramManagerWithPathRenderingTest::kFragmentInput3Name = "color3";
const char* ProgramManagerWithPathRenderingTest::kFragmentInput3GLName =
"color3[0]";
@@ -2205,10 +2241,10 @@ TEST_P(ProgramManagerWithPathRenderingTest, BindFragmentInputLocation) {
kFragmentInput3StaticUse, kFragmentInput3Name);
TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr,
nullptr, nullptr, nullptr, &varying_map, nullptr,
- nullptr);
+ nullptr, nullptr);
TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
nullptr, nullptr, nullptr, &varying_map, nullptr,
- nullptr);
+ nullptr, nullptr);
Program* program =
manager_->CreateProgram(kClientProgramId, kServiceProgramId);
ASSERT_TRUE(program != NULL);
@@ -2238,7 +2274,7 @@ TEST_P(ProgramManagerWithPathRenderingTest, BindFragmentInputLocation) {
TestHelper::SetupShaderExpectationsWithVaryings(
gl_.get(), feature_info_.get(), nullptr, 0, nullptr, 0,
kFragmentInputExpectationInfos, arraysize(kFragmentInputExpectationInfos),
- kServiceProgramId);
+ nullptr, 0, kServiceProgramId);
program->Link(NULL, Program::kCountOnlyStaticallyUsed,
base::Bind(&ShaderCacheCb));
const Program::FragmentInputInfo* info1 =
@@ -2273,5 +2309,90 @@ INSTANTIATE_TEST_CASE_P(
make_gl_ext_tuple("4.5", "GL_NV_path_rendering"),
make_gl_ext_tuple("opengl es 3.1", "GL_NV_path_rendering")));
+class ProgramManagerDualSourceBlendingTest
+ : public ProgramManagerWithShaderTest,
+ public testing::WithParamInterface<
+ testing::tuple<const char*, const char*>> {
+ public:
+ ProgramManagerDualSourceBlendingTest() {}
+
+ protected:
+ void SetUpWithFeatureInfo(FeatureInfo* feature_info) {
+ const char* gl_version = testing::get<0>(GetParam());
+ const char* gl_extensions = testing::get<1>(GetParam());
+ SetUpBase(gl_version, gl_extensions, feature_info);
+ }
+
+ void SetUp() override { SetUpWithFeatureInfo(nullptr); }
+};
+
+class ProgramManagerDualSourceBlendingES2Test
+ : public ProgramManagerDualSourceBlendingTest {};
+
+TEST_P(ProgramManagerDualSourceBlendingES2Test, UseSecondaryFragCoord) {
+ DCHECK(feature_info_->feature_flags().ext_blend_func_extended);
+
+ const VarInfo kFragmentVaryings[] = {
+ {GL_FLOAT_VEC4, 0, GL_MEDIUM_FLOAT, true, "gl_SecondaryFragColorEXT",
+ kVarOutput},
+ {GL_FLOAT_VEC4, 0, GL_MEDIUM_FLOAT, true, "gl_FragColor", kVarOutput},
+ };
+
+ int shader_version = 100;
+ Program* program =
+ SetupProgramForVariables(nullptr, 0, kFragmentVaryings,
+ arraysize(kFragmentVaryings), &shader_version);
+
+ const gfx::GLVersionInfo& gl_version = feature_info_->gl_version_info();
+ if (!gl_version.is_es) {
+ // The call is expected only for OpenGL. OpenGL ES expects to
+ // output GLES SL 1.00, which does not bind.
+ EXPECT_CALL(*(gl_.get()),
+ BindFragDataLocationIndexed(kServiceProgramId, 0, 1,
+ StrEq("angle_SecondaryFragColor")))
+ .Times(1)
+ .RetiresOnSaturation();
+ }
+
+ EXPECT_TRUE(LinkAsExpected(program, true));
+}
+
+TEST_P(ProgramManagerDualSourceBlendingES2Test, UseSecondaryFragData) {
+ const VarInfo kFragmentVaryings[] = {
+ {GL_FLOAT_VEC4, kMaxDualSourceDrawBuffers, GL_MEDIUM_FLOAT, true,
+ "gl_SecondaryFragDataEXT", kVarOutput},
+ {GL_FLOAT_VEC4, kMaxDrawBuffers, GL_MEDIUM_FLOAT, true, "gl_FragData",
+ kVarOutput},
+ };
+
+ int shader_version = 100;
+ Program* program =
+ SetupProgramForVariables(nullptr, 0, kFragmentVaryings,
+ arraysize(kFragmentVaryings), &shader_version);
+
+ const gfx::GLVersionInfo& gl_version = feature_info_->gl_version_info();
+ if (!gl_version.is_es) {
+ // The call is expected only for OpenGL. OpenGL ES expects to
+ // output GLES SL 1.00, which does not bind.
+ EXPECT_CALL(*(gl_.get()),
+ BindFragDataLocationIndexed(kServiceProgramId, 0, 1,
+ StrEq("angle_SecondaryFragData")))
+ .Times(1)
+ .RetiresOnSaturation();
+ }
+
+ EXPECT_TRUE(LinkAsExpected(program, true));
+}
+
+INSTANTIATE_TEST_CASE_P(
+ SupportedContexts,
+ ProgramManagerDualSourceBlendingES2Test,
+ testing::Values(
+ make_gl_ext_tuple("3.2",
+ "GL_ARB_draw_buffers GL_ARB_blend_func_extended "
+ "GL_ARB_program_interface_query"),
+ make_gl_ext_tuple("opengl es 3.1",
+ "GL_EXT_draw_buffers GL_EXT_blend_func_extended")));
+
} // namespace gles2
} // namespace gpu
diff --git a/gpu/command_buffer/service/shader_manager.cc b/gpu/command_buffer/service/shader_manager.cc
index 342a476..22df5f7 100644
--- a/gpu/command_buffer/service/shader_manager.cc
+++ b/gpu/command_buffer/service/shader_manager.cc
@@ -69,15 +69,10 @@ void Shader::DoCompile() {
const char* source_for_driver = last_compiled_source_.c_str();
ShaderTranslatorInterface* translator = translator_.get();
if (translator) {
- bool success = translator->Translate(last_compiled_source_,
- &log_info_,
- &translated_source_,
- &shader_version_,
- &attrib_map_,
- &uniform_map_,
- &varying_map_,
- &interface_block_map_,
- &name_map_);
+ bool success = translator->Translate(
+ last_compiled_source_, &log_info_, &translated_source_,
+ &shader_version_, &attrib_map_, &uniform_map_, &varying_map_,
+ &interface_block_map_, &output_variable_list_, &name_map_);
if (!success) {
return;
}
@@ -215,6 +210,15 @@ const std::string* Shader::GetInterfaceBlockMappedName(
return NULL;
}
+const std::string* Shader::GetOutputVariableMappedName(
+ const std::string& original_name) const {
+ for (const auto& value : output_variable_list_) {
+ if (value.name == original_name)
+ return &value.mappedName;
+ }
+ return nullptr;
+}
+
const std::string* Shader::GetOriginalNameFromHashedName(
const std::string& hashed_name) const {
NameMap::const_iterator it = name_map_.find(hashed_name);
@@ -249,6 +253,18 @@ const sh::InterfaceBlock* Shader::GetInterfaceBlockInfo(
return it != interface_block_map_.end() ? &it->second : NULL;
}
+const sh::OutputVariable* Shader::GetOutputVariableInfo(
+ const std::string& name) const {
+ std::string mapped_name = GetTopVariableName(name);
+ // Number of output variables is expected to be so low that
+ // a linear search of a list should be faster than using a map.
+ for (const auto& value : output_variable_list_) {
+ if (value.mappedName == mapped_name)
+ return &value;
+ }
+ return nullptr;
+}
+
ShaderManager::ShaderManager() {}
ShaderManager::~ShaderManager() {
diff --git a/gpu/command_buffer/service/shader_manager.h b/gpu/command_buffer/service/shader_manager.h
index 3288bff..1869371 100644
--- a/gpu/command_buffer/service/shader_manager.h
+++ b/gpu/command_buffer/service/shader_manager.h
@@ -88,6 +88,8 @@ class GPU_EXPORT Shader : public base::RefCounted<Shader> {
const sh::Varying* GetVaryingInfo(const std::string& name) const;
const sh::InterfaceBlock* GetInterfaceBlockInfo(
const std::string& name) const;
+ const sh::OutputVariable* GetOutputVariableInfo(
+ const std::string& name) const;
// If the original_name is not found, return NULL.
const std::string* GetAttribMappedName(
@@ -105,6 +107,10 @@ class GPU_EXPORT Shader : public base::RefCounted<Shader> {
const std::string* GetInterfaceBlockMappedName(
const std::string& original_name) const;
+ // If the original_name is not found, return NULL.
+ const std::string* GetOutputVariableMappedName(
+ const std::string& original_name) const;
+
// If the hashed_name is not found, return NULL.
const std::string* GetOriginalNameFromHashedName(
const std::string& hashed_name) const;
@@ -144,6 +150,10 @@ class GPU_EXPORT Shader : public base::RefCounted<Shader> {
return varying_map_;
}
+ const OutputVariableList& output_variable_list() const {
+ return output_variable_list_;
+ }
+
// Used by program cache.
const InterfaceBlockMap& interface_block_map() const {
return interface_block_map_;
@@ -177,6 +187,12 @@ class GPU_EXPORT Shader : public base::RefCounted<Shader> {
uniform_map_[uniform.mappedName] = uniform;
}
+ void set_output_variable_list(
+ const OutputVariableList& output_variable_list) {
+ // copied because cache might be cleared
+ output_variable_list_ = output_variable_list;
+ }
+
private:
friend class base::RefCounted<Shader>;
friend class ShaderManager;
@@ -237,6 +253,7 @@ class GPU_EXPORT Shader : public base::RefCounted<Shader> {
UniformMap uniform_map_;
VaryingMap varying_map_;
InterfaceBlockMap interface_block_map_;
+ OutputVariableList output_variable_list_;
// The name hashing info when the shader was last compiled.
NameMap name_map_;
diff --git a/gpu/command_buffer/service/shader_manager_unittest.cc b/gpu/command_buffer/service/shader_manager_unittest.cc
index c125cfe..93471ec 100644
--- a/gpu/command_buffer/service/shader_manager_unittest.cc
+++ b/gpu/command_buffer/service/shader_manager_unittest.cc
@@ -126,6 +126,11 @@ TEST_F(ShaderManagerTest, DoCompile) {
const GLenum kVarying1Precision = GL_HIGH_FLOAT;
const bool kVarying1StaticUse = false;
const char* kVarying1Name = "varying1";
+ const GLenum kOutputVariable1Type = GL_FLOAT_VEC4;
+ const GLint kOutputVariable1Size = 4;
+ const GLenum kOutputVariable1Precision = GL_MEDIUM_FLOAT;
+ const char* kOutputVariable1Name = "gl_FragColor";
+ const bool kOutputVariable1StaticUse = true;
// Check we can create shader.
Shader* shader1 = manager_.CreateShader(
@@ -187,10 +192,13 @@ TEST_F(ShaderManagerTest, DoCompile) {
varying_map[kVarying1Name] = TestHelper::ConstructVarying(
kVarying1Type, kVarying1Size, kVarying1Precision,
kVarying1StaticUse, kVarying1Name);
-
+ OutputVariableList output_variable_list;
+ output_variable_list.push_back(TestHelper::ConstructOutputVariable(
+ kOutputVariable1Type, kOutputVariable1Size, kOutputVariable1Precision,
+ kOutputVariable1StaticUse, kOutputVariable1Name));
TestHelper::SetShaderStates(
- gl_.get(), shader1, true, &kLog, &kTranslatedSource, NULL,
- &attrib_map, &uniform_map, &varying_map, NULL, NULL);
+ gl_.get(), shader1, true, &kLog, &kTranslatedSource, nullptr, &attrib_map,
+ &uniform_map, &varying_map, nullptr, &output_variable_list, nullptr);
EXPECT_TRUE(shader1->valid());
// When compilation succeeds, no log is recorded.
EXPECT_STREQ("", shader1->log_info().c_str());
@@ -233,17 +241,33 @@ TEST_F(ShaderManagerTest, DoCompile) {
EXPECT_EQ(it->second.staticUse, variable_info->staticUse);
EXPECT_STREQ(it->second.name.c_str(), variable_info->name.c_str());
}
+ // Check output variable infos got copied.
+ EXPECT_EQ(output_variable_list.size(),
+ shader1->output_variable_list().size());
+ for (auto it = output_variable_list.begin(); it != output_variable_list.end();
+ ++it) {
+ const sh::OutputVariable* variable_info =
+ shader1->GetOutputVariableInfo(it->mappedName);
+ ASSERT_TRUE(variable_info != nullptr);
+ EXPECT_EQ(it->type, variable_info->type);
+ EXPECT_EQ(it->arraySize, variable_info->arraySize);
+ EXPECT_EQ(it->precision, variable_info->precision);
+ EXPECT_EQ(it->staticUse, variable_info->staticUse);
+ EXPECT_STREQ(it->name.c_str(), variable_info->name.c_str());
+ }
// Compile failure case.
- TestHelper::SetShaderStates(
- gl_.get(), shader1, false, &kLog, &kTranslatedSource, NULL,
- &attrib_map, &uniform_map, &varying_map, NULL, NULL);
+ TestHelper::SetShaderStates(gl_.get(), shader1, false, &kLog,
+ &kTranslatedSource, nullptr, &attrib_map,
+ &uniform_map, &varying_map, nullptr,
+ &output_variable_list, nullptr);
EXPECT_FALSE(shader1->valid());
EXPECT_STREQ(kLog.c_str(), shader1->log_info().c_str());
EXPECT_STREQ("", shader1->translated_source().c_str());
EXPECT_TRUE(shader1->attrib_map().empty());
EXPECT_TRUE(shader1->uniform_map().empty());
EXPECT_TRUE(shader1->varying_map().empty());
+ EXPECT_TRUE(shader1->output_variable_list().empty());
}
TEST_F(ShaderManagerTest, ShaderInfoUseCount) {
diff --git a/gpu/command_buffer/service/shader_translator.cc b/gpu/command_buffer/service/shader_translator.cc
index 92c7f9d..255caff 100644
--- a/gpu/command_buffer/service/shader_translator.cc
+++ b/gpu/command_buffer/service/shader_translator.cc
@@ -71,6 +71,11 @@ void GetVaryings(ShHandle compiler, VaryingMap* var_map) {
(*var_map)[(*varyings)[ii].mappedName] = (*varyings)[ii];
}
}
+void GetOutputVariables(ShHandle compiler, OutputVariableList* var_list) {
+ if (!var_list)
+ return;
+ *var_list = *ShGetOutputVariables(compiler);
+}
void GetInterfaceBlocks(ShHandle compiler, InterfaceBlockMap* var_map) {
if (!var_map)
@@ -206,6 +211,7 @@ bool ShaderTranslator::Translate(const std::string& shader_source,
UniformMap* uniform_map,
VaryingMap* varying_map,
InterfaceBlockMap* interface_block_map,
+ OutputVariableList* output_variable_list,
NameMap* name_map) const {
// Make sure this instance is initialized.
DCHECK(compiler_ != NULL);
@@ -224,11 +230,12 @@ bool ShaderTranslator::Translate(const std::string& shader_source,
}
// Get shader version.
*shader_version = ShGetShaderVersion(compiler_);
- // Get info for attribs, uniforms, and varyings.
+ // Get info for attribs, uniforms, varyings and output variables.
GetAttributes(compiler_, attrib_map);
GetUniforms(compiler_, uniform_map);
GetVaryings(compiler_, varying_map);
GetInterfaceBlocks(compiler_, interface_block_map);
+ GetOutputVariables(compiler_, output_variable_list);
// Get info for name hashing.
GetNameHashingInfo(compiler_, name_map);
}
diff --git a/gpu/command_buffer/service/shader_translator.h b/gpu/command_buffer/service/shader_translator.h
index 58ba69f..5a4914d 100644
--- a/gpu/command_buffer/service/shader_translator.h
+++ b/gpu/command_buffer/service/shader_translator.h
@@ -24,6 +24,7 @@ namespace gles2 {
// Mapping between variable name and info.
typedef base::hash_map<std::string, sh::Attribute> AttributeMap;
+typedef std::vector<sh::OutputVariable> OutputVariableList;
typedef base::hash_map<std::string, sh::Uniform> UniformMap;
typedef base::hash_map<std::string, sh::Varying> VaryingMap;
typedef base::hash_map<std::string, sh::InterfaceBlock> InterfaceBlockMap;
@@ -58,6 +59,7 @@ class ShaderTranslatorInterface
UniformMap* uniform_map,
VaryingMap* varying_map,
InterfaceBlockMap* interface_block_map,
+ OutputVariableList* output_variable_list,
NameMap* name_map) const = 0;
// Return a string that is unique for a specfic set of options that would
@@ -109,6 +111,7 @@ class GPU_EXPORT ShaderTranslator
UniformMap* uniform_map,
VaryingMap* varying_map,
InterfaceBlockMap* interface_block_map,
+ OutputVariableList* output_variable_list,
NameMap* name_map) const override;
std::string GetStringForOptionsThatWouldAffectCompilation() const override;
diff --git a/gpu/command_buffer/service/shader_translator_unittest.cc b/gpu/command_buffer/service/shader_translator_unittest.cc
index eff11c1..e5b3f6b 100644
--- a/gpu/command_buffer/service/shader_translator_unittest.cc
+++ b/gpu/command_buffer/service/shader_translator_unittest.cc
@@ -98,16 +98,13 @@ TEST_F(ShaderTranslatorTest, ValidVertexShader) {
UniformMap uniform_map;
VaryingMap varying_map;
InterfaceBlockMap interface_block_map;
+ OutputVariableList output_variable_list;
NameMap name_map;
- EXPECT_TRUE(vertex_translator_->Translate(shader,
- &info_log,
- &translated_source,
- &shader_version,
- &attrib_map,
- &uniform_map,
- &varying_map,
- &interface_block_map,
- &name_map));
+ EXPECT_TRUE(vertex_translator_->Translate(
+ shader, &info_log, &translated_source, &shader_version, &attrib_map,
+ &uniform_map, &varying_map, &interface_block_map, &output_variable_list,
+ &name_map));
+
// Info log must be NULL.
EXPECT_TRUE(info_log.empty());
// Translated shader must be valid and non-empty.
@@ -118,6 +115,7 @@ TEST_F(ShaderTranslatorTest, ValidVertexShader) {
EXPECT_TRUE(uniform_map.empty());
EXPECT_TRUE(interface_block_map.empty());
EXPECT_EQ(1u, varying_map.size());
+ EXPECT_TRUE(output_variable_list.empty());
// There should be no name mapping.
EXPECT_TRUE(name_map.empty());
}
@@ -136,16 +134,12 @@ TEST_F(ShaderTranslatorTest, InvalidVertexShader) {
UniformMap uniform_map;
VaryingMap varying_map;
InterfaceBlockMap interface_block_map;
+ OutputVariableList output_variable_list;
NameMap name_map;
- EXPECT_FALSE(vertex_translator_->Translate(bad_shader,
- &info_log,
- &translated_source,
- &shader_version,
- &attrib_map,
- &uniform_map,
- &varying_map,
- &interface_block_map,
- &name_map));
+ EXPECT_FALSE(vertex_translator_->Translate(
+ bad_shader, &info_log, &translated_source, &shader_version, &attrib_map,
+ &uniform_map, &varying_map, &interface_block_map, &output_variable_list,
+ &name_map));
// Info log must be valid and non-empty.
ASSERT_FALSE(info_log.empty());
// Translated shader must be NULL.
@@ -156,19 +150,15 @@ TEST_F(ShaderTranslatorTest, InvalidVertexShader) {
EXPECT_TRUE(uniform_map.empty());
EXPECT_TRUE(varying_map.empty());
EXPECT_TRUE(interface_block_map.empty());
+ EXPECT_TRUE(output_variable_list.empty());
EXPECT_TRUE(name_map.empty());
// Try a good shader after bad.
info_log.clear();
- EXPECT_TRUE(vertex_translator_->Translate(good_shader,
- &info_log,
- &translated_source,
- &shader_version,
- &attrib_map,
- &uniform_map,
- &varying_map,
- &interface_block_map,
- &name_map));
+ EXPECT_TRUE(vertex_translator_->Translate(
+ good_shader, &info_log, &translated_source, &shader_version, &attrib_map,
+ &uniform_map, &varying_map, &interface_block_map, &output_variable_list,
+ &name_map));
EXPECT_TRUE(info_log.empty());
EXPECT_FALSE(translated_source.empty());
EXPECT_TRUE(interface_block_map.empty());
@@ -187,16 +177,12 @@ TEST_F(ShaderTranslatorTest, ValidFragmentShader) {
UniformMap uniform_map;
VaryingMap varying_map;
InterfaceBlockMap interface_block_map;
+ OutputVariableList output_variable_list;
NameMap name_map;
- EXPECT_TRUE(fragment_translator_->Translate(shader,
- &info_log,
- &translated_source,
- &shader_version,
- &attrib_map,
- &uniform_map,
- &varying_map,
- &interface_block_map,
- &name_map));
+ EXPECT_TRUE(fragment_translator_->Translate(
+ shader, &info_log, &translated_source, &shader_version, &attrib_map,
+ &uniform_map, &varying_map, &interface_block_map, &output_variable_list,
+ &name_map));
// Info log must be NULL.
EXPECT_TRUE(info_log.empty());
// Translated shader must be valid and non-empty.
@@ -208,6 +194,8 @@ TEST_F(ShaderTranslatorTest, ValidFragmentShader) {
EXPECT_TRUE(varying_map.empty());
EXPECT_TRUE(interface_block_map.empty());
EXPECT_TRUE(name_map.empty());
+ // gl_FragColor.
+ EXPECT_EQ(1u, output_variable_list.size());
}
TEST_F(ShaderTranslatorTest, InvalidFragmentShader) {
@@ -219,17 +207,13 @@ TEST_F(ShaderTranslatorTest, InvalidFragmentShader) {
UniformMap uniform_map;
VaryingMap varying_map;
InterfaceBlockMap interface_block_map;
+ OutputVariableList output_variable_list;
NameMap name_map;
// An invalid shader should fail.
- EXPECT_FALSE(fragment_translator_->Translate(shader,
- &info_log,
- &translated_source,
- &shader_version,
- &attrib_map,
- &uniform_map,
- &varying_map,
- &interface_block_map,
- &name_map));
+ EXPECT_FALSE(fragment_translator_->Translate(
+ shader, &info_log, &translated_source, &shader_version, &attrib_map,
+ &uniform_map, &varying_map, &interface_block_map, &output_variable_list,
+ &name_map));
// Info log must be valid and non-empty.
EXPECT_FALSE(info_log.empty());
// Translated shader must be NULL.
@@ -239,6 +223,7 @@ TEST_F(ShaderTranslatorTest, InvalidFragmentShader) {
EXPECT_TRUE(attrib_map.empty());
EXPECT_TRUE(uniform_map.empty());
EXPECT_TRUE(varying_map.empty());
+ EXPECT_TRUE(output_variable_list.empty());
EXPECT_TRUE(name_map.empty());
}
@@ -255,16 +240,12 @@ TEST_F(ShaderTranslatorTest, GetAttributes) {
UniformMap uniform_map;
VaryingMap varying_map;
InterfaceBlockMap interface_block_map;
+ OutputVariableList output_variable_list;
NameMap name_map;
- EXPECT_TRUE(vertex_translator_->Translate(shader,
- &info_log,
- &translated_source,
- &shader_version,
- &attrib_map,
- &uniform_map,
- &varying_map,
- &interface_block_map,
- &name_map));
+ EXPECT_TRUE(vertex_translator_->Translate(
+ shader, &info_log, &translated_source, &shader_version, &attrib_map,
+ &uniform_map, &varying_map, &interface_block_map, &output_variable_list,
+ &name_map));
// Info log must be NULL.
EXPECT_TRUE(info_log.empty());
// Translated shader must be valid and non-empty.
@@ -303,16 +284,12 @@ TEST_F(ShaderTranslatorTest, GetUniforms) {
UniformMap uniform_map;
VaryingMap varying_map;
InterfaceBlockMap interface_block_map;
+ OutputVariableList output_variable_list;
NameMap name_map;
- EXPECT_TRUE(fragment_translator_->Translate(shader,
- &info_log,
- &translated_source,
- &shader_version,
- &attrib_map,
- &uniform_map,
- &varying_map,
- &interface_block_map,
- &name_map));
+ EXPECT_TRUE(fragment_translator_->Translate(
+ shader, &info_log, &translated_source, &shader_version, &attrib_map,
+ &uniform_map, &varying_map, &interface_block_map, &output_variable_list,
+ &name_map));
// Info log must be NULL.
EXPECT_TRUE(info_log.empty());
// Translated shader must be valid and non-empty.
@@ -344,6 +321,9 @@ TEST_F(ShaderTranslatorTest, GetUniforms) {
EXPECT_EQ(1u, info->arraySize);
EXPECT_STREQ("color", info->name.c_str());
EXPECT_STREQ("bar[1].foo.color[0]", original_name.c_str());
+ EXPECT_EQ(1u, output_variable_list.size());
+ ASSERT_TRUE(output_variable_list.size() > 0);
+ EXPECT_EQ(output_variable_list[0].mappedName, "gl_FragColor");
}
@@ -372,16 +352,12 @@ TEST_F(ES3ShaderTranslatorTest, InvalidInterfaceBlocks) {
UniformMap uniform_map;
VaryingMap varying_map;
InterfaceBlockMap interface_block_map;
+ OutputVariableList output_variable_list;
NameMap name_map;
- EXPECT_FALSE(fragment_translator_->Translate(shader,
- &info_log,
- &translated_source,
- &shader_version,
- &attrib_map,
- &uniform_map,
- &varying_map,
- &interface_block_map,
- &name_map));
+ EXPECT_FALSE(fragment_translator_->Translate(
+ shader, &info_log, &translated_source, &shader_version, &attrib_map,
+ &uniform_map, &varying_map, &interface_block_map, &output_variable_list,
+ &name_map));
// Info log must be valid and non-empty.
ASSERT_FALSE(info_log.empty());
// Translated shader must be NULL.
@@ -415,16 +391,12 @@ TEST_F(ES3ShaderTranslatorTest, GetInterfaceBlocks) {
UniformMap uniform_map;
VaryingMap varying_map;
InterfaceBlockMap interface_block_map;
+ OutputVariableList output_variable_list;
NameMap name_map;
- EXPECT_TRUE(fragment_translator_->Translate(shader,
- &info_log,
- &translated_source,
- &shader_version,
- &attrib_map,
- &uniform_map,
- &varying_map,
- &interface_block_map,
- &name_map));
+ EXPECT_TRUE(fragment_translator_->Translate(
+ shader, &info_log, &translated_source, &shader_version, &attrib_map,
+ &uniform_map, &varying_map, &interface_block_map, &output_variable_list,
+ &name_map));
// Info log must be NULL.
EXPECT_TRUE(info_log.empty());
// Translated shader must be valid and non-empty.
@@ -509,7 +481,7 @@ TEST_P(ShaderTranslatorOutputVersionTest, HasCorrectOutputGLSLVersion) {
int shader_version;
EXPECT_TRUE(translator->Translate(kShader, nullptr, &translated_source,
&shader_version, nullptr, nullptr, nullptr,
- nullptr, nullptr));
+ nullptr, nullptr, nullptr));
std::string expected_version_directive = testing::get<1>(GetParam());
if (expected_version_directive.empty()) {
diff --git a/gpu/command_buffer/service/test_helper.cc b/gpu/command_buffer/service/test_helper.cc
index 2c8fc25..40c285a 100644
--- a/gpu/command_buffer/service/test_helper.cc
+++ b/gpu/command_buffer/service/test_helper.cc
@@ -339,6 +339,16 @@ void TestHelper::SetupContextGroupInitExpectations(
.WillOnce(SetArgumentPointee<1>(kMaxSamples))
.RetiresOnSaturation();
}
+
+ if (gl_info.IsAtLeastGL(3, 3) ||
+ (gl_info.IsAtLeastGL(3, 2) &&
+ strstr(extensions, "GL_ARB_blend_func_extended")) ||
+ (gl_info.is_es && strstr(extensions, "GL_EXT_blend_func_extended"))) {
+ EXPECT_CALL(*gl, GetIntegerv(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT, _))
+ .WillOnce(SetArgumentPointee<1>(8))
+ .RetiresOnSaturation();
+ }
+
EXPECT_CALL(*gl, GetIntegerv(GL_MAX_VERTEX_ATTRIBS, _))
.WillOnce(SetArgumentPointee<1>(kNumVertexAttribs))
.RetiresOnSaturation();
@@ -689,6 +699,8 @@ void TestHelper::SetupProgramSuccessExpectations(
size_t num_uniforms,
VaryingInfo* varyings,
size_t num_varyings,
+ ProgramOutputInfo* program_outputs,
+ size_t num_program_outputs,
GLuint service_id) {
EXPECT_CALL(*gl,
GetProgramiv(service_id, GL_LINK_STATUS, _))
@@ -724,7 +736,7 @@ void TestHelper::SetupProgramSuccessExpectations(
SetArrayArgument<6>(info.name,
info.name + strlen(info.name) + 1)))
.RetiresOnSaturation();
- if (!ProgramManager::IsInvalidPrefix(info.name, strlen(info.name))) {
+ if (!ProgramManager::HasBuiltInPrefix(info.name)) {
EXPECT_CALL(*gl, GetAttribLocation(service_id, StrEq(info.name)))
.WillOnce(Return(info.location))
.RetiresOnSaturation();
@@ -799,7 +811,9 @@ void TestHelper::SetupProgramSuccessExpectations(
SetArrayArgument<5>(
info.name, info.name + strlen(info.name) + 1)))
.RetiresOnSaturation();
- if (!ProgramManager::IsInvalidPrefix(info.name, strlen(info.name))) {
+ if (ProgramManager::HasBuiltInPrefix(info.name))
+ continue;
+
static const GLenum kPropsArray[] = {GL_LOCATION, GL_TYPE,
GL_ARRAY_SIZE};
static const size_t kPropsSize = arraysize(kPropsArray);
@@ -818,6 +832,26 @@ void TestHelper::SetupProgramSuccessExpectations(
}))
.RetiresOnSaturation();
}
+ }
+ if (feature_info->gl_version_info().IsES3Capable() &&
+ !feature_info->disable_shader_translator()) {
+ for (size_t ii = 0; ii < num_program_outputs; ++ii) {
+ ProgramOutputInfo& info = program_outputs[ii];
+ if (ProgramManager::HasBuiltInPrefix(info.name))
+ continue;
+
+ EXPECT_CALL(*gl, GetFragDataLocation(service_id, StrEq(info.name)))
+ .WillOnce(Return(info.color_name))
+ .RetiresOnSaturation();
+ if (feature_info->feature_flags().ext_blend_func_extended) {
+ EXPECT_CALL(*gl, GetFragDataIndex(service_id, StrEq(info.name)))
+ .WillOnce(Return(info.index))
+ .RetiresOnSaturation();
+ } else {
+ // Test case must not use indices, or the context of the testcase has to
+ // support the dual source blending.
+ DCHECK(info.index == 0);
+ }
}
}
}
@@ -834,8 +868,8 @@ void TestHelper::SetupShaderExpectations(::gfx::MockGLInterface* gl,
EXPECT_CALL(*gl, LinkProgram(service_id)).Times(1).RetiresOnSaturation();
SetupProgramSuccessExpectations(gl, feature_info, attribs, num_attribs,
- uniforms, num_uniforms, nullptr, 0,
- service_id);
+ uniforms, num_uniforms, nullptr, 0, nullptr,
+ 0, service_id);
}
void TestHelper::SetupShaderExpectationsWithVaryings(
@@ -847,6 +881,8 @@ void TestHelper::SetupShaderExpectationsWithVaryings(
size_t num_uniforms,
VaryingInfo* varyings,
size_t num_varyings,
+ ProgramOutputInfo* program_outputs,
+ size_t num_program_outputs,
GLuint service_id) {
InSequence s;
@@ -855,9 +891,9 @@ void TestHelper::SetupShaderExpectationsWithVaryings(
.Times(1)
.RetiresOnSaturation();
- SetupProgramSuccessExpectations(gl, feature_info, attribs, num_attribs,
- uniforms, num_uniforms, varyings,
- num_varyings, service_id);
+ SetupProgramSuccessExpectations(
+ gl, feature_info, attribs, num_attribs, uniforms, num_uniforms, varyings,
+ num_varyings, program_outputs, num_program_outputs, service_id);
}
void TestHelper::DoBufferData(
@@ -905,16 +941,18 @@ void TestHelper::SetTexParameteriWithExpectations(
// static
void TestHelper::SetShaderStates(
- ::gfx::MockGLInterface* gl, Shader* shader,
- bool expected_valid,
- const std::string* const expected_log_info,
- const std::string* const expected_translated_source,
- const int* const expected_shader_version,
- const AttributeMap* const expected_attrib_map,
- const UniformMap* const expected_uniform_map,
- const VaryingMap* const expected_varying_map,
- const InterfaceBlockMap* const expected_interface_block_map,
- const NameMap* const expected_name_map) {
+ ::gfx::MockGLInterface* gl,
+ Shader* shader,
+ bool expected_valid,
+ const std::string* const expected_log_info,
+ const std::string* const expected_translated_source,
+ const int* const expected_shader_version,
+ const AttributeMap* const expected_attrib_map,
+ const UniformMap* const expected_uniform_map,
+ const VaryingMap* const expected_varying_map,
+ const InterfaceBlockMap* const expected_interface_block_map,
+ const OutputVariableList* const expected_output_variable_list,
+ const NameMap* const expected_name_map) {
const std::string empty_log_info;
const std::string* log_info = (expected_log_info && !expected_valid) ?
expected_log_info : &empty_log_info;
@@ -938,6 +976,11 @@ void TestHelper::SetShaderStates(
const InterfaceBlockMap* interface_block_map =
(expected_interface_block_map && expected_valid) ?
expected_interface_block_map : &empty_interface_block_map;
+ const OutputVariableList empty_output_variable_list;
+ const OutputVariableList* output_variable_list =
+ (expected_output_variable_list && expected_valid)
+ ? expected_output_variable_list
+ : &empty_output_variable_list;
const NameMap empty_name_map;
const NameMap* name_map = (expected_name_map && expected_valid) ?
expected_name_map : &empty_name_map;
@@ -945,13 +988,14 @@ void TestHelper::SetShaderStates(
MockShaderTranslator* mock_translator = new MockShaderTranslator;
scoped_refptr<ShaderTranslatorInterface> translator(mock_translator);
EXPECT_CALL(*mock_translator, Translate(_,
- NotNull(), // log_info
- NotNull(), // translated_source
- NotNull(), // shader_version
- NotNull(), // attrib_map
- NotNull(), // uniform_map
- NotNull(), // varying_map
- NotNull(), // interface_block_map
+ NotNull(), // log_info
+ NotNull(), // translated_source
+ NotNull(), // shader_version
+ NotNull(), // attrib_map
+ NotNull(), // uniform_map
+ NotNull(), // varying_map
+ NotNull(), // interface_block_map
+ NotNull(), // output_variable_list
NotNull())) // name_map
.WillOnce(DoAll(SetArgumentPointee<1>(*log_info),
SetArgumentPointee<2>(*translated_source),
@@ -960,8 +1004,8 @@ void TestHelper::SetShaderStates(
SetArgumentPointee<5>(*uniform_map),
SetArgumentPointee<6>(*varying_map),
SetArgumentPointee<7>(*interface_block_map),
- SetArgumentPointee<8>(*name_map),
- Return(expected_valid)))
+ SetArgumentPointee<8>(*output_variable_list),
+ SetArgumentPointee<9>(*name_map), Return(expected_valid)))
.RetiresOnSaturation();
if (expected_valid) {
EXPECT_CALL(*gl, ShaderSource(shader->service_id(), 1, _, NULL))
@@ -983,8 +1027,8 @@ void TestHelper::SetShaderStates(
// static
void TestHelper::SetShaderStates(
::gfx::MockGLInterface* gl, Shader* shader, bool valid) {
- SetShaderStates(
- gl, shader, valid, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ SetShaderStates(gl, shader, valid, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr);
}
// static
@@ -1011,6 +1055,16 @@ sh::Varying TestHelper::ConstructVarying(
type, array_size, precision, static_use, name);
}
+sh::OutputVariable TestHelper::ConstructOutputVariable(
+ GLenum type,
+ GLint array_size,
+ GLenum precision,
+ bool static_use,
+ const std::string& name) {
+ return ConstructShaderVariable<sh::OutputVariable>(
+ type, array_size, precision, static_use, name);
+}
+
ScopedGLImplementationSetter::ScopedGLImplementationSetter(
gfx::GLImplementation implementation)
: old_implementation_(gfx::GetGLImplementation()) {
diff --git a/gpu/command_buffer/service/test_helper.h b/gpu/command_buffer/service/test_helper.h
index 33c91fb..7072428 100644
--- a/gpu/command_buffer/service/test_helper.h
+++ b/gpu/command_buffer/service/test_helper.h
@@ -82,6 +82,13 @@ class TestHelper {
GLint real_location;
GLint desired_location;
};
+ struct ProgramOutputInfo {
+ const char* name;
+ GLint size;
+ GLenum type;
+ GLint color_name;
+ GLuint index;
+ };
static void SetupContextGroupInitExpectations(
::gfx::MockGLInterface* gl,
@@ -126,17 +133,22 @@ class TestHelper {
size_t num_uniforms,
VaryingInfo* varyings,
size_t num_varyings,
+ ProgramOutputInfo* program_outputs,
+ size_t num_program_outputs,
GLuint service_id);
- static void SetupProgramSuccessExpectations(::gfx::MockGLInterface* gl,
- const FeatureInfo* feature_info,
- AttribInfo* attribs,
- size_t num_attribs,
- UniformInfo* uniforms,
- size_t num_uniforms,
- VaryingInfo* varyings,
- size_t num_varyings,
- GLuint service_id);
+ static void SetupProgramSuccessExpectations(
+ ::gfx::MockGLInterface* gl,
+ const FeatureInfo* feature_info,
+ AttribInfo* attribs,
+ size_t num_attribs,
+ UniformInfo* uniforms,
+ size_t num_uniforms,
+ VaryingInfo* varyings,
+ size_t num_varyings,
+ ProgramOutputInfo* program_outputs,
+ size_t num_program_outputs,
+ GLuint service_id);
static void DoBufferData(
::gfx::MockGLInterface* gl, MockErrorState* error_state,
@@ -149,7 +161,8 @@ class TestHelper {
GLenum pname, GLint value, GLenum error);
static void SetShaderStates(
- ::gfx::MockGLInterface* gl, Shader* shader,
+ ::gfx::MockGLInterface* gl,
+ Shader* shader,
bool expected_valid,
const std::string* const expected_log_info,
const std::string* const expected_translated_source,
@@ -158,6 +171,7 @@ class TestHelper {
const UniformMap* const expected_uniform_map,
const VaryingMap* const expected_varying_map,
const InterfaceBlockMap* const expected_interface_block_map,
+ const OutputVariableList* const expected_output_variable_list,
const NameMap* const expected_name_map);
static void SetShaderStates(
@@ -172,6 +186,11 @@ class TestHelper {
static sh::Varying ConstructVarying(
GLenum type, GLint array_size, GLenum precision,
bool static_use, const std::string& name);
+ static sh::OutputVariable ConstructOutputVariable(GLenum type,
+ GLint array_size,
+ GLenum precision,
+ bool static_use,
+ const std::string& name);
private:
static void SetupTextureInitializationExpectations(::gfx::MockGLInterface* gl,
diff --git a/gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc b/gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc
new file mode 100644
index 0000000..d2f01ab
--- /dev/null
+++ b/gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc
@@ -0,0 +1,653 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES2/gl2extchromium.h>
+#include <GLES3/gl3.h>
+
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
+#include "gpu/command_buffer/tests/gl_manager.h"
+#include "gpu/command_buffer/tests/gl_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_switches.h"
+
+#define SHADER(Src) #Src
+#define BFE_SHADER(Src) "#extension GL_EXT_blend_func_extended : require\n" #Src
+
+namespace {
+// Partial implementation of weight function for GLES 2 blend equation that
+// is dual-source aware.
+template <int factor, int index>
+float Weight(float /*dst*/[4], float src[4], float src1[4]) {
+ if (factor == GL_SRC_COLOR)
+ return src[index];
+ if (factor == GL_SRC_ALPHA)
+ return src[3];
+ if (factor == GL_SRC1_COLOR_EXT)
+ return src1[index];
+ if (factor == GL_SRC1_ALPHA_EXT)
+ return src1[3];
+ if (factor == GL_ONE_MINUS_SRC1_COLOR_EXT)
+ return 1.0f - src1[index];
+ if (factor == GL_ONE_MINUS_SRC1_ALPHA_EXT)
+ return 1.0f - src1[3];
+ return 0.0f;
+}
+
+// Implementation of GLES 2 blend equation that is dual-source aware.
+template <int RGBs, int RGBd, int As, int Ad>
+void BlendEquationFuncAdd(float dst[4],
+ float src[4],
+ float src1[4],
+ uint8 result[4]) {
+ float r[4];
+ r[0] = src[0] * Weight<RGBs, 0>(dst, src, src1) +
+ dst[0] * Weight<RGBd, 0>(dst, src, src1);
+ r[1] = src[1] * Weight<RGBs, 1>(dst, src, src1) +
+ dst[1] * Weight<RGBd, 1>(dst, src, src1);
+ r[2] = src[2] * Weight<RGBs, 2>(dst, src, src1) +
+ dst[2] * Weight<RGBd, 2>(dst, src, src1);
+ r[3] = src[3] * Weight<As, 3>(dst, src, src1) +
+ dst[3] * Weight<Ad, 3>(dst, src, src1);
+ for (int i = 0; i < 4; ++i) {
+ result[i] = static_cast<uint8>(
+ std::floor(std::max(0.0f, std::min(1.0f, r[i])) * 255.0f));
+ }
+}
+
+} // namespace
+
+namespace gpu {
+
+class EXTBlendFuncExtendedTest : public testing::Test {
+ public:
+ protected:
+ void SetUp() override { gl_.Initialize(GLManager::Options()); }
+
+ void TearDown() override { gl_.Destroy(); }
+ bool IsApplicable() const {
+ return GLTestHelper::HasExtension("GL_EXT_blend_func_extended");
+ }
+ GLManager gl_;
+};
+
+TEST_F(EXTBlendFuncExtendedTest, TestMaxDualSourceDrawBuffers) {
+ if (!IsApplicable())
+ return;
+
+ GLint maxDualSourceDrawBuffers = 0;
+ glGetIntegerv(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT, &maxDualSourceDrawBuffers);
+ EXPECT_GT(maxDualSourceDrawBuffers, 0);
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+}
+
+class EXTBlendFuncExtendedDrawTest : public testing::TestWithParam<bool> {
+ public:
+ static const GLsizei kWidth = 100;
+ static const GLsizei kHeight = 100;
+ EXTBlendFuncExtendedDrawTest() : program_(0) {}
+
+ protected:
+ void SetUp() override {
+ GLManager::Options options;
+ options.size = gfx::Size(kWidth, kHeight);
+ options.force_shader_name_hashing = GetParam();
+ base::CommandLine command_line(*base::CommandLine::ForCurrentProcess());
+ gl_.InitializeWithCommandLine(options, &command_line);
+ }
+
+ bool IsApplicable() const {
+ return GLTestHelper::HasExtension("GL_EXT_blend_func_extended");
+ }
+
+ virtual const char* GetVertexShader() {
+ // clang-format off
+ static const char* kVertexShader =
+ SHADER(
+ attribute vec4 position;
+ void main() {
+ gl_Position = position;
+ });
+ // clang-format on
+ return kVertexShader;
+ }
+
+ void CreateProgramWithFragmentShader(const char* fragment_shader_str) {
+ GLuint vertex_shader =
+ GLTestHelper::LoadShader(GL_VERTEX_SHADER, GetVertexShader());
+ GLuint fragment_shader =
+ GLTestHelper::LoadShader(GL_FRAGMENT_SHADER, fragment_shader_str);
+ ASSERT_NE(0u, vertex_shader);
+ ASSERT_NE(0u, fragment_shader);
+ program_ = glCreateProgram();
+ ASSERT_NE(0u, program_);
+ glAttachShader(program_, vertex_shader);
+ glAttachShader(program_, fragment_shader);
+ glDeleteShader(vertex_shader);
+ glDeleteShader(fragment_shader);
+ }
+
+ testing::AssertionResult LinkProgram() {
+ glLinkProgram(program_);
+ GLint linked = 0;
+ glGetProgramiv(program_, GL_LINK_STATUS, &linked);
+ if (linked == 0) {
+ char buffer[1024];
+ GLsizei length = 0;
+ glGetProgramInfoLog(program_, sizeof(buffer), &length, buffer);
+ std::string log(buffer, length);
+ return testing::AssertionFailure() << "Error linking program: " << log;
+ }
+ glUseProgram(program_);
+ position_loc_ = glGetAttribLocation(program_, "position");
+ src_loc_ = glGetUniformLocation(program_, "src");
+ src1_loc_ = glGetUniformLocation(program_, "src1");
+ return testing::AssertionSuccess();
+ }
+
+ void TearDown() override {
+ if (program_ != 0)
+ glDeleteProgram(program_);
+ gl_.Destroy();
+ }
+
+ void DrawAndVerify() {
+ float kDst[4] = {0.5f, 0.5f, 0.5f, 0.5f};
+ float kSrc[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ float kSrc1[4] = {0.3f, 0.6f, 0.9f, 0.7f};
+
+ glUniform4f(src_loc_, kSrc[0], kSrc[1], kSrc[2], kSrc[3]);
+ glUniform4f(src1_loc_, kSrc1[0], kSrc1[1], kSrc1[2], kSrc1[3]);
+
+ GLTestHelper::SetupUnitQuad(position_loc_);
+
+ glEnable(GL_BLEND);
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFuncSeparate(GL_SRC1_COLOR_EXT, GL_SRC_ALPHA,
+ GL_ONE_MINUS_SRC1_COLOR_EXT,
+ GL_ONE_MINUS_SRC1_ALPHA_EXT);
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+
+ // Draw one triangle (bottom left half).
+ glViewport(0, 0, kWidth, kHeight);
+ glClearColor(kDst[0], kDst[1], kDst[2], kDst[3]);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ // Verify.
+ uint8 color[4];
+ BlendEquationFuncAdd<GL_SRC1_COLOR_EXT, GL_SRC_ALPHA,
+ GL_ONE_MINUS_SRC1_COLOR_EXT,
+ GL_ONE_MINUS_SRC1_ALPHA_EXT>(kDst, kSrc, kSrc1, color);
+
+ EXPECT_TRUE(GLTestHelper::CheckPixels(kWidth / 4, (3 * kHeight) / 4, 1, 1,
+ 1, color));
+ EXPECT_TRUE(GLTestHelper::CheckPixels(kWidth - 1, 0, 1, 1, 1, color));
+ }
+
+ protected:
+ GLuint program_;
+ GLuint position_loc_;
+ GLuint src_loc_;
+ GLuint src1_loc_;
+ GLManager gl_;
+};
+
+TEST_P(EXTBlendFuncExtendedDrawTest, ESSL1FragColor) {
+ if (!IsApplicable())
+ return;
+ // clang-format off
+ static const char* kFragColorShader =
+ BFE_SHADER(
+ precision mediump float;
+ uniform vec4 src;
+ uniform vec4 src1;
+ void main() {
+ gl_FragColor = src;
+ gl_SecondaryFragColorEXT = src1;
+ });
+ // clang-format on
+ CreateProgramWithFragmentShader(kFragColorShader);
+ LinkProgram();
+ DrawAndVerify();
+}
+
+TEST_P(EXTBlendFuncExtendedDrawTest, ESSL1FragData) {
+ if (!IsApplicable())
+ return;
+ // clang-format off
+ static const char* kFragDataShader =
+ BFE_SHADER(
+ precision mediump float;
+ uniform vec4 src;
+ uniform vec4 src1;
+ void main() {
+ gl_FragData[0] = src;
+ gl_SecondaryFragDataEXT[0] = src1;
+ });
+ // clang-format on
+ CreateProgramWithFragmentShader(kFragDataShader);
+ LinkProgram();
+ DrawAndVerify();
+}
+
+class EXTBlendFuncExtendedES3DrawTest : public EXTBlendFuncExtendedDrawTest {
+ protected:
+ void SetUp() override {
+ GLManager::Options options;
+ options.size = gfx::Size(kWidth, kHeight);
+ options.context_type = gles2::CONTEXT_TYPE_OPENGLES3;
+ options.force_shader_name_hashing = GetParam();
+ base::CommandLine command_line(*base::CommandLine::ForCurrentProcess());
+ command_line.AppendSwitch(switches::kEnableUnsafeES3APIs);
+ gl_.InitializeWithCommandLine(options, &command_line);
+ }
+ bool IsApplicable() const {
+ return gl_.IsInitialized() && EXTBlendFuncExtendedDrawTest::IsApplicable();
+ }
+ const char* GetVertexShader() override {
+ // clang-format off
+ static const char* kVertexShader =
+ "#version 300 es\n"
+ SHADER(
+ in vec4 position;
+ void main() {
+ gl_Position = position;
+ });
+ // clang-format on
+ return kVertexShader;
+ }
+};
+
+TEST_P(EXTBlendFuncExtendedES3DrawTest, ESSL3Var) {
+ if (!IsApplicable())
+ return;
+ // clang-format off
+ static const char* kFragColorShader =
+ "#version 300 es\n"
+ BFE_SHADER(
+ precision mediump float;
+ uniform vec4 src;
+ uniform vec4 src1;
+ out vec4 FragColor;
+ out vec4 SecondaryFragColor;
+ void main() {
+ FragColor = src;
+ SecondaryFragColor = src1;
+ });
+ // clang-format on
+ CreateProgramWithFragmentShader(kFragColorShader);
+ glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragColor");
+ LinkProgram();
+ DrawAndVerify();
+}
+
+TEST_P(EXTBlendFuncExtendedES3DrawTest, ESSL3BindArrayWithSimpleName) {
+ if (!IsApplicable())
+ return;
+ // clang-format off
+ static const char* kFragDataShader =
+ "#version 300 es\n"
+ BFE_SHADER(
+ precision mediump float;
+ uniform vec4 src;
+ uniform vec4 src1;
+ out vec4 FragData[1];
+ out vec4 SecondaryFragData[1];
+ void main() {
+ FragData[0] = src;
+ SecondaryFragData[0] = src1;
+ });
+ // clang-format on
+ CreateProgramWithFragmentShader(kFragDataShader);
+ glBindFragDataLocationEXT(program_, 0, "FragData");
+ glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragData");
+ LinkProgram();
+ DrawAndVerify();
+}
+
+TEST_P(EXTBlendFuncExtendedES3DrawTest, ESSL3BindSimpleVarAsArrayNoBind) {
+ if (!IsApplicable())
+ return;
+ // clang-format off
+ static const char* kFragDataShader =
+ "#version 300 es\n"
+ BFE_SHADER(
+ precision mediump float;
+ uniform vec4 src;
+ uniform vec4 src1;
+ out vec4 FragData;
+ out vec4 SecondaryFragData;
+ void main() {
+ FragData = src;
+ SecondaryFragData = src1;
+ });
+ // clang-format on
+ CreateProgramWithFragmentShader(kFragDataShader);
+ glBindFragDataLocationEXT(program_, 0, "FragData[0]");
+ glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragData[0]");
+ // Does not fail, since FragData[0] and SecondaryFragData[0] do not exist.
+ EXPECT_TRUE(LinkProgram());
+
+ EXPECT_EQ(-1, glGetFragDataLocation(program_, "FragData[0]"));
+ EXPECT_EQ(0, glGetFragDataLocation(program_, "FragData"));
+ EXPECT_EQ(1, glGetFragDataLocation(program_, "SecondaryFragData"));
+ // Did not bind index.
+ EXPECT_EQ(0, glGetFragDataIndexEXT(program_, "SecondaryFragData"));
+
+ glBindFragDataLocationEXT(program_, 0, "FragData");
+ glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragData");
+ EXPECT_TRUE(LinkProgram());
+ DrawAndVerify();
+}
+
+TEST_P(EXTBlendFuncExtendedES3DrawTest, ESSL3BindArrayAsArray) {
+ if (!IsApplicable())
+ return;
+ // clang-format off
+ static const char* kFragDataShader =
+ "#version 300 es\n"
+ BFE_SHADER(
+ precision mediump float;
+ uniform vec4 src;
+ uniform vec4 src1;
+ out vec4 FragData[1];
+ out vec4 SecondaryFragData[1];
+ void main() {
+ FragData[0] = src;
+ SecondaryFragData[0] = src1;
+ });
+ // clang-format on
+ CreateProgramWithFragmentShader(kFragDataShader);
+ glBindFragDataLocationEXT(program_, 0, "FragData[0]");
+ glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragData[0]");
+ LinkProgram();
+ DrawAndVerify();
+}
+
+TEST_P(EXTBlendFuncExtendedES3DrawTest, ES3Getters) {
+ if (!IsApplicable())
+ return;
+ // clang-format off
+ static const char* kFragColorShader =
+ "#version 300 es\n"
+ BFE_SHADER(
+ precision mediump float;
+ uniform vec4 src;
+ uniform vec4 src1;
+ out vec4 FragColor;
+ out vec4 SecondaryFragColor;
+ void main() {
+ FragColor = src;
+ SecondaryFragColor = src1;
+ });
+ // clang-format on
+ CreateProgramWithFragmentShader(kFragColorShader);
+ glBindFragDataLocationEXT(program_, 0, "FragColor");
+ glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragColor");
+
+ // Getters return GL error before linking.
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ GLint location = glGetFragDataLocation(program_, "FragColor");
+ EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
+ GLint index = glGetFragDataIndexEXT(program_, "FragColor");
+ EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
+ location = glGetFragDataLocation(program_, "SecondaryFragColor");
+ EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
+ index = glGetFragDataIndexEXT(program_, "SecondaryFragColor");
+ EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
+ LinkProgram();
+
+ // Getters return location and index after linking. Run twice to confirm that
+ // setters do not affect the getters until next link.
+ for (int i = 0; i < 2; ++i) {
+ SCOPED_TRACE(testing::Message() << "Testing getters after link, iteration "
+ << i);
+
+ location = glGetFragDataLocation(program_, "FragColor");
+ EXPECT_EQ(0, location);
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ index = glGetFragDataIndexEXT(program_, "FragColor");
+ EXPECT_EQ(0, index);
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ location = glGetFragDataLocation(program_, "SecondaryFragColor");
+ EXPECT_EQ(0, location);
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ index = glGetFragDataIndexEXT(program_, "SecondaryFragColor");
+ EXPECT_EQ(1, index);
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+
+ // The calls should not affect the getters until re-linking.
+ glBindFragDataLocationEXT(program_, 0, "SecondaryFragColor");
+ glBindFragDataLocationIndexedEXT(program_, 0, 1, "FragColor");
+ }
+
+ LinkProgram();
+
+ location = glGetFragDataLocation(program_, "FragColor");
+ EXPECT_EQ(0, location);
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ index = glGetFragDataIndexEXT(program_, "FragColor");
+ EXPECT_EQ(1, index);
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ location = glGetFragDataLocation(program_, "SecondaryFragColor");
+ EXPECT_EQ(0, location);
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ index = glGetFragDataIndexEXT(program_, "SecondaryFragColor");
+ EXPECT_EQ(0, index);
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+
+ // Unknown colors return location -1, index -1.
+ location = glGetFragDataLocation(program_, "UnknownColor");
+ EXPECT_EQ(-1, location);
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ index = glGetFragDataIndexEXT(program_, "UnknownColor");
+ EXPECT_EQ(-1, index);
+
+ // Reset the settings and verify that the driver gets them correct.
+ glBindFragDataLocationEXT(program_, 0, "FragColor");
+ glBindFragDataLocationIndexedEXT(program_, 0, 1, "SecondaryFragColor");
+ LinkProgram();
+ DrawAndVerify();
+}
+
+// Test that tests glBindFragDataLocationEXT, glBindFragDataLocationIndexedEXT,
+// glGetFragDataLocation, glGetFragDataIndexEXT work correctly with
+// GLSL array output variables. The output variable can be bound by
+// referring to the variable name with or without the first element array
+// accessor. The getters can query location of the individual elements in
+// the array. The test does not actually use the base test drawing,
+// since the drivers at the time of writing do not support multiple
+// buffers and dual source blending.
+TEST_P(EXTBlendFuncExtendedES3DrawTest, ES3GettersArray) {
+ if (!IsApplicable())
+ return;
+ const GLint kTestArraySize = 2;
+ const GLint kFragData0Location = 2;
+ const GLint kFragData1Location = 1;
+ const GLint kUnusedLocation = 5;
+
+ // The test binds kTestArraySize -sized array to location 1 for test purposes.
+ // The GL_MAX_DRAW_BUFFERS must be > kTestArraySize, since an
+ // array will be bound to continuous locations, starting from the first
+ // location.
+ GLint maxDrawBuffers = 0;
+ glGetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &maxDrawBuffers);
+ EXPECT_LT(kTestArraySize, maxDrawBuffers);
+
+ // clang-format off
+ static const char* kFragColorShader =
+ "#version 300 es\n"
+ BFE_SHADER(
+ precision mediump float;
+ uniform vec4 src;
+ uniform vec4 src1;
+ out vec4 FragData[2];
+ void main() {
+ FragData[0] = src;
+ FragData[1] = src1;
+ });
+ // clang-format on
+ CreateProgramWithFragmentShader(kFragColorShader);
+
+ for (int testcase = 0; testcase < 4; ++testcase) {
+ if (testcase == 0) {
+ glBindFragDataLocationEXT(program_, kUnusedLocation, "FragData[0]");
+ glBindFragDataLocationEXT(program_, kFragData0Location, "FragData");
+ glBindFragDataLocationEXT(program_, kFragData1Location, "FragData[1]");
+ } else if (testcase == 1) {
+ glBindFragDataLocationEXT(program_, kUnusedLocation, "FragData");
+ glBindFragDataLocationEXT(program_, kFragData0Location, "FragData[0]");
+ glBindFragDataLocationEXT(program_, kFragData1Location, "FragData[1]");
+ } else if (testcase == 2) {
+ glBindFragDataLocationIndexedEXT(program_, kUnusedLocation, 0,
+ "FragData[0]");
+ glBindFragDataLocationIndexedEXT(program_, kFragData0Location, 0,
+ "FragData");
+ glBindFragDataLocationIndexedEXT(program_, kFragData1Location, 0,
+ "FragData[1]");
+ } else if (testcase == 3) {
+ glBindFragDataLocationIndexedEXT(program_, kUnusedLocation, 0,
+ "FragData");
+ glBindFragDataLocationIndexedEXT(program_, kFragData0Location, 0,
+ "FragData[0]");
+ glBindFragDataLocationIndexedEXT(program_, kFragData1Location, 0,
+ "FragData[1]");
+ }
+
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ LinkProgram();
+ EXPECT_EQ(kFragData0Location, glGetFragDataLocation(program_, "FragData"));
+ EXPECT_EQ(0, glGetFragDataIndexEXT(program_, "FragData"));
+ EXPECT_EQ(kFragData0Location,
+ glGetFragDataLocation(program_, "FragData[0]"));
+ EXPECT_EQ(0, glGetFragDataIndexEXT(program_, "FragData[0]"));
+ EXPECT_EQ(kFragData1Location,
+ glGetFragDataLocation(program_, "FragData[1]"));
+ EXPECT_EQ(0, glGetFragDataIndexEXT(program_, "FragData[1]"));
+
+ // Index bigger than the GLSL variable array length does not find anything.
+ EXPECT_EQ(-1, glGetFragDataLocation(program_, "FragData[3]"));
+ }
+}
+
+// Test that tests glBindFragDataLocationEXT, glBindFragDataLocationIndexedEXT
+// conflicts
+// with GLSL output variables.
+TEST_P(EXTBlendFuncExtendedES3DrawTest, ES3Conflicts) {
+ if (!IsApplicable())
+ return;
+ const GLint kTestArraySize = 2;
+ const GLint kColorName0Location = 0;
+ const GLint kColorName1Location = 1;
+ GLint maxDrawBuffers = 0;
+ glGetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &maxDrawBuffers);
+ EXPECT_LT(kTestArraySize, maxDrawBuffers);
+
+ // clang-format off
+ static const char* kFragColorShader =
+ "#version 300 es\n"
+ BFE_SHADER(
+ precision mediump float;
+ uniform vec4 src;
+ uniform vec4 src1;
+ out vec4 FragData0;
+ out vec4 FragData1;
+ void main() {
+ FragData0 = src;
+ FragData1 = src1;
+ });
+ // clang-format on
+ CreateProgramWithFragmentShader(kFragColorShader);
+
+ glBindFragDataLocationEXT(program_, kColorName0Location, "FragData0");
+ glBindFragDataLocationEXT(program_, kColorName0Location, "FragData1");
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ EXPECT_FALSE(LinkProgram());
+
+ glBindFragDataLocationIndexedEXT(program_, kColorName0Location, 0,
+ "FragData0");
+ glBindFragDataLocationIndexedEXT(program_, kColorName0Location, 0,
+ "FragData1");
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ EXPECT_FALSE(LinkProgram());
+
+ glBindFragDataLocationIndexedEXT(program_, kColorName0Location, 1,
+ "FragData0");
+ glBindFragDataLocationIndexedEXT(program_, kColorName0Location, 1,
+ "FragData1");
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ EXPECT_FALSE(LinkProgram());
+
+ // Test that correct binding actually works.
+ glBindFragDataLocationEXT(program_, kColorName0Location, "FragData0");
+ glBindFragDataLocationEXT(program_, kColorName1Location, "FragData1");
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ EXPECT_TRUE(LinkProgram());
+
+ glBindFragDataLocationIndexedEXT(program_, kColorName0Location, 0,
+ "FragData0");
+ glBindFragDataLocationIndexedEXT(program_, kColorName0Location, 1,
+ "FragData1");
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ EXPECT_TRUE(LinkProgram());
+}
+
+// Test that tests glBindFragDataLocationEXT conflicts
+// with GLSL array output variables.
+TEST_P(EXTBlendFuncExtendedES3DrawTest, ES3ConflictsArray) {
+ if (!IsApplicable())
+ return;
+ const GLint kTestArraySize = 2;
+ const GLint kColorName0Location = 0;
+ const GLint kColorName1Location = 1;
+ const GLint kUnusedLocation = 5;
+ GLint maxDrawBuffers = 0;
+ glGetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &maxDrawBuffers);
+ EXPECT_LT(kTestArraySize, maxDrawBuffers);
+
+ // clang-format off
+ static const char* kFragColorShader =
+ "#version 300 es\n"
+ BFE_SHADER(
+ precision mediump float;
+ uniform vec4 src;
+ uniform vec4 src1;
+ out vec4 FragData[2];
+ void main() {
+ FragData[0] = src;
+ FragData[1] = src1;
+ });
+ // clang-format on
+ CreateProgramWithFragmentShader(kFragColorShader);
+
+ glBindFragDataLocationEXT(program_, kColorName1Location, "FragData");
+ glBindFragDataLocationEXT(program_, kColorName1Location, "FragData[1]");
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ EXPECT_FALSE(LinkProgram());
+ glBindFragDataLocationEXT(program_, kUnusedLocation, "FragData");
+ glBindFragDataLocationEXT(program_, kColorName1Location, "FragData[0]");
+ glBindFragDataLocationEXT(program_, kColorName1Location, "FragData[1]");
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ EXPECT_FALSE(LinkProgram());
+ // Test that binding actually works.
+ glBindFragDataLocationEXT(program_, kColorName0Location, "FragData[0]");
+ glBindFragDataLocationEXT(program_, kColorName1Location, "FragData[1]");
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+ EXPECT_TRUE(LinkProgram());
+}
+
+INSTANTIATE_TEST_CASE_P(TranslatorVariants,
+ EXTBlendFuncExtendedDrawTest,
+ ::testing::Bool());
+
+INSTANTIATE_TEST_CASE_P(TranslatorVariants,
+ EXTBlendFuncExtendedES3DrawTest,
+ ::testing::Bool());
+
+} // namespace gpu