diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 6e8c92cee9050b..db91adffc8aeb4 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -3273,9 +3273,7 @@ bool GLES2DecoderImpl::InitializeShaderTranslator() { resources.HashFunction = &CityHash64; else resources.HashFunction = NULL; - ShaderTranslatorInterface::GlslImplementationType implementation_type = - gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2 ? - ShaderTranslatorInterface::kGlslES : ShaderTranslatorInterface::kGlsl; + int driver_bug_workarounds = 0; if (workarounds().needs_glsl_built_in_function_emulation) driver_bug_workarounds |= SH_EMULATE_BUILT_IN_FUNCTIONS; @@ -3299,11 +3297,12 @@ bool GLES2DecoderImpl::InitializeShaderTranslator() { switches::kEmulateShaderPrecision)) resources.WEBGL_debug_shader_precision = true; + ShShaderOutput shader_output_language = + ShaderTranslator::GetShaderOutputLanguageForContext( + feature_info_->gl_version_info()); + vertex_translator_ = shader_translator_cache()->GetTranslator( - GL_VERTEX_SHADER, - shader_spec, - &resources, - implementation_type, + GL_VERTEX_SHADER, shader_spec, &resources, shader_output_language, static_cast(driver_bug_workarounds)); if (!vertex_translator_.get()) { LOG(ERROR) << "Could not initialize vertex shader translator."; @@ -3312,10 +3311,7 @@ bool GLES2DecoderImpl::InitializeShaderTranslator() { } fragment_translator_ = shader_translator_cache()->GetTranslator( - GL_FRAGMENT_SHADER, - shader_spec, - &resources, - implementation_type, + GL_FRAGMENT_SHADER, shader_spec, &resources, shader_output_language, static_cast(driver_bug_workarounds)); if (!fragment_translator_.get()) { LOG(ERROR) << "Could not initialize fragment shader translator."; diff --git a/gpu/command_buffer/service/mocks.h b/gpu/command_buffer/service/mocks.h index 0b4164f33037bf..8391aeb29e9ffd 100644 --- a/gpu/command_buffer/service/mocks.h +++ b/gpu/command_buffer/service/mocks.h @@ -89,12 +89,12 @@ class MockShaderTranslator : public ShaderTranslatorInterface { public: MockShaderTranslator(); - MOCK_METHOD5(Init, bool( - sh::GLenum shader_type, - ShShaderSpec shader_spec, - const ShBuiltInResources* resources, - GlslImplementationType glsl_implementation_type, - ShCompileOptions driver_bug_workarounds)); + MOCK_METHOD5(Init, + bool(sh::GLenum shader_type, + ShShaderSpec shader_spec, + const ShBuiltInResources* resources, + ShShaderOutput shader_output_language, + ShCompileOptions driver_bug_workarounds)); MOCK_CONST_METHOD8(Translate, bool( const std::string& shader_source, std::string* info_log, diff --git a/gpu/command_buffer/service/shader_translator.cc b/gpu/command_buffer/service/shader_translator.cc index 04e12276afb372..b852d2aa929cb2 100644 --- a/gpu/command_buffer/service/shader_translator.cc +++ b/gpu/command_buffer/service/shader_translator.cc @@ -16,6 +16,7 @@ #include "base/trace_event/trace_event.h" #include "gpu/command_buffer/service/gpu_switches.h" #include "ui/gl/gl_implementation.h" +#include "ui/gl/gl_version_info.h" namespace gpu { namespace gles2 { @@ -90,6 +91,50 @@ void GetNameHashingInfo(ShHandle compiler, NameMap* name_map) { } // namespace +ShShaderOutput ShaderTranslator::GetShaderOutputLanguageForContext( + const gfx::GLVersionInfo& version_info) { + if (version_info.is_es) { + return SH_ESSL_OUTPUT; + } + + // Determine the GLSL version based on OpenGL specification. + + unsigned context_version = + version_info.major_version * 100 + version_info.minor_version * 10; + if (context_version >= 450) { + // OpenGL specs from 4.2 on specify that the core profile is "also + // guaranteed to support all previous versions of the OpenGL Shading + // Language back to version 1.40". For simplicity, we assume future + // specs do not unspecify this. If they did, they could unspecify + // glGetStringi(GL_SHADING_LANGUAGE_VERSION, k), too. + // Since current context >= 4.5, use GLSL 4.50 core. + return SH_GLSL_450_CORE_OUTPUT; + } else if (context_version == 440) { + return SH_GLSL_440_CORE_OUTPUT; + } else if (context_version == 430) { + return SH_GLSL_430_CORE_OUTPUT; + } else if (context_version == 420) { + return SH_GLSL_420_CORE_OUTPUT; + } else if (context_version == 410) { + return SH_GLSL_410_CORE_OUTPUT; + } else if (context_version == 400) { + return SH_GLSL_400_CORE_OUTPUT; + } else if (context_version == 330) { + return SH_GLSL_330_CORE_OUTPUT; + } else if (context_version == 320) { + return SH_GLSL_150_CORE_OUTPUT; + } else if (context_version == 310) { + return SH_GLSL_140_OUTPUT; + } else if (context_version == 300) { + return SH_GLSL_130_OUTPUT; + } + + // Before OpenGL 3.0 we use compatibility profile. Also for future + // specs between OpenGL 3.3 and OpenGL 4.0, at the time of writing, + // we use compatibility profile. + return SH_GLSL_COMPATIBILITY_OUTPUT; +} + ShaderTranslator::DestructionObserver::DestructionObserver() { } @@ -98,16 +143,14 @@ ShaderTranslator::DestructionObserver::~DestructionObserver() { ShaderTranslator::ShaderTranslator() : compiler_(NULL), - implementation_is_glsl_es_(false), driver_bug_workarounds_(static_cast(0)) { } -bool ShaderTranslator::Init( - GLenum shader_type, - ShShaderSpec shader_spec, - const ShBuiltInResources* resources, - ShaderTranslatorInterface::GlslImplementationType glsl_implementation_type, - ShCompileOptions driver_bug_workarounds) { +bool ShaderTranslator::Init(GLenum shader_type, + ShShaderSpec shader_spec, + const ShBuiltInResources* resources, + ShShaderOutput shader_output_language, + ShCompileOptions driver_bug_workarounds) { // Make sure Init is called only once. DCHECK(compiler_ == NULL); DCHECK(shader_type == GL_FRAGMENT_SHADER || shader_type == GL_VERTEX_SHADER); @@ -117,27 +160,12 @@ bool ShaderTranslator::Init( g_translator_initializer.Get(); - ShShaderOutput shader_output; - if (glsl_implementation_type == kGlslES) { - shader_output = SH_ESSL_OUTPUT; - } else { - // TODO(kbr): clean up the tests of shader_spec and - // gfx::GetGLImplementation(). crbug.com/471960 - if (shader_spec == SH_WEBGL2_SPEC || - gfx::GetGLImplementation() == - gfx::kGLImplementationDesktopGLCoreProfile) { - shader_output = SH_GLSL_410_CORE_OUTPUT; - } else { - shader_output = SH_GLSL_COMPATIBILITY_OUTPUT; - } - } { TRACE_EVENT0("gpu", "ShConstructCompiler"); - compiler_ = ShConstructCompiler( - shader_type, shader_spec, shader_output, resources); + compiler_ = ShConstructCompiler(shader_type, shader_spec, + shader_output_language, resources); } - implementation_is_glsl_es_ = (glsl_implementation_type == kGlslES); driver_bug_workarounds_ = driver_bug_workarounds; return compiler_ != NULL; } diff --git a/gpu/command_buffer/service/shader_translator.h b/gpu/command_buffer/service/shader_translator.h index 858014dc0494fb..6e7628ffa05f59 100644 --- a/gpu/command_buffer/service/shader_translator.h +++ b/gpu/command_buffer/service/shader_translator.h @@ -15,6 +15,10 @@ #include "gpu/gpu_export.h" #include "third_party/angle/include/GLSLANG/ShaderLang.h" +namespace gfx { +struct GLVersionInfo; +} + namespace gpu { namespace gles2 { @@ -31,19 +35,14 @@ class ShaderTranslatorInterface : public base::RefCounted { public: ShaderTranslatorInterface() {} - enum GlslImplementationType { - kGlsl, - kGlslES - }; // Initializes the translator. // Must be called once before using the translator object. - virtual bool Init( - sh::GLenum shader_type, - ShShaderSpec shader_spec, - const ShBuiltInResources* resources, - GlslImplementationType glsl_implementation_type, - ShCompileOptions driver_bug_workarounds) = 0; + virtual bool Init(sh::GLenum shader_type, + ShShaderSpec shader_spec, + const ShBuiltInResources* resources, + ShShaderOutput shader_output_language, + ShCompileOptions driver_bug_workarounds) = 0; // Translates the given shader source. // Returns true if translation is successful, false otherwise. @@ -88,11 +87,15 @@ class GPU_EXPORT ShaderTranslator ShaderTranslator(); + // Return shader output lanaguage type based on the context version. + static ShShaderOutput GetShaderOutputLanguageForContext( + const gfx::GLVersionInfo& context_version); + // Overridden from ShaderTranslatorInterface. bool Init(sh::GLenum shader_type, ShShaderSpec shader_spec, const ShBuiltInResources* resources, - GlslImplementationType glsl_implementation_type, + ShShaderOutput shader_output_language, ShCompileOptions driver_bug_workarounds) override; // Overridden from ShaderTranslatorInterface. @@ -116,7 +119,6 @@ class GPU_EXPORT ShaderTranslator int GetCompileOptions() const; ShHandle compiler_; - bool implementation_is_glsl_es_; ShCompileOptions driver_bug_workarounds_; base::ObserverList destruction_observers_; }; diff --git a/gpu/command_buffer/service/shader_translator_cache.cc b/gpu/command_buffer/service/shader_translator_cache.cc index 631a88e4142128..f78039fcac25d7 100644 --- a/gpu/command_buffer/service/shader_translator_cache.cc +++ b/gpu/command_buffer/service/shader_translator_cache.cc @@ -31,13 +31,10 @@ scoped_refptr ShaderTranslatorCache::GetTranslator( sh::GLenum shader_type, ShShaderSpec shader_spec, const ShBuiltInResources* resources, - ShaderTranslatorInterface::GlslImplementationType - glsl_implementation_type, + ShShaderOutput shader_output_language, ShCompileOptions driver_bug_workarounds) { - ShaderTranslatorInitParams params(shader_type, - shader_spec, - *resources, - glsl_implementation_type, + ShaderTranslatorInitParams params(shader_type, shader_spec, *resources, + shader_output_language, driver_bug_workarounds); Cache::iterator it = cache_.find(params); @@ -46,8 +43,7 @@ scoped_refptr ShaderTranslatorCache::GetTranslator( ShaderTranslator* translator = new ShaderTranslator(); if (translator->Init(shader_type, shader_spec, resources, - glsl_implementation_type, - driver_bug_workarounds)) { + shader_output_language, driver_bug_workarounds)) { cache_[params] = translator; translator->AddDestructionObserver(this); return translator; diff --git a/gpu/command_buffer/service/shader_translator_cache.h b/gpu/command_buffer/service/shader_translator_cache.h index 6b0b1a57ef35cf..471b081ac728cf 100644 --- a/gpu/command_buffer/service/shader_translator_cache.h +++ b/gpu/command_buffer/service/shader_translator_cache.h @@ -35,8 +35,7 @@ class GPU_EXPORT ShaderTranslatorCache sh::GLenum shader_type, ShShaderSpec shader_spec, const ShBuiltInResources* resources, - ShaderTranslatorInterface::GlslImplementationType - glsl_implementation_type, + ShShaderOutput shader_output_language, ShCompileOptions driver_bug_workarounds); private: @@ -49,21 +48,19 @@ class GPU_EXPORT ShaderTranslatorCache sh::GLenum shader_type; ShShaderSpec shader_spec; ShBuiltInResources resources; - ShaderTranslatorInterface::GlslImplementationType - glsl_implementation_type; + ShShaderOutput shader_output_language; ShCompileOptions driver_bug_workarounds; ShaderTranslatorInitParams(sh::GLenum shader_type, ShShaderSpec shader_spec, const ShBuiltInResources& resources, - ShaderTranslatorInterface::GlslImplementationType - glsl_implementation_type, + ShShaderOutput shader_output_language, ShCompileOptions driver_bug_workarounds) { memset(this, 0, sizeof(*this)); this->shader_type = shader_type; this->shader_spec = shader_spec; this->resources = resources; - this->glsl_implementation_type = glsl_implementation_type; + this->shader_output_language = shader_output_language; this->driver_bug_workarounds = driver_bug_workarounds; } diff --git a/gpu/command_buffer/service/shader_translator_cache_unittest.cc b/gpu/command_buffer/service/shader_translator_cache_unittest.cc index c5233e510e8351..eefd2e69a7a7ac 100644 --- a/gpu/command_buffer/service/shader_translator_cache_unittest.cc +++ b/gpu/command_buffer/service/shader_translator_cache_unittest.cc @@ -30,17 +30,11 @@ TEST(ShaderTranslatorCacheTest, InitParamComparable) { memset(a_storage, 55, sizeof(a_storage)); ShaderTranslatorCache::ShaderTranslatorInitParams* a = new (&a_storage) ShaderTranslatorCache::ShaderTranslatorInitParams( - GL_VERTEX_SHADER, - SH_GLES2_SPEC, - a_resources, - ShaderTranslatorInterface::kGlslES, + GL_VERTEX_SHADER, SH_GLES2_SPEC, a_resources, SH_ESSL_OUTPUT, driver_bug_workarounds); ShaderTranslatorCache::ShaderTranslatorInitParams b( - GL_VERTEX_SHADER, - SH_GLES2_SPEC, - b_resources, - ShaderTranslatorInterface::kGlslES, + GL_VERTEX_SHADER, SH_GLES2_SPEC, b_resources, SH_ESSL_OUTPUT, driver_bug_workarounds); EXPECT_TRUE(*a == b); diff --git a/gpu/command_buffer/service/shader_translator_unittest.cc b/gpu/command_buffer/service/shader_translator_unittest.cc index 5f6173818f922b..0c60b44c5a6385 100644 --- a/gpu/command_buffer/service/shader_translator_unittest.cc +++ b/gpu/command_buffer/service/shader_translator_unittest.cc @@ -6,6 +6,7 @@ #include "gpu/command_buffer/service/shader_translator.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gl/gl_version_info.h" namespace gpu { namespace gles2 { @@ -13,6 +14,9 @@ namespace gles2 { class ShaderTranslatorTest : public testing::Test { public: ShaderTranslatorTest() { + shader_output_language_ = + ShaderTranslator::GetShaderOutputLanguageForContext( + gfx::GLVersionInfo("2.0", "", "")); } ~ShaderTranslatorTest() override {} @@ -27,14 +31,12 @@ class ShaderTranslatorTest : public testing::Test { vertex_translator_ = new ShaderTranslator(); fragment_translator_ = new ShaderTranslator(); - ASSERT_TRUE(vertex_translator_->Init( - GL_VERTEX_SHADER, SH_GLES2_SPEC, &resources, - ShaderTranslatorInterface::kGlsl, - SH_EMULATE_BUILT_IN_FUNCTIONS)); - ASSERT_TRUE(fragment_translator_->Init( - GL_FRAGMENT_SHADER, SH_GLES2_SPEC, &resources, - ShaderTranslatorInterface::kGlsl, - static_cast(0))); + ASSERT_TRUE(vertex_translator_->Init(GL_VERTEX_SHADER, SH_GLES2_SPEC, + &resources, shader_output_language_, + SH_EMULATE_BUILT_IN_FUNCTIONS)); + ASSERT_TRUE(fragment_translator_->Init(GL_FRAGMENT_SHADER, SH_GLES2_SPEC, + &resources, shader_output_language_, + static_cast(0))); } void TearDown() override { vertex_translator_ = NULL; @@ -43,6 +45,7 @@ class ShaderTranslatorTest : public testing::Test { scoped_refptr vertex_translator_; scoped_refptr fragment_translator_; + ShShaderOutput shader_output_language_; }; TEST_F(ShaderTranslatorTest, ValidVertexShader) { @@ -290,19 +293,16 @@ TEST_F(ShaderTranslatorTest, OptionsString) { ShBuiltInResources resources; ShInitBuiltInResources(&resources); - ASSERT_TRUE(translator_1->Init( - GL_VERTEX_SHADER, SH_GLES2_SPEC, &resources, - ShaderTranslatorInterface::kGlsl, - SH_EMULATE_BUILT_IN_FUNCTIONS)); - ASSERT_TRUE(translator_2->Init( - GL_FRAGMENT_SHADER, SH_GLES2_SPEC, &resources, - ShaderTranslatorInterface::kGlsl, - static_cast(0))); + ASSERT_TRUE(translator_1->Init(GL_VERTEX_SHADER, SH_GLES2_SPEC, &resources, + SH_GLSL_150_CORE_OUTPUT, + SH_EMULATE_BUILT_IN_FUNCTIONS)); + ASSERT_TRUE(translator_2->Init(GL_FRAGMENT_SHADER, SH_GLES2_SPEC, &resources, + SH_GLSL_150_CORE_OUTPUT, + static_cast(0))); resources.EXT_draw_buffers = 1; - ASSERT_TRUE(translator_3->Init( - GL_VERTEX_SHADER, SH_GLES2_SPEC, &resources, - ShaderTranslatorInterface::kGlsl, - SH_EMULATE_BUILT_IN_FUNCTIONS)); + ASSERT_TRUE(translator_3->Init(GL_VERTEX_SHADER, SH_GLES2_SPEC, &resources, + SH_GLSL_150_CORE_OUTPUT, + SH_EMULATE_BUILT_IN_FUNCTIONS)); std::string options_1( translator_1->GetStringForOptionsThatWouldAffectCompilation()); @@ -319,6 +319,113 @@ TEST_F(ShaderTranslatorTest, OptionsString) { EXPECT_NE(options_3, options_4); } +class ShaderTranslatorOutputVersionTest + : public testing::TestWithParam> { +}; + +TEST_P(ShaderTranslatorOutputVersionTest, HasCorrectOutputGLSLVersion) { + // Test that translating to a shader targeting certain OpenGL context version + // (version string in test param tuple index 0) produces a GLSL shader that + // contains correct version string for that context (version directive + // in test param tuple index 1). + + const char* kShader = + "attribute vec4 vPosition;\n" + "void main() {\n" + " gl_Position = vPosition;\n" + "}"; + + gfx::GLVersionInfo output_context_version(testing::get<0>(GetParam()), "", + ""); + + scoped_refptr translator = new ShaderTranslator(); + ShBuiltInResources resources; + ShInitBuiltInResources(&resources); + ShCompileOptions compile_options = SH_OBJECT_CODE; + ShShaderOutput shader_output_language = + ShaderTranslator::GetShaderOutputLanguageForContext( + output_context_version); + ASSERT_TRUE(translator->Init(GL_VERTEX_SHADER, SH_GLES2_SPEC, &resources, + shader_output_language, compile_options)); + + std::string translated_source; + int shader_version; + EXPECT_TRUE(translator->Translate(kShader, nullptr, &translated_source, + &shader_version, nullptr, nullptr, nullptr, + nullptr)); + + std::string expected_version_directive = testing::get<1>(GetParam()); + if (expected_version_directive.empty()) { + EXPECT_TRUE(translated_source.find("#version") == std::string::npos) + << "Translation was:\n" << translated_source; + } else { + EXPECT_TRUE(translated_source.find(expected_version_directive) != + std::string::npos) + << "Translation was:\n" << translated_source; + } +} + +// For some compilers, using make_tuple("a", "bb") would end up +// instantiating make_tuple. This does not work. +namespace { +testing::tuple make_gl_glsl_tuple( + const char* gl_version, + const char* glsl_version_directive) { + return testing::make_tuple(gl_version, glsl_version_directive); +} +} + +// Test data for the above test. OpenGL specifications specify a +// certain version of GLSL to be guaranteed to be supported. Test +// that ShaderTranslator produces a GLSL shader with the exact +// specified GLSL version for each known OpenGL version. +INSTANTIATE_TEST_CASE_P( + KnownOpenGLContexts, + ShaderTranslatorOutputVersionTest, + testing::Values(make_gl_glsl_tuple("4.5", "#version 450\n"), + make_gl_glsl_tuple("4.4", "#version 440\n"), + make_gl_glsl_tuple("4.3", "#version 430\n"), + make_gl_glsl_tuple("4.2", "#version 420\n"), + make_gl_glsl_tuple("4.1", "#version 410\n"), + make_gl_glsl_tuple("4.0", "#version 400\n"), + make_gl_glsl_tuple("3.3", "#version 330\n"), + make_gl_glsl_tuple("3.2", "#version 150\n"), + make_gl_glsl_tuple("3.1", "#version 140\n"), + make_gl_glsl_tuple("3.0", "#version 130\n"))); + +// Test data for the above test. Check that early OpenGL contexts get +// GLSL compatibility profile shader, e.g. shader has no #version +// directive. Also check that future version 3.3+ OpenGL contexts get +// similar shader. We do not expect that future 3.3+ specs contain +// the "all eariler GLSL versions" clause, since 3.3 did not contain +// it either. +INSTANTIATE_TEST_CASE_P(OldOrUnknownOpenGLContexts, + ShaderTranslatorOutputVersionTest, + testing::Values(make_gl_glsl_tuple("3.4", ""), + make_gl_glsl_tuple("2.0", ""))); + +// Test data for the above test. Cases for the future OpenGL versions. The +// code assumes that the future OpenGL specs specify the clause that all +// earlier GLSL versions are supported. We select the highest GLSL +// version known at the time of writing. +INSTANTIATE_TEST_CASE_P( + BackwardsCompatibleFutureOpenGLContexts, + ShaderTranslatorOutputVersionTest, + testing::Values(make_gl_glsl_tuple("5.0", "#version 450\n"), + make_gl_glsl_tuple("4.6", "#version 450\n"))); + +// Test data for the above test. Check that for the OpenGL ES output +// contexts, the shader is such that GLSL 1.0 is used. The translator +// selects GLSL 1.0 by not output any version at the moment, though we +// do not know if that would be correct for the future OpenGL ES specs. +INSTANTIATE_TEST_CASE_P(OpenGLESContexts, + ShaderTranslatorOutputVersionTest, + testing::Values(make_gl_glsl_tuple("opengl es 2.0", ""), + make_gl_glsl_tuple("opengl es 3.0", ""), + make_gl_glsl_tuple("opengl es 3.1", ""), + make_gl_glsl_tuple("opengl es 3.2", + ""))); + } // namespace gles2 } // namespace gpu