// Copyright (c) 2012 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 "gpu/command_buffer/service/test_helper.h" #include #include #include #include #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_tokenizer.h" #include "gpu/command_buffer/service/buffer_manager.h" #include "gpu/command_buffer/service/error_state_mock.h" #include "gpu/command_buffer/service/feature_info.h" #include "gpu/command_buffer/service/gl_utils.h" #include "gpu/command_buffer/service/gpu_switches.h" #include "gpu/command_buffer/service/mocks.h" #include "gpu/command_buffer/service/program_manager.h" #include "gpu/command_buffer/service/texture_manager.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; using ::testing::InSequence; using ::testing::MatcherCast; using ::testing::Pointee; using ::testing::NotNull; using ::testing::Return; using ::testing::SetArrayArgument; using ::testing::SetArgumentPointee; using ::testing::StrEq; using ::testing::StrictMock; namespace gpu { namespace gles2 { namespace { template T ConstructShaderVariable( GLenum type, GLint array_size, GLenum precision, bool static_use, const std::string& name) { T var; var.type = type; var.arraySize = array_size; var.precision = precision; var.staticUse = static_use; var.name = name; var.mappedName = name; // No name hashing. return var; } } // namespace anonymous // GCC requires these declarations, but MSVC requires they not be present #ifndef COMPILER_MSVC const GLuint TestHelper::kServiceBlackTexture2dId; const GLuint TestHelper::kServiceDefaultTexture2dId; const GLuint TestHelper::kServiceBlackTexture3dId; const GLuint TestHelper::kServiceDefaultTexture3dId; const GLuint TestHelper::kServiceBlackTexture2dArrayId; const GLuint TestHelper::kServiceDefaultTexture2dArrayId; const GLuint TestHelper::kServiceBlackTextureCubemapId; const GLuint TestHelper::kServiceDefaultTextureCubemapId; const GLuint TestHelper::kServiceBlackExternalTextureId; const GLuint TestHelper::kServiceDefaultExternalTextureId; const GLuint TestHelper::kServiceBlackRectangleTextureId; const GLuint TestHelper::kServiceDefaultRectangleTextureId; const GLint TestHelper::kMaxSamples; const GLint TestHelper::kMaxRenderbufferSize; const GLint TestHelper::kMaxTextureSize; const GLint TestHelper::kMaxCubeMapTextureSize; const GLint TestHelper::kMaxRectangleTextureSize; const GLint TestHelper::kMax3DTextureSize; const GLint TestHelper::kNumVertexAttribs; const GLint TestHelper::kNumTextureUnits; const GLint TestHelper::kMaxTextureImageUnits; const GLint TestHelper::kMaxVertexTextureImageUnits; const GLint TestHelper::kMaxFragmentUniformVectors; const GLint TestHelper::kMaxFragmentUniformComponents; const GLint TestHelper::kMaxVaryingVectors; const GLint TestHelper::kMaxVaryingFloats; const GLint TestHelper::kMaxVertexUniformVectors; const GLint TestHelper::kMaxVertexUniformComponents; #endif std::vector TestHelper::split_extensions_; void TestHelper::SetupTextureInitializationExpectations( ::gfx::MockGLInterface* gl, GLenum target, bool use_default_textures) { InSequence sequence; bool needs_initialization = (target != GL_TEXTURE_EXTERNAL_OES); bool needs_faces = (target == GL_TEXTURE_CUBE_MAP); bool is_3d_or_2d_array_target = (target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY); static GLuint texture_2d_ids[] = { kServiceBlackTexture2dId, kServiceDefaultTexture2dId }; static GLuint texture_3d_ids[] = { kServiceBlackTexture3dId, kServiceDefaultTexture3dId }; static GLuint texture_2d_array_ids[] = { kServiceBlackTexture2dArrayId, kServiceDefaultTexture2dArrayId }; static GLuint texture_cube_map_ids[] = { kServiceBlackTextureCubemapId, kServiceDefaultTextureCubemapId }; static GLuint texture_external_oes_ids[] = { kServiceBlackExternalTextureId, kServiceDefaultExternalTextureId }; static GLuint texture_rectangle_arb_ids[] = { kServiceBlackRectangleTextureId, kServiceDefaultRectangleTextureId }; const GLuint* texture_ids = NULL; switch (target) { case GL_TEXTURE_2D: texture_ids = &texture_2d_ids[0]; break; case GL_TEXTURE_3D: texture_ids = &texture_3d_ids[0]; break; case GL_TEXTURE_2D_ARRAY: texture_ids = &texture_2d_array_ids[0]; break; case GL_TEXTURE_CUBE_MAP: texture_ids = &texture_cube_map_ids[0]; break; case GL_TEXTURE_EXTERNAL_OES: texture_ids = &texture_external_oes_ids[0]; break; case GL_TEXTURE_RECTANGLE_ARB: texture_ids = &texture_rectangle_arb_ids[0]; break; default: NOTREACHED(); } int array_size = use_default_textures ? 2 : 1; EXPECT_CALL(*gl, GenTextures(array_size, _)) .WillOnce(SetArrayArgument<1>(texture_ids, texture_ids + array_size)) .RetiresOnSaturation(); for (int ii = 0; ii < array_size; ++ii) { EXPECT_CALL(*gl, BindTexture(target, texture_ids[ii])) .Times(1) .RetiresOnSaturation(); if (needs_initialization) { if (needs_faces) { static GLenum faces[] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, }; for (size_t ii = 0; ii < arraysize(faces); ++ii) { EXPECT_CALL(*gl, TexImage2D(faces[ii], 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, _)) .Times(1) .RetiresOnSaturation(); } } else { if (is_3d_or_2d_array_target) { EXPECT_CALL(*gl, TexImage3D(target, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, _)) .Times(1) .RetiresOnSaturation(); } else { EXPECT_CALL(*gl, TexImage2D(target, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, _)) .Times(1) .RetiresOnSaturation(); } } } } EXPECT_CALL(*gl, BindTexture(target, 0)) .Times(1) .RetiresOnSaturation(); } void TestHelper::SetupTextureManagerInitExpectations( ::gfx::MockGLInterface* gl, bool is_es3_enabled, const char* extensions, bool use_default_textures) { InSequence sequence; SetupTextureInitializationExpectations( gl, GL_TEXTURE_2D, use_default_textures); SetupTextureInitializationExpectations( gl, GL_TEXTURE_CUBE_MAP, use_default_textures); if (is_es3_enabled) { SetupTextureInitializationExpectations( gl, GL_TEXTURE_3D, use_default_textures); SetupTextureInitializationExpectations( gl, GL_TEXTURE_2D_ARRAY, use_default_textures); } bool ext_image_external = false; bool arb_texture_rectangle = false; base::CStringTokenizer t(extensions, extensions + strlen(extensions), " "); while (t.GetNext()) { if (t.token() == "GL_OES_EGL_image_external") { ext_image_external = true; break; } if (t.token() == "GL_ARB_texture_rectangle") { arb_texture_rectangle = true; break; } } if (ext_image_external) { SetupTextureInitializationExpectations( gl, GL_TEXTURE_EXTERNAL_OES, use_default_textures); } if (arb_texture_rectangle) { SetupTextureInitializationExpectations( gl, GL_TEXTURE_RECTANGLE_ARB, use_default_textures); } } void TestHelper::SetupTextureDestructionExpectations( ::gfx::MockGLInterface* gl, GLenum target, bool use_default_textures) { if (!use_default_textures) return; GLuint texture_id = 0; switch (target) { case GL_TEXTURE_2D: texture_id = kServiceDefaultTexture2dId; break; case GL_TEXTURE_3D: texture_id = kServiceDefaultTexture3dId; break; case GL_TEXTURE_2D_ARRAY: texture_id = kServiceDefaultTexture2dArrayId; break; case GL_TEXTURE_CUBE_MAP: texture_id = kServiceDefaultTextureCubemapId; break; case GL_TEXTURE_EXTERNAL_OES: texture_id = kServiceDefaultExternalTextureId; break; case GL_TEXTURE_RECTANGLE_ARB: texture_id = kServiceDefaultRectangleTextureId; break; default: NOTREACHED(); } EXPECT_CALL(*gl, DeleteTextures(1, Pointee(texture_id))) .Times(1) .RetiresOnSaturation(); } void TestHelper::SetupTextureManagerDestructionExpectations( ::gfx::MockGLInterface* gl, bool is_es3_enabled, const char* extensions, bool use_default_textures) { SetupTextureDestructionExpectations(gl, GL_TEXTURE_2D, use_default_textures); SetupTextureDestructionExpectations( gl, GL_TEXTURE_CUBE_MAP, use_default_textures); if (is_es3_enabled) { SetupTextureDestructionExpectations( gl, GL_TEXTURE_3D, use_default_textures); SetupTextureDestructionExpectations( gl, GL_TEXTURE_2D_ARRAY,use_default_textures); } bool ext_image_external = false; bool arb_texture_rectangle = false; base::CStringTokenizer t(extensions, extensions + strlen(extensions), " "); while (t.GetNext()) { if (t.token() == "GL_OES_EGL_image_external") { ext_image_external = true; break; } if (t.token() == "GL_ARB_texture_rectangle") { arb_texture_rectangle = true; break; } } if (ext_image_external) { SetupTextureDestructionExpectations( gl, GL_TEXTURE_EXTERNAL_OES, use_default_textures); } if (arb_texture_rectangle) { SetupTextureDestructionExpectations( gl, GL_TEXTURE_RECTANGLE_ARB, use_default_textures); } EXPECT_CALL(*gl, DeleteTextures(TextureManager::kNumDefaultTextures, _)) .Times(1) .RetiresOnSaturation(); } void TestHelper::SetupContextGroupInitExpectations( ::gfx::MockGLInterface* gl, const DisallowedFeatures& disallowed_features, const char* extensions, const char* gl_version, bool bind_generates_resource) { InSequence sequence; SetupFeatureInfoInitExpectationsWithGLVersion(gl, extensions, "", gl_version); gfx::GLVersionInfo gl_info(gl_version, "", extensions); EXPECT_CALL(*gl, GetIntegerv(GL_MAX_RENDERBUFFER_SIZE, _)) .WillOnce(SetArgumentPointee<1>(kMaxRenderbufferSize)) .RetiresOnSaturation(); if (strstr(extensions, "GL_EXT_framebuffer_multisample") || strstr(extensions, "GL_EXT_multisampled_render_to_texture") || gl_info.is_es3) { EXPECT_CALL(*gl, GetIntegerv(GL_MAX_SAMPLES, _)) .WillOnce(SetArgumentPointee<1>(kMaxSamples)) .RetiresOnSaturation(); } else if (strstr(extensions, "GL_IMG_multisampled_render_to_texture")) { EXPECT_CALL(*gl, GetIntegerv(GL_MAX_SAMPLES_IMG, _)) .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(); EXPECT_CALL(*gl, GetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, _)) .WillOnce(SetArgumentPointee<1>(kNumTextureUnits)) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetIntegerv(GL_MAX_TEXTURE_SIZE, _)) .WillOnce(SetArgumentPointee<1>(kMaxTextureSize)) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, _)) .WillOnce(SetArgumentPointee<1>(kMaxCubeMapTextureSize)) .RetiresOnSaturation(); if (gl_info.IsES3Capable()) { EXPECT_CALL(*gl, GetIntegerv(GL_MAX_3D_TEXTURE_SIZE, _)) .WillOnce(SetArgumentPointee<1>(kMax3DTextureSize)) .RetiresOnSaturation(); } if (strstr(extensions, "GL_ARB_texture_rectangle")) { EXPECT_CALL(*gl, GetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE, _)) .WillOnce(SetArgumentPointee<1>(kMaxRectangleTextureSize)) .RetiresOnSaturation(); } EXPECT_CALL(*gl, GetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, _)) .WillOnce(SetArgumentPointee<1>(kMaxTextureImageUnits)) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, _)) .WillOnce(SetArgumentPointee<1>(kMaxVertexTextureImageUnits)) .RetiresOnSaturation(); if (gl_info.is_es) { EXPECT_CALL(*gl, GetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, _)) .WillOnce(SetArgumentPointee<1>(kMaxFragmentUniformVectors)) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetIntegerv(GL_MAX_VARYING_VECTORS, _)) .WillOnce(SetArgumentPointee<1>(kMaxVaryingVectors)) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, _)) .WillOnce(SetArgumentPointee<1>(kMaxVertexUniformVectors)) .RetiresOnSaturation(); } else { EXPECT_CALL(*gl, GetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, _)) .WillOnce(SetArgumentPointee<1>(kMaxFragmentUniformComponents)) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetIntegerv(GL_MAX_VARYING_FLOATS, _)) .WillOnce(SetArgumentPointee<1>(kMaxVaryingFloats)) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, _)) .WillOnce(SetArgumentPointee<1>(kMaxVertexUniformComponents)) .RetiresOnSaturation(); } bool use_default_textures = bind_generates_resource; SetupTextureManagerInitExpectations( gl, false, extensions, use_default_textures); } void TestHelper::SetupFeatureInfoInitExpectations( ::gfx::MockGLInterface* gl, const char* extensions) { SetupFeatureInfoInitExpectationsWithGLVersion(gl, extensions, "", ""); } void TestHelper::SetupFeatureInfoInitExpectationsWithGLVersion( ::gfx::MockGLInterface* gl, const char* extensions, const char* gl_renderer, const char* gl_version) { InSequence sequence; EXPECT_CALL(*gl, GetString(GL_VERSION)) .WillOnce(Return(reinterpret_cast(gl_version))) .RetiresOnSaturation(); // Persistent storage is needed for the split extension string. split_extensions_.clear(); if (extensions) { split_extensions_ = base::SplitString( extensions, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); } gfx::GLVersionInfo gl_info(gl_version, gl_renderer, extensions); if (!gl_info.is_es && gl_info.major_version >= 3) { EXPECT_CALL(*gl, GetIntegerv(GL_NUM_EXTENSIONS, _)) .WillOnce(SetArgumentPointee<1>(split_extensions_.size())) .RetiresOnSaturation(); for (size_t ii = 0; ii < split_extensions_.size(); ++ii) { EXPECT_CALL(*gl, GetStringi(GL_EXTENSIONS, ii)) .WillOnce(Return( reinterpret_cast(split_extensions_[ii].c_str()))) .RetiresOnSaturation(); } } else { EXPECT_CALL(*gl, GetString(GL_EXTENSIONS)) .WillOnce(Return(reinterpret_cast(extensions))) .RetiresOnSaturation(); } EXPECT_CALL(*gl, GetString(GL_VERSION)) .WillOnce(Return(reinterpret_cast(gl_version))) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetString(GL_RENDERER)) .WillOnce(Return(reinterpret_cast(gl_renderer))) .RetiresOnSaturation(); if ((strstr(extensions, "GL_ARB_texture_float") || gl_info.is_desktop_core_profile) || (gl_info.is_es3 && strstr(extensions, "GL_EXT_color_buffer_float"))) { static const GLuint tx_ids[] = {101, 102}; static const GLuint fb_ids[] = {103, 104}; const GLsizei width = 16; EXPECT_CALL(*gl, GetIntegerv(GL_FRAMEBUFFER_BINDING, _)) .WillOnce(SetArgumentPointee<1>(fb_ids[0])) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetIntegerv(GL_TEXTURE_BINDING_2D, _)) .WillOnce(SetArgumentPointee<1>(tx_ids[0])) .RetiresOnSaturation(); EXPECT_CALL(*gl, GenTextures(1, _)) .WillOnce(SetArrayArgument<1>(tx_ids + 1, tx_ids + 2)) .RetiresOnSaturation(); EXPECT_CALL(*gl, GenFramebuffersEXT(1, _)) .WillOnce(SetArrayArgument<1>(fb_ids + 1, fb_ids + 2)) .RetiresOnSaturation(); EXPECT_CALL(*gl, BindTexture(GL_TEXTURE_2D, tx_ids[1])) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, width, 0, GL_RGBA, GL_FLOAT, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, BindFramebufferEXT(GL_FRAMEBUFFER, fb_ids[1])) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, FramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tx_ids[1], 0)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, CheckFramebufferStatusEXT(GL_FRAMEBUFFER)) .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE)) .RetiresOnSaturation(); EXPECT_CALL(*gl, TexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, width, width, 0, GL_RGB, GL_FLOAT, _)) .Times(1) .RetiresOnSaturation(); if (gl_info.is_es3) { EXPECT_CALL(*gl, CheckFramebufferStatusEXT(GL_FRAMEBUFFER)) .WillOnce(Return(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)) .RetiresOnSaturation(); } else { EXPECT_CALL(*gl, CheckFramebufferStatusEXT(GL_FRAMEBUFFER)) .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE)) .RetiresOnSaturation(); } EXPECT_CALL(*gl, DeleteFramebuffersEXT(1, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, DeleteTextures(1, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, BindFramebufferEXT(GL_FRAMEBUFFER, fb_ids[0])) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, BindTexture(GL_TEXTURE_2D, tx_ids[0])) .Times(1) .RetiresOnSaturation(); #if DCHECK_IS_ON() EXPECT_CALL(*gl, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); #endif } if (strstr(extensions, "GL_EXT_draw_buffers") || strstr(extensions, "GL_ARB_draw_buffers") || (gl_info.is_es3 && strstr(extensions, "GL_NV_draw_buffers")) || gl_info.is_desktop_core_profile) { EXPECT_CALL(*gl, GetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, _)) .WillOnce(SetArgumentPointee<1>(8)) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetIntegerv(GL_MAX_DRAW_BUFFERS_ARB, _)) .WillOnce(SetArgumentPointee<1>(8)) .RetiresOnSaturation(); } if (gl_info.is_es3 || gl_info.is_desktop_core_profile || strstr(extensions, "GL_EXT_texture_rg") || (strstr(extensions, "GL_ARB_texture_rg"))) { static const GLuint tx_ids[] = {101, 102}; static const GLuint fb_ids[] = {103, 104}; const GLsizei width = 1; EXPECT_CALL(*gl, GetIntegerv(GL_FRAMEBUFFER_BINDING, _)) .WillOnce(SetArgumentPointee<1>(fb_ids[0])) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetIntegerv(GL_TEXTURE_BINDING_2D, _)) .WillOnce(SetArgumentPointee<1>(tx_ids[0])) .RetiresOnSaturation(); EXPECT_CALL(*gl, GenTextures(1, _)) .WillOnce(SetArrayArgument<1>(tx_ids + 1, tx_ids + 2)) .RetiresOnSaturation(); EXPECT_CALL(*gl, BindTexture(GL_TEXTURE_2D, tx_ids[1])) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, TexImage2D(GL_TEXTURE_2D, 0, _, width, width, 0, GL_RED_EXT, GL_UNSIGNED_BYTE, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, GenFramebuffersEXT(1, _)) .WillOnce(SetArrayArgument<1>(fb_ids + 1, fb_ids + 2)) .RetiresOnSaturation(); EXPECT_CALL(*gl, BindFramebufferEXT(GL_FRAMEBUFFER, fb_ids[1])) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, FramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tx_ids[1], 0)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, CheckFramebufferStatusEXT(GL_FRAMEBUFFER)) .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE)) .RetiresOnSaturation(); EXPECT_CALL(*gl, DeleteFramebuffersEXT(1, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, DeleteTextures(1, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, BindFramebufferEXT(GL_FRAMEBUFFER, fb_ids[0])) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, BindTexture(GL_TEXTURE_2D, tx_ids[0])) .Times(1) .RetiresOnSaturation(); #if DCHECK_IS_ON() EXPECT_CALL(*gl, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); #endif } } void TestHelper::SetupExpectationsForClearingUniforms( ::gfx::MockGLInterface* gl, UniformInfo* uniforms, size_t num_uniforms) { for (size_t ii = 0; ii < num_uniforms; ++ii) { const UniformInfo& info = uniforms[ii]; switch (info.type) { case GL_FLOAT: EXPECT_CALL(*gl, Uniform1fv(info.real_location, info.size, _)) .Times(1) .RetiresOnSaturation(); break; case GL_FLOAT_VEC2: EXPECT_CALL(*gl, Uniform2fv(info.real_location, info.size, _)) .Times(1) .RetiresOnSaturation(); break; case GL_FLOAT_VEC3: EXPECT_CALL(*gl, Uniform3fv(info.real_location, info.size, _)) .Times(1) .RetiresOnSaturation(); break; case GL_FLOAT_VEC4: EXPECT_CALL(*gl, Uniform4fv(info.real_location, info.size, _)) .Times(1) .RetiresOnSaturation(); break; case GL_INT: case GL_BOOL: case GL_SAMPLER_2D: case GL_SAMPLER_CUBE: case GL_SAMPLER_EXTERNAL_OES: case GL_SAMPLER_3D_OES: case GL_SAMPLER_2D_RECT_ARB: case GL_SAMPLER_2D_ARRAY: EXPECT_CALL(*gl, Uniform1iv(info.real_location, info.size, _)) .Times(1) .RetiresOnSaturation(); break; case GL_UNSIGNED_INT: EXPECT_CALL(*gl, Uniform1uiv(info.real_location, info.size, _)) .Times(1) .RetiresOnSaturation(); break; case GL_INT_VEC2: case GL_BOOL_VEC2: EXPECT_CALL(*gl, Uniform2iv(info.real_location, info.size, _)) .Times(1) .RetiresOnSaturation(); break; case GL_UNSIGNED_INT_VEC2: EXPECT_CALL(*gl, Uniform2uiv(info.real_location, info.size, _)) .Times(1) .RetiresOnSaturation(); break; case GL_INT_VEC3: case GL_BOOL_VEC3: EXPECT_CALL(*gl, Uniform3iv(info.real_location, info.size, _)) .Times(1) .RetiresOnSaturation(); break; case GL_UNSIGNED_INT_VEC3: EXPECT_CALL(*gl, Uniform3uiv(info.real_location, info.size, _)) .Times(1) .RetiresOnSaturation(); break; case GL_INT_VEC4: case GL_BOOL_VEC4: EXPECT_CALL(*gl, Uniform4iv(info.real_location, info.size, _)) .Times(1) .RetiresOnSaturation(); break; case GL_UNSIGNED_INT_VEC4: EXPECT_CALL(*gl, Uniform4uiv(info.real_location, info.size, _)) .Times(1) .RetiresOnSaturation(); break; case GL_FLOAT_MAT2: EXPECT_CALL(*gl, UniformMatrix2fv( info.real_location, info.size, false, _)) .Times(1) .RetiresOnSaturation(); break; case GL_FLOAT_MAT3: EXPECT_CALL(*gl, UniformMatrix3fv( info.real_location, info.size, false, _)) .Times(1) .RetiresOnSaturation(); break; case GL_FLOAT_MAT4: EXPECT_CALL(*gl, UniformMatrix4fv( info.real_location, info.size, false, _)) .Times(1) .RetiresOnSaturation(); break; default: NOTREACHED(); break; } } } void TestHelper::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) { EXPECT_CALL(*gl, GetProgramiv(service_id, GL_LINK_STATUS, _)) .WillOnce(SetArgumentPointee<2>(1)) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetProgramiv(service_id, GL_INFO_LOG_LENGTH, _)) .WillOnce(SetArgumentPointee<2>(0)) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetProgramiv(service_id, GL_ACTIVE_ATTRIBUTES, _)) .WillOnce(SetArgumentPointee<2>(num_attribs)) .RetiresOnSaturation(); size_t max_attrib_len = 0; for (size_t ii = 0; ii < num_attribs; ++ii) { size_t len = strlen(attribs[ii].name) + 1; max_attrib_len = std::max(max_attrib_len, len); } EXPECT_CALL(*gl, GetProgramiv(service_id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, _)) .WillOnce(SetArgumentPointee<2>(max_attrib_len)) .RetiresOnSaturation(); for (size_t ii = 0; ii < num_attribs; ++ii) { const AttribInfo& info = attribs[ii]; EXPECT_CALL(*gl, GetActiveAttrib(service_id, ii, max_attrib_len, _, _, _, _)) .WillOnce(DoAll( SetArgumentPointee<3>(strlen(info.name)), SetArgumentPointee<4>(info.size), SetArgumentPointee<5>(info.type), SetArrayArgument<6>(info.name, info.name + strlen(info.name) + 1))) .RetiresOnSaturation(); if (!ProgramManager::HasBuiltInPrefix(info.name)) { EXPECT_CALL(*gl, GetAttribLocation(service_id, StrEq(info.name))) .WillOnce(Return(info.location)) .RetiresOnSaturation(); } } EXPECT_CALL(*gl, GetProgramiv(service_id, GL_ACTIVE_UNIFORMS, _)) .WillOnce(SetArgumentPointee<2>(num_uniforms)) .RetiresOnSaturation(); if (num_uniforms > 0) { size_t max_uniform_len = 0; for (size_t ii = 0; ii < num_uniforms; ++ii) { size_t len = strlen(uniforms[ii].name) + 1; max_uniform_len = std::max(max_uniform_len, len); } EXPECT_CALL(*gl, GetProgramiv(service_id, GL_ACTIVE_UNIFORM_MAX_LENGTH, _)) .WillOnce(SetArgumentPointee<2>(max_uniform_len)) .RetiresOnSaturation(); for (size_t ii = 0; ii < num_uniforms; ++ii) { const UniformInfo& info = uniforms[ii]; EXPECT_CALL(*gl, GetActiveUniform(service_id, ii, max_uniform_len, _, _, _, _)) .WillOnce(DoAll(SetArgumentPointee<3>(strlen(info.name)), SetArgumentPointee<4>(info.size), SetArgumentPointee<5>(info.type), SetArrayArgument<6>( info.name, info.name + strlen(info.name) + 1))) .RetiresOnSaturation(); if (info.real_location != -1) { EXPECT_CALL(*gl, GetUniformLocation(service_id, StrEq(info.name))) .WillOnce(Return(info.real_location)) .RetiresOnSaturation(); } if (info.size > 1) { std::string base_name = info.name; size_t array_pos = base_name.rfind("[0]"); if (base_name.size() > 3 && array_pos == base_name.size() - 3) { base_name = base_name.substr(0, base_name.size() - 3); } for (GLsizei jj = 1; jj < info.size; ++jj) { std::string element_name(std::string(base_name) + "[" + base::IntToString(jj) + "]"); EXPECT_CALL(*gl, GetUniformLocation(service_id, StrEq(element_name))) .WillOnce(Return(info.real_location + jj * 2)) .RetiresOnSaturation(); } } } } if (feature_info->feature_flags().chromium_path_rendering) { EXPECT_CALL(*gl, GetProgramInterfaceiv(service_id, GL_FRAGMENT_INPUT_NV, GL_ACTIVE_RESOURCES, _)) .WillOnce(SetArgumentPointee<3>(int(num_varyings))) .RetiresOnSaturation(); size_t max_varying_len = 0; for (size_t ii = 0; ii < num_varyings; ++ii) { size_t len = strlen(varyings[ii].name) + 1; max_varying_len = std::max(max_varying_len, len); } EXPECT_CALL(*gl, GetProgramInterfaceiv(service_id, GL_FRAGMENT_INPUT_NV, GL_MAX_NAME_LENGTH, _)) .WillOnce(SetArgumentPointee<3>(int(max_varying_len))) .RetiresOnSaturation(); for (size_t ii = 0; ii < num_varyings; ++ii) { VaryingInfo& info = varyings[ii]; EXPECT_CALL(*gl, GetProgramResourceName(service_id, GL_FRAGMENT_INPUT_NV, ii, max_varying_len, _, _)) .WillOnce(DoAll(SetArgumentPointee<4>(strlen(info.name)), SetArrayArgument<5>( info.name, info.name + strlen(info.name) + 1))) .RetiresOnSaturation(); if (ProgramManager::HasBuiltInPrefix(info.name)) continue; static const GLenum kPropsArray[] = {GL_LOCATION, GL_TYPE, GL_ARRAY_SIZE}; static const size_t kPropsSize = arraysize(kPropsArray); EXPECT_CALL( *gl, GetProgramResourceiv( service_id, GL_FRAGMENT_INPUT_NV, ii, kPropsSize, _ /*testing::ElementsAreArray(kPropsArray, kPropsSize)*/, kPropsSize, _, _)) .WillOnce(testing::Invoke([info](GLuint, GLenum, GLuint, GLsizei, const GLenum*, GLsizei, GLsizei* length, GLint* params) { *length = kPropsSize; params[0] = info.real_location; params[1] = info.type; params[2] = info.size; })) .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); } } } } void TestHelper::SetupShaderExpectations(::gfx::MockGLInterface* gl, const FeatureInfo* feature_info, AttribInfo* attribs, size_t num_attribs, UniformInfo* uniforms, size_t num_uniforms, GLuint service_id) { InSequence s; EXPECT_CALL(*gl, LinkProgram(service_id)).Times(1).RetiresOnSaturation(); SetupProgramSuccessExpectations(gl, feature_info, attribs, num_attribs, uniforms, num_uniforms, nullptr, 0, nullptr, 0, service_id); } void TestHelper::SetupShaderExpectationsWithVaryings( ::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) { InSequence s; EXPECT_CALL(*gl, LinkProgram(service_id)) .Times(1) .RetiresOnSaturation(); SetupProgramSuccessExpectations( gl, feature_info, attribs, num_attribs, uniforms, num_uniforms, varyings, num_varyings, program_outputs, num_program_outputs, service_id); } void TestHelper::DoBufferData( ::gfx::MockGLInterface* gl, MockErrorState* error_state, BufferManager* manager, Buffer* buffer, GLenum target, GLsizeiptr size, GLenum usage, const GLvoid* data, GLenum error) { EXPECT_CALL(*error_state, CopyRealGLErrorsToWrapper(_, _, _)) .Times(1) .RetiresOnSaturation(); if (manager->IsUsageClientSideArray(usage)) { EXPECT_CALL(*gl, BufferData(target, 0, _, usage)) .Times(1) .RetiresOnSaturation(); } else { EXPECT_CALL(*gl, BufferData(target, size, _, usage)) .Times(1) .RetiresOnSaturation(); } EXPECT_CALL(*error_state, PeekGLError(_, _, _)) .WillOnce(Return(error)) .RetiresOnSaturation(); manager->DoBufferData(error_state, buffer, target, size, usage, data); } void TestHelper::SetTexParameteriWithExpectations( ::gfx::MockGLInterface* gl, MockErrorState* error_state, TextureManager* manager, TextureRef* texture_ref, GLenum pname, GLint value, GLenum error) { if (error == GL_NO_ERROR) { EXPECT_CALL(*gl, TexParameteri(texture_ref->texture()->target(), pname, value)) .Times(1) .RetiresOnSaturation(); } else if (error == GL_INVALID_ENUM) { EXPECT_CALL(*error_state, SetGLErrorInvalidEnum(_, _, _, value, _)) .Times(1) .RetiresOnSaturation(); } else { EXPECT_CALL(*error_state, SetGLErrorInvalidParami(_, _, error, _, _, _)) .Times(1) .RetiresOnSaturation(); } manager->SetParameteri("", error_state, texture_ref, pname, value); } // 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 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; const std::string empty_translated_source; const std::string* translated_source = (expected_translated_source && expected_valid) ? expected_translated_source : &empty_translated_source; int default_shader_version = 100; const int* shader_version = (expected_shader_version && expected_valid) ? expected_shader_version : &default_shader_version; const AttributeMap empty_attrib_map; const AttributeMap* attrib_map = (expected_attrib_map && expected_valid) ? expected_attrib_map : &empty_attrib_map; const UniformMap empty_uniform_map; const UniformMap* uniform_map = (expected_uniform_map && expected_valid) ? expected_uniform_map : &empty_uniform_map; const VaryingMap empty_varying_map; const VaryingMap* varying_map = (expected_varying_map && expected_valid) ? expected_varying_map : &empty_varying_map; const InterfaceBlockMap empty_interface_block_map; 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; MockShaderTranslator* mock_translator = new MockShaderTranslator; scoped_refptr 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(), // output_variable_list NotNull())) // name_map .WillOnce(DoAll(SetArgumentPointee<1>(*log_info), SetArgumentPointee<2>(*translated_source), SetArgumentPointee<3>(*shader_version), SetArgumentPointee<4>(*attrib_map), SetArgumentPointee<5>(*uniform_map), SetArgumentPointee<6>(*varying_map), SetArgumentPointee<7>(*interface_block_map), 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)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, CompileShader(shader->service_id())) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetShaderiv(shader->service_id(), GL_COMPILE_STATUS, NotNull())) // status .WillOnce(SetArgumentPointee<2>(GL_TRUE)) .RetiresOnSaturation(); } shader->RequestCompile(translator, Shader::kGL); shader->DoCompile(); } // static void TestHelper::SetShaderStates( ::gfx::MockGLInterface* gl, Shader* shader, bool valid) { SetShaderStates(gl, shader, valid, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); } // static sh::Attribute TestHelper::ConstructAttribute( GLenum type, GLint array_size, GLenum precision, bool static_use, const std::string& name) { return ConstructShaderVariable( type, array_size, precision, static_use, name); } // static sh::Uniform TestHelper::ConstructUniform( GLenum type, GLint array_size, GLenum precision, bool static_use, const std::string& name) { return ConstructShaderVariable( type, array_size, precision, static_use, name); } // static sh::Varying TestHelper::ConstructVarying( GLenum type, GLint array_size, GLenum precision, bool static_use, const std::string& name) { return ConstructShaderVariable( 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( type, array_size, precision, static_use, name); } ScopedGLImplementationSetter::ScopedGLImplementationSetter( gfx::GLImplementation implementation) : old_implementation_(gfx::GetGLImplementation()) { gfx::SetGLImplementation(implementation); } ScopedGLImplementationSetter::~ScopedGLImplementationSetter() { gfx::SetGLImplementation(old_implementation_); } } // namespace gles2 } // namespace gpu