Fix tabs sharing TEXTURE_2D_ARRAY/TEXTURE_3D data.

In linux and android, we are seeing an issue where texture data from one
tab overwrites the texture data of another tab. This is happening for apps
which are using webgl2 texture of type TEXTURE_2D_ARRAY/TEXTURE_3D.
Due to a bug in virtual context save/restore code for above texture formats,
the texture data is not properly restored while switching tabs. Hence
texture data from one tab overwrites other.

This CL has fix for that issue, an update for existing test expectations
and a new unit test for this bug.


Bug: 788448
Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: Ie933984cdd2d1381f42eb4638f730c8245207a28
Reviewed-on: https://chromium-review.googlesource.com/930327
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Commit-Queue: vikas soni <vikassoni@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#539111}(cherry picked from commit d128139d53e9268e87921e82d89b3f2053cb83fd)
Reviewed-on: https://chromium-review.googlesource.com/939878
Cr-Commit-Position: refs/branch-heads/3325@{#610}
Cr-Branched-From: bc084a8b5afa3744a74927344e304c02ae54189f-refs/heads/master@{#530369}
diff --git a/gpu/command_buffer/service/context_state.cc b/gpu/command_buffer/service/context_state.cc
index 3f9a3484..324f461 100644
--- a/gpu/command_buffer/service/context_state.cc
+++ b/gpu/command_buffer/service/context_state.cc
@@ -29,6 +29,16 @@
       ? unit.bound_texture_2d->service_id() : 0;
 }
 
+GLuint Get2dArrayServiceId(const TextureUnit& unit) {
+  return unit.bound_texture_2d_array.get()
+             ? unit.bound_texture_2d_array->service_id()
+             : 0;
+}
+
+GLuint Get3dServiceId(const TextureUnit& unit) {
+  return unit.bound_texture_3d.get() ? unit.bound_texture_3d->service_id() : 0;
+}
+
 GLuint GetCubeServiceId(const TextureUnit& unit) {
   return unit.bound_texture_cube_map.get()
       ? unit.bound_texture_cube_map->service_id() : 0;
@@ -237,6 +247,8 @@
   DCHECK_LT(unit, texture_units.size());
   const TextureUnit& texture_unit = texture_units[unit];
   GLuint service_id_2d = Get2dServiceId(texture_unit);
+  GLuint service_id_2d_array = Get2dArrayServiceId(texture_unit);
+  GLuint service_id_3d = Get3dServiceId(texture_unit);
   GLuint service_id_cube = GetCubeServiceId(texture_unit);
   GLuint service_id_oes = GetOesServiceId(texture_unit);
   GLuint service_id_arb = GetArbServiceId(texture_unit);
@@ -247,10 +259,22 @@
       feature_info_->feature_flags().oes_egl_image_external ||
       feature_info_->feature_flags().nv_egl_stream_consumer_external;
   bool bind_texture_arb = feature_info_->feature_flags().arb_texture_rectangle;
+  // TEXTURE_2D_ARRAY and TEXTURE_3D are only applicable from ES3 version.
+  // So set it to FALSE by default.
+  bool bind_texture_2d_array = false;
+  bool bind_texture_3d = false;
+  // set the variables to true only if the application is ES3 or newer
+  if (feature_info_->IsES3Capable()) {
+    bind_texture_2d_array = true;
+    bind_texture_3d = true;
+  }
 
   if (prev_state) {
     const TextureUnit& prev_unit = prev_state->texture_units[unit];
     bind_texture_2d = service_id_2d != Get2dServiceId(prev_unit);
+    bind_texture_2d_array =
+        service_id_2d_array != Get2dArrayServiceId(prev_unit);
+    bind_texture_3d = service_id_3d != Get3dServiceId(prev_unit);
     bind_texture_cube = service_id_cube != GetCubeServiceId(prev_unit);
     bind_texture_oes =
         bind_texture_oes && service_id_oes != GetOesServiceId(prev_unit);
@@ -259,8 +283,8 @@
   }
 
   // Early-out if nothing has changed from the previous state.
-  if (!bind_texture_2d && !bind_texture_cube
-      && !bind_texture_oes && !bind_texture_arb) {
+  if (!bind_texture_2d && !bind_texture_2d_array && !bind_texture_3d &&
+      !bind_texture_cube && !bind_texture_oes && !bind_texture_arb) {
     return;
   }
 
@@ -277,6 +301,12 @@
   if (bind_texture_arb) {
     api()->glBindTextureFn(GL_TEXTURE_RECTANGLE_ARB, service_id_arb);
   }
+  if (bind_texture_2d_array) {
+    api()->glBindTextureFn(GL_TEXTURE_2D_ARRAY, service_id_2d_array);
+  }
+  if (bind_texture_3d) {
+    api()->glBindTextureFn(GL_TEXTURE_3D, service_id_3d);
+  }
 }
 
 void ContextState::RestoreSamplerBinding(GLuint unit,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_state.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_state.cc
index 3765e69..78c1832 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_state.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_state.cc
@@ -418,6 +418,8 @@
   AddExpectationsForActiveTexture(GL_TEXTURE0);
   AddExpectationsForBindTexture(GL_TEXTURE_2D, kServiceTextureId);
   AddExpectationsForBindTexture(GL_TEXTURE_CUBE_MAP, 0);
+  AddExpectationsForBindTexture(GL_TEXTURE_2D_ARRAY, 0);
+  AddExpectationsForBindTexture(GL_TEXTURE_3D, 0);
   // Expect to restore sampler binding for unit GL_TEXTURE0.
   AddExpectationsForBindSampler(0, kServiceSamplerId);
 
@@ -426,6 +428,8 @@
     AddExpectationsForActiveTexture(GL_TEXTURE0 + i);
     AddExpectationsForBindTexture(GL_TEXTURE_2D, 0);
     AddExpectationsForBindTexture(GL_TEXTURE_CUBE_MAP, 0);
+    AddExpectationsForBindTexture(GL_TEXTURE_2D_ARRAY, 0);
+    AddExpectationsForBindTexture(GL_TEXTURE_3D, 0);
     AddExpectationsForBindSampler(i, 0);
   }
 
diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc
index 9752df9..8e38d10 100644
--- a/gpu/command_buffer/tests/gl_manager.cc
+++ b/gpu/command_buffer/tests/gl_manager.cc
@@ -268,6 +268,7 @@
   InitializeGpuPreferencesForTestingFromCommandLine(command_line,
                                                     &gpu_preferences_);
 
+  context_type_ = options.context_type;
   if (options.share_mailbox_manager) {
     mailbox_manager_ = options.share_mailbox_manager->mailbox_manager();
   } else if (options.share_group_manager) {
@@ -572,4 +573,7 @@
 
 void GLManager::SetSnapshotRequested() {}
 
+ContextType GLManager::GetContextType() const {
+  return context_type_;
+}
 }  // namespace gpu
diff --git a/gpu/command_buffer/tests/gl_manager.h b/gpu/command_buffer/tests/gl_manager.h
index 900b3d7..7acb4b7 100644
--- a/gpu/command_buffer/tests/gl_manager.h
+++ b/gpu/command_buffer/tests/gl_manager.h
@@ -155,6 +155,7 @@
   void SetSnapshotRequested() override;
 
   size_t GetSharedMemoryBytesAllocated() const;
+  ContextType GetContextType() const;
 
  private:
   void SetupBaseContext();
@@ -192,6 +193,7 @@
   static scoped_refptr<gl::GLShareGroup>* base_share_group_;
   static scoped_refptr<gl::GLSurface>* base_surface_;
   static scoped_refptr<gl::GLContext>* base_context_;
+  ContextType context_type_ = CONTEXT_TYPE_OPENGLES2;
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/tests/gl_virtual_contexts_unittest.cc b/gpu/command_buffer/tests/gl_virtual_contexts_unittest.cc
index b4de3d4..82bd266 100644
--- a/gpu/command_buffer/tests/gl_virtual_contexts_unittest.cc
+++ b/gpu/command_buffer/tests/gl_virtual_contexts_unittest.cc
@@ -27,8 +27,15 @@
   void SetUp() override {
     GpuDriverBugWorkarounds workarounds = GetParam();
     GLManager::Options options;
+    options.context_type = CONTEXT_TYPE_OPENGLES3;
     options.size = gfx::Size(kSize0, kSize0);
     gl_real_.InitializeWithWorkarounds(options, workarounds);
+    // If the gl_real context is not initialised, switch to ES2 context type
+    // and re-initialise
+    if (!gl_real_.IsInitialized()) {
+      options.context_type = CONTEXT_TYPE_OPENGLES2;
+      gl_real_.InitializeWithWorkarounds(options, workarounds);
+    }
     gl_real_shared_.InitializeWithWorkarounds(options, workarounds);
     options.virtual_manager = &gl_real_shared_;
     options.size = gfx::Size(kSize1, kSize1);
@@ -363,6 +370,57 @@
   }
 }
 
+// http://crbug.com/930327
+TEST_P(GLVirtualContextsTest, Texture2DArrayAnd3DRestore) {
+  // This test should only be run for ES3 or higher context
+  // So if the current version is ES2, do not run this test
+  if (gl1_.GetContextType() == CONTEXT_TYPE_OPENGLES2)
+    return;
+
+  // Context 1
+  gl1_.MakeCurrent();
+  GLuint tex1_2d_array = 0, tex1_3d = 0;
+  glActiveTexture(GL_TEXTURE0);
+  // 2d array texture
+  glGenTextures(1, &tex1_2d_array);
+  glBindTexture(GL_TEXTURE_2D_ARRAY, tex1_2d_array);
+  glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+  // 3d texture
+  glGenTextures(1, &tex1_3d);
+  glBindTexture(GL_TEXTURE_3D, tex1_3d);
+  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER,
+                  GL_NEAREST_MIPMAP_NEAREST);
+  glFinish();
+
+  // switch to context 2
+  gl2_.MakeCurrent();
+  GLuint tex2_2d_array = 0, tex2_3d = 0;
+  glActiveTexture(GL_TEXTURE0);
+  // 2d array texture
+  glGenTextures(1, &tex2_2d_array);
+  glBindTexture(GL_TEXTURE_2D_ARRAY, tex2_2d_array);
+  glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  // 3d texture
+  glGenTextures(1, &tex2_3d);
+  glBindTexture(GL_TEXTURE_3D, tex2_3d);
+  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER,
+                  GL_LINEAR_MIPMAP_LINEAR);
+  glFinish();
+
+  // switch back to context1
+  gl1_.MakeCurrent();
+
+  // get the texture parameters which were programmed earlier for context1
+  GLint tex_2d_array_params = 0, tex_3d_params = 0;
+  glGetTexParameteriv(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER,
+                      &tex_2d_array_params);
+  glGetTexParameteriv(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, &tex_3d_params);
+  // Do checks to make sure texture params are restored correctly after context
+  // switching
+  EXPECT_EQ(GL_NEAREST, tex_2d_array_params);
+  EXPECT_EQ(GL_NEAREST_MIPMAP_NEAREST, tex_3d_params);
+  GLTestHelper::CheckGLError("no errors", __LINE__);
+}
 static const GpuDriverBugWorkarounds workarounds_cases[] = {
     // No extra workarounds.
     GpuDriverBugWorkarounds(),