1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "gpu/command_buffer/service/test_helper.h" 6 7 #include <algorithm> 8 #include <string> 9 10 #include "base/strings/string_number_conversions.h" 11 #include "base/strings/string_tokenizer.h" 12 #include "gpu/command_buffer/common/types.h" 13 #include "gpu/command_buffer/service/buffer_manager.h" 14 #include "gpu/command_buffer/service/error_state_mock.h" 15 #include "gpu/command_buffer/service/gl_utils.h" 16 #include "gpu/command_buffer/service/gpu_switches.h" 17 #include "gpu/command_buffer/service/program_manager.h" 18 #include "gpu/command_buffer/service/texture_manager.h" 19 #include "testing/gtest/include/gtest/gtest.h" 20 #include "ui/gl/gl_mock.h" 21 22 using ::testing::_; 23 using ::testing::DoAll; 24 using ::testing::InSequence; 25 using ::testing::MatcherCast; 26 using ::testing::Pointee; 27 using ::testing::Return; 28 using ::testing::SetArrayArgument; 29 using ::testing::SetArgumentPointee; 30 using ::testing::StrEq; 31 using ::testing::StrictMock; 32 33 namespace gpu { 34 namespace gles2 { 35 36 // GCC requires these declarations, but MSVC requires they not be present 37 #ifndef COMPILER_MSVC 38 const GLuint TestHelper::kServiceBlackTexture2dId; 39 const GLuint TestHelper::kServiceDefaultTexture2dId; 40 const GLuint TestHelper::kServiceBlackTextureCubemapId; 41 const GLuint TestHelper::kServiceDefaultTextureCubemapId; 42 const GLuint TestHelper::kServiceBlackExternalTextureId; 43 const GLuint TestHelper::kServiceDefaultExternalTextureId; 44 const GLuint TestHelper::kServiceBlackRectangleTextureId; 45 const GLuint TestHelper::kServiceDefaultRectangleTextureId; 46 47 const GLint TestHelper::kMaxSamples; 48 const GLint TestHelper::kMaxRenderbufferSize; 49 const GLint TestHelper::kMaxTextureSize; 50 const GLint TestHelper::kMaxCubeMapTextureSize; 51 const GLint TestHelper::kNumVertexAttribs; 52 const GLint TestHelper::kNumTextureUnits; 53 const GLint TestHelper::kMaxTextureImageUnits; 54 const GLint TestHelper::kMaxVertexTextureImageUnits; 55 const GLint TestHelper::kMaxFragmentUniformVectors; 56 const GLint TestHelper::kMaxFragmentUniformComponents; 57 const GLint TestHelper::kMaxVaryingVectors; 58 const GLint TestHelper::kMaxVaryingFloats; 59 const GLint TestHelper::kMaxVertexUniformVectors; 60 const GLint TestHelper::kMaxVertexUniformComponents; 61 #endif 62 63 void TestHelper::SetupTextureInitializationExpectations( 64 ::gfx::MockGLInterface* gl, GLenum target) { 65 InSequence sequence; 66 67 bool needs_initialization = (target != GL_TEXTURE_EXTERNAL_OES); 68 bool needs_faces = (target == GL_TEXTURE_CUBE_MAP); 69 70 static GLuint texture_2d_ids[] = { 71 kServiceBlackTexture2dId, 72 kServiceDefaultTexture2dId }; 73 static GLuint texture_cube_map_ids[] = { 74 kServiceBlackTextureCubemapId, 75 kServiceDefaultTextureCubemapId }; 76 static GLuint texture_external_oes_ids[] = { 77 kServiceBlackExternalTextureId, 78 kServiceDefaultExternalTextureId }; 79 static GLuint texture_rectangle_arb_ids[] = { 80 kServiceBlackRectangleTextureId, 81 kServiceDefaultRectangleTextureId }; 82 83 const GLuint* texture_ids = NULL; 84 switch (target) { 85 case GL_TEXTURE_2D: 86 texture_ids = &texture_2d_ids[0]; 87 break; 88 case GL_TEXTURE_CUBE_MAP: 89 texture_ids = &texture_cube_map_ids[0]; 90 break; 91 case GL_TEXTURE_EXTERNAL_OES: 92 texture_ids = &texture_external_oes_ids[0]; 93 break; 94 case GL_TEXTURE_RECTANGLE_ARB: 95 texture_ids = &texture_rectangle_arb_ids[0]; 96 break; 97 default: 98 NOTREACHED(); 99 } 100 101 int array_size = 2; 102 103 EXPECT_CALL(*gl, GenTextures(array_size, _)) 104 .WillOnce(SetArrayArgument<1>(texture_ids, 105 texture_ids + array_size)) 106 .RetiresOnSaturation(); 107 for (int ii = 0; ii < array_size; ++ii) { 108 EXPECT_CALL(*gl, BindTexture(target, texture_ids[ii])) 109 .Times(1) 110 .RetiresOnSaturation(); 111 if (needs_initialization) { 112 if (needs_faces) { 113 static GLenum faces[] = { 114 GL_TEXTURE_CUBE_MAP_POSITIVE_X, 115 GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 116 GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 117 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 118 GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 119 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 120 }; 121 for (size_t ii = 0; ii < arraysize(faces); ++ii) { 122 EXPECT_CALL(*gl, TexImage2D(faces[ii], 0, GL_RGBA, 1, 1, 0, GL_RGBA, 123 GL_UNSIGNED_BYTE, _)) 124 .Times(1) 125 .RetiresOnSaturation(); 126 } 127 } else { 128 EXPECT_CALL(*gl, TexImage2D(target, 0, GL_RGBA, 1, 1, 0, GL_RGBA, 129 GL_UNSIGNED_BYTE, _)) 130 .Times(1) 131 .RetiresOnSaturation(); 132 } 133 } 134 } 135 EXPECT_CALL(*gl, BindTexture(target, 0)) 136 .Times(1) 137 .RetiresOnSaturation(); 138 } 139 140 void TestHelper::SetupTextureManagerInitExpectations( 141 ::gfx::MockGLInterface* gl, 142 const char* extensions) { 143 InSequence sequence; 144 145 SetupTextureInitializationExpectations(gl, GL_TEXTURE_2D); 146 SetupTextureInitializationExpectations(gl, GL_TEXTURE_CUBE_MAP); 147 148 bool ext_image_external = false; 149 bool arb_texture_rectangle = false; 150 base::CStringTokenizer t(extensions, extensions + strlen(extensions), " "); 151 while (t.GetNext()) { 152 if (t.token() == "GL_OES_EGL_image_external") { 153 ext_image_external = true; 154 break; 155 } 156 if (t.token() == "GL_ARB_texture_rectangle") { 157 arb_texture_rectangle = true; 158 break; 159 } 160 } 161 162 if (ext_image_external) { 163 SetupTextureInitializationExpectations(gl, GL_TEXTURE_EXTERNAL_OES); 164 } 165 if (arb_texture_rectangle) { 166 SetupTextureInitializationExpectations(gl, GL_TEXTURE_RECTANGLE_ARB); 167 } 168 } 169 170 void TestHelper::SetupTextureDestructionExpectations( 171 ::gfx::MockGLInterface* gl, GLenum target) { 172 GLuint texture_id = 0; 173 switch (target) { 174 case GL_TEXTURE_2D: 175 texture_id = kServiceDefaultTexture2dId; 176 break; 177 case GL_TEXTURE_CUBE_MAP: 178 texture_id = kServiceDefaultTextureCubemapId; 179 break; 180 case GL_TEXTURE_EXTERNAL_OES: 181 texture_id = kServiceDefaultExternalTextureId; 182 break; 183 case GL_TEXTURE_RECTANGLE_ARB: 184 texture_id = kServiceDefaultRectangleTextureId; 185 break; 186 default: 187 NOTREACHED(); 188 } 189 190 EXPECT_CALL(*gl, DeleteTextures(1, Pointee(texture_id))) 191 .Times(1) 192 .RetiresOnSaturation(); 193 } 194 195 void TestHelper::SetupTextureManagerDestructionExpectations( 196 ::gfx::MockGLInterface* gl, 197 const char* extensions) { 198 SetupTextureDestructionExpectations(gl, GL_TEXTURE_2D); 199 SetupTextureDestructionExpectations(gl, GL_TEXTURE_CUBE_MAP); 200 201 bool ext_image_external = false; 202 bool arb_texture_rectangle = false; 203 base::CStringTokenizer t(extensions, extensions + strlen(extensions), " "); 204 while (t.GetNext()) { 205 if (t.token() == "GL_OES_EGL_image_external") { 206 ext_image_external = true; 207 break; 208 } 209 if (t.token() == "GL_ARB_texture_rectangle") { 210 arb_texture_rectangle = true; 211 break; 212 } 213 } 214 215 if (ext_image_external) { 216 SetupTextureDestructionExpectations(gl, GL_TEXTURE_EXTERNAL_OES); 217 } 218 if (arb_texture_rectangle) { 219 SetupTextureDestructionExpectations(gl, GL_TEXTURE_RECTANGLE_ARB); 220 } 221 222 EXPECT_CALL(*gl, DeleteTextures(4, _)) 223 .Times(1) 224 .RetiresOnSaturation(); 225 } 226 227 void TestHelper::SetupContextGroupInitExpectations( 228 ::gfx::MockGLInterface* gl, 229 const DisallowedFeatures& disallowed_features, 230 const char* extensions) { 231 InSequence sequence; 232 233 SetupFeatureInfoInitExpectations(gl, extensions); 234 235 EXPECT_CALL(*gl, GetIntegerv(GL_MAX_RENDERBUFFER_SIZE, _)) 236 .WillOnce(SetArgumentPointee<1>(kMaxRenderbufferSize)) 237 .RetiresOnSaturation(); 238 if (strstr(extensions, "GL_EXT_framebuffer_multisample")) { 239 EXPECT_CALL(*gl, GetIntegerv(GL_MAX_SAMPLES, _)) 240 .WillOnce(SetArgumentPointee<1>(kMaxSamples)) 241 .RetiresOnSaturation(); 242 } 243 EXPECT_CALL(*gl, GetIntegerv(GL_MAX_VERTEX_ATTRIBS, _)) 244 .WillOnce(SetArgumentPointee<1>(kNumVertexAttribs)) 245 .RetiresOnSaturation(); 246 EXPECT_CALL(*gl, GetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, _)) 247 .WillOnce(SetArgumentPointee<1>(kNumTextureUnits)) 248 .RetiresOnSaturation(); 249 EXPECT_CALL(*gl, GetIntegerv(GL_MAX_TEXTURE_SIZE, _)) 250 .WillOnce(SetArgumentPointee<1>(kMaxTextureSize)) 251 .RetiresOnSaturation(); 252 EXPECT_CALL(*gl, GetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, _)) 253 .WillOnce(SetArgumentPointee<1>(kMaxCubeMapTextureSize)) 254 .RetiresOnSaturation(); 255 EXPECT_CALL(*gl, GetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, _)) 256 .WillOnce(SetArgumentPointee<1>(kMaxTextureImageUnits)) 257 .RetiresOnSaturation(); 258 EXPECT_CALL(*gl, GetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, _)) 259 .WillOnce(SetArgumentPointee<1>(kMaxVertexTextureImageUnits)) 260 .RetiresOnSaturation(); 261 EXPECT_CALL(*gl, GetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, _)) 262 .WillOnce(SetArgumentPointee<1>(kMaxFragmentUniformComponents)) 263 .RetiresOnSaturation(); 264 EXPECT_CALL(*gl, GetIntegerv(GL_MAX_VARYING_FLOATS, _)) 265 .WillOnce(SetArgumentPointee<1>(kMaxVaryingFloats)) 266 .RetiresOnSaturation(); 267 EXPECT_CALL(*gl, GetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, _)) 268 .WillOnce(SetArgumentPointee<1>(kMaxVertexUniformComponents)) 269 .RetiresOnSaturation(); 270 271 SetupTextureManagerInitExpectations(gl, extensions); 272 } 273 274 void TestHelper::SetupFeatureInfoInitExpectations( 275 ::gfx::MockGLInterface* gl, const char* extensions) { 276 SetupFeatureInfoInitExpectationsWithGLVersion(gl, extensions, ""); 277 } 278 279 void TestHelper::SetupFeatureInfoInitExpectationsWithGLVersion( 280 ::gfx::MockGLInterface* gl, 281 const char* extensions, 282 const char* version) { 283 InSequence sequence; 284 285 EXPECT_CALL(*gl, GetString(GL_EXTENSIONS)) 286 .WillOnce(Return(reinterpret_cast<const uint8*>(extensions))) 287 .RetiresOnSaturation(); 288 EXPECT_CALL(*gl, GetString(GL_VERSION)) 289 .WillOnce(Return(reinterpret_cast<const uint8*>(version))) 290 .RetiresOnSaturation(); 291 } 292 293 void TestHelper::SetupExpectationsForClearingUniforms( 294 ::gfx::MockGLInterface* gl, UniformInfo* uniforms, size_t num_uniforms) { 295 for (size_t ii = 0; ii < num_uniforms; ++ii) { 296 const UniformInfo& info = uniforms[ii]; 297 switch (info.type) { 298 case GL_FLOAT: 299 EXPECT_CALL(*gl, Uniform1fv(info.real_location, info.size, _)) 300 .Times(1) 301 .RetiresOnSaturation(); 302 break; 303 case GL_FLOAT_VEC2: 304 EXPECT_CALL(*gl, Uniform2fv(info.real_location, info.size, _)) 305 .Times(1) 306 .RetiresOnSaturation(); 307 break; 308 case GL_FLOAT_VEC3: 309 EXPECT_CALL(*gl, Uniform3fv(info.real_location, info.size, _)) 310 .Times(1) 311 .RetiresOnSaturation(); 312 break; 313 case GL_FLOAT_VEC4: 314 EXPECT_CALL(*gl, Uniform4fv(info.real_location, info.size, _)) 315 .Times(1) 316 .RetiresOnSaturation(); 317 break; 318 case GL_INT: 319 case GL_BOOL: 320 case GL_SAMPLER_2D: 321 case GL_SAMPLER_CUBE: 322 case GL_SAMPLER_EXTERNAL_OES: 323 case GL_SAMPLER_3D_OES: 324 case GL_SAMPLER_2D_RECT_ARB: 325 EXPECT_CALL(*gl, Uniform1iv(info.real_location, info.size, _)) 326 .Times(1) 327 .RetiresOnSaturation(); 328 break; 329 case GL_INT_VEC2: 330 case GL_BOOL_VEC2: 331 EXPECT_CALL(*gl, Uniform2iv(info.real_location, info.size, _)) 332 .Times(1) 333 .RetiresOnSaturation(); 334 break; 335 case GL_INT_VEC3: 336 case GL_BOOL_VEC3: 337 EXPECT_CALL(*gl, Uniform3iv(info.real_location, info.size, _)) 338 .Times(1) 339 .RetiresOnSaturation(); 340 break; 341 case GL_INT_VEC4: 342 case GL_BOOL_VEC4: 343 EXPECT_CALL(*gl, Uniform4iv(info.real_location, info.size, _)) 344 .Times(1) 345 .RetiresOnSaturation(); 346 break; 347 case GL_FLOAT_MAT2: 348 EXPECT_CALL(*gl, UniformMatrix2fv( 349 info.real_location, info.size, false, _)) 350 .Times(1) 351 .RetiresOnSaturation(); 352 break; 353 case GL_FLOAT_MAT3: 354 EXPECT_CALL(*gl, UniformMatrix3fv( 355 info.real_location, info.size, false, _)) 356 .Times(1) 357 .RetiresOnSaturation(); 358 break; 359 case GL_FLOAT_MAT4: 360 EXPECT_CALL(*gl, UniformMatrix4fv( 361 info.real_location, info.size, false, _)) 362 .Times(1) 363 .RetiresOnSaturation(); 364 break; 365 default: 366 NOTREACHED(); 367 break; 368 } 369 } 370 } 371 372 void TestHelper::SetupProgramSuccessExpectations( 373 ::gfx::MockGLInterface* gl, 374 AttribInfo* attribs, size_t num_attribs, 375 UniformInfo* uniforms, size_t num_uniforms, 376 GLuint service_id) { 377 EXPECT_CALL(*gl, 378 GetProgramiv(service_id, GL_LINK_STATUS, _)) 379 .WillOnce(SetArgumentPointee<2>(1)) 380 .RetiresOnSaturation(); 381 EXPECT_CALL(*gl, 382 GetProgramiv(service_id, GL_INFO_LOG_LENGTH, _)) 383 .WillOnce(SetArgumentPointee<2>(0)) 384 .RetiresOnSaturation(); 385 EXPECT_CALL(*gl, 386 GetProgramiv(service_id, GL_ACTIVE_ATTRIBUTES, _)) 387 .WillOnce(SetArgumentPointee<2>(num_attribs)) 388 .RetiresOnSaturation(); 389 size_t max_attrib_len = 0; 390 for (size_t ii = 0; ii < num_attribs; ++ii) { 391 size_t len = strlen(attribs[ii].name) + 1; 392 max_attrib_len = std::max(max_attrib_len, len); 393 } 394 EXPECT_CALL(*gl, 395 GetProgramiv(service_id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, _)) 396 .WillOnce(SetArgumentPointee<2>(max_attrib_len)) 397 .RetiresOnSaturation(); 398 399 for (size_t ii = 0; ii < num_attribs; ++ii) { 400 const AttribInfo& info = attribs[ii]; 401 EXPECT_CALL(*gl, 402 GetActiveAttrib(service_id, ii, 403 max_attrib_len, _, _, _, _)) 404 .WillOnce(DoAll( 405 SetArgumentPointee<3>(strlen(info.name)), 406 SetArgumentPointee<4>(info.size), 407 SetArgumentPointee<5>(info.type), 408 SetArrayArgument<6>(info.name, 409 info.name + strlen(info.name) + 1))) 410 .RetiresOnSaturation(); 411 if (!ProgramManager::IsInvalidPrefix(info.name, strlen(info.name))) { 412 EXPECT_CALL(*gl, GetAttribLocation(service_id, StrEq(info.name))) 413 .WillOnce(Return(info.location)) 414 .RetiresOnSaturation(); 415 } 416 } 417 EXPECT_CALL(*gl, 418 GetProgramiv(service_id, GL_ACTIVE_UNIFORMS, _)) 419 .WillOnce(SetArgumentPointee<2>(num_uniforms)) 420 .RetiresOnSaturation(); 421 422 size_t max_uniform_len = 0; 423 for (size_t ii = 0; ii < num_uniforms; ++ii) { 424 size_t len = strlen(uniforms[ii].name) + 1; 425 max_uniform_len = std::max(max_uniform_len, len); 426 } 427 EXPECT_CALL(*gl, 428 GetProgramiv(service_id, GL_ACTIVE_UNIFORM_MAX_LENGTH, _)) 429 .WillOnce(SetArgumentPointee<2>(max_uniform_len)) 430 .RetiresOnSaturation(); 431 for (size_t ii = 0; ii < num_uniforms; ++ii) { 432 const UniformInfo& info = uniforms[ii]; 433 EXPECT_CALL(*gl, 434 GetActiveUniform(service_id, ii, 435 max_uniform_len, _, _, _, _)) 436 .WillOnce(DoAll( 437 SetArgumentPointee<3>(strlen(info.name)), 438 SetArgumentPointee<4>(info.size), 439 SetArgumentPointee<5>(info.type), 440 SetArrayArgument<6>(info.name, 441 info.name + strlen(info.name) + 1))) 442 .RetiresOnSaturation(); 443 } 444 445 for (int pass = 0; pass < 2; ++pass) { 446 for (size_t ii = 0; ii < num_uniforms; ++ii) { 447 const UniformInfo& info = uniforms[ii]; 448 if (ProgramManager::IsInvalidPrefix(info.name, strlen(info.name))) { 449 continue; 450 } 451 if (pass == 0) { 452 EXPECT_CALL(*gl, GetUniformLocation(service_id, StrEq(info.name))) 453 .WillOnce(Return(info.real_location)) 454 .RetiresOnSaturation(); 455 } 456 if ((pass == 0 && info.desired_location >= 0) || 457 (pass == 1 && info.desired_location < 0)) { 458 if (info.size > 1) { 459 std::string base_name = info.name; 460 size_t array_pos = base_name.rfind("[0]"); 461 if (base_name.size() > 3 && array_pos == base_name.size() - 3) { 462 base_name = base_name.substr(0, base_name.size() - 3); 463 } 464 for (GLsizei jj = 1; jj < info.size; ++jj) { 465 std::string element_name( 466 std::string(base_name) + "[" + base::IntToString(jj) + "]"); 467 EXPECT_CALL(*gl, GetUniformLocation( 468 service_id, StrEq(element_name))) 469 .WillOnce(Return(info.real_location + jj * 2)) 470 .RetiresOnSaturation(); 471 } 472 } 473 } 474 } 475 } 476 } 477 478 void TestHelper::SetupShader( 479 ::gfx::MockGLInterface* gl, 480 AttribInfo* attribs, size_t num_attribs, 481 UniformInfo* uniforms, size_t num_uniforms, 482 GLuint service_id) { 483 InSequence s; 484 485 EXPECT_CALL(*gl, 486 LinkProgram(service_id)) 487 .Times(1) 488 .RetiresOnSaturation(); 489 490 SetupProgramSuccessExpectations( 491 gl, attribs, num_attribs, uniforms, num_uniforms, service_id); 492 } 493 494 void TestHelper::DoBufferData( 495 ::gfx::MockGLInterface* gl, MockErrorState* error_state, 496 BufferManager* manager, Buffer* buffer, GLsizeiptr size, GLenum usage, 497 const GLvoid* data, GLenum error) { 498 EXPECT_CALL(*error_state, CopyRealGLErrorsToWrapper(_, _, _)) 499 .Times(1) 500 .RetiresOnSaturation(); 501 if (manager->IsUsageClientSideArray(usage)) { 502 EXPECT_CALL(*gl, BufferData( 503 buffer->target(), 0, _, usage)) 504 .Times(1) 505 .RetiresOnSaturation(); 506 } else { 507 EXPECT_CALL(*gl, BufferData( 508 buffer->target(), size, _, usage)) 509 .Times(1) 510 .RetiresOnSaturation(); 511 } 512 EXPECT_CALL(*error_state, PeekGLError(_, _, _)) 513 .WillOnce(Return(error)) 514 .RetiresOnSaturation(); 515 manager->DoBufferData(error_state, buffer, size, usage, data); 516 } 517 518 void TestHelper::SetTexParameterWithExpectations( 519 ::gfx::MockGLInterface* gl, MockErrorState* error_state, 520 TextureManager* manager, TextureRef* texture_ref, 521 GLenum pname, GLint value, GLenum error) { 522 if (error == GL_NO_ERROR) { 523 if (pname != GL_TEXTURE_POOL_CHROMIUM) { 524 EXPECT_CALL(*gl, TexParameteri(texture_ref->texture()->target(), 525 pname, value)) 526 .Times(1) 527 .RetiresOnSaturation(); 528 } 529 } else if (error == GL_INVALID_ENUM) { 530 EXPECT_CALL(*error_state, SetGLErrorInvalidEnum(_, _, _, value, _)) 531 .Times(1) 532 .RetiresOnSaturation(); 533 } else { 534 EXPECT_CALL(*error_state, SetGLErrorInvalidParam(_, _, error, _, _, _)) 535 .Times(1) 536 .RetiresOnSaturation(); 537 } 538 manager->SetParameter("", error_state, texture_ref, pname, value); 539 } 540 541 ScopedGLImplementationSetter::ScopedGLImplementationSetter( 542 gfx::GLImplementation implementation) 543 : old_implementation_(gfx::GetGLImplementation()) { 544 gfx::SetGLImplementation(implementation); 545 } 546 547 ScopedGLImplementationSetter::~ScopedGLImplementationSetter() { 548 gfx::SetGLImplementation(old_implementation_); 549 } 550 551 } // namespace gles2 552 } // namespace gpu 553 554