1// Copyright 2013 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 "android_webview/browser/scoped_app_gl_state_restore.h"
6
7#include <string>
8
9#include "base/debug/trace_event.h"
10#include "base/lazy_instance.h"
11#include "ui/gl/gl_bindings.h"
12#include "ui/gl/gl_context.h"
13#include "ui/gl/gl_surface_stub.h"
14
15namespace android_webview {
16
17namespace {
18
19// "App" context is a bit of a stretch. Basically we use this context while
20// saving and restoring the App GL state.
21class AppContextSurface {
22 public:
23  AppContextSurface()
24      : surface(new gfx::GLSurfaceStub),
25        context(gfx::GLContext::CreateGLContext(NULL,
26                                                surface.get(),
27                                                gfx::PreferDiscreteGpu)) {}
28  void MakeCurrent() { context->MakeCurrent(surface.get()); }
29
30 private:
31  scoped_refptr<gfx::GLSurfaceStub> surface;
32  scoped_refptr<gfx::GLContext> context;
33
34  DISALLOW_COPY_AND_ASSIGN(AppContextSurface);
35};
36
37base::LazyInstance<AppContextSurface> g_app_context_surface =
38    LAZY_INSTANCE_INITIALIZER;
39
40// Make the global g_app_context_surface current so that the gl_binding is not
41// NULL for making gl* calls. The binding can be null if another GlContext was
42// destroyed immediately before gl* calls here.
43void MakeAppContextCurrent() {
44  g_app_context_surface.Get().MakeCurrent();
45}
46
47void GLEnableDisable(GLenum cap, bool enable) {
48  if (enable)
49    glEnable(cap);
50  else
51    glDisable(cap);
52}
53
54bool ClearGLErrors(bool warn, const char* msg) {
55  bool no_error = true;
56  GLenum error;
57  while ((error = glGetError()) != GL_NO_ERROR) {
58    DLOG_IF(WARNING, warn) << error << " " << msg;
59    no_error = false;
60  }
61
62  return no_error;
63}
64
65bool g_globals_initialized = false;
66GLint g_gl_max_texture_units = 0;
67bool g_supports_oes_vertex_array_object = false;
68
69}  // namespace
70
71namespace internal {
72
73class ScopedAppGLStateRestoreImpl {
74 public:
75  ScopedAppGLStateRestoreImpl(ScopedAppGLStateRestore::CallMode mode);
76  ~ScopedAppGLStateRestoreImpl();
77
78  bool stencil_enabled() const { return stencil_test_; }
79  GLint framebuffer_binding_ext() const { return framebuffer_binding_ext_; }
80
81 private:
82  const ScopedAppGLStateRestore::CallMode mode_;
83
84  GLint pack_alignment_;
85  GLint unpack_alignment_;
86
87  struct {
88    GLint enabled;
89    GLint size;
90    GLint type;
91    GLint normalized;
92    GLint stride;
93    GLvoid* pointer;
94    GLint vertex_attrib_array_buffer_binding;
95    GLfloat current_vertex_attrib[4];
96  } vertex_attrib_[3];
97
98  GLint vertex_array_buffer_binding_;
99  GLint index_array_buffer_binding_;
100
101  GLboolean depth_test_;
102  GLboolean cull_face_;
103  GLint cull_face_mode_;
104  GLboolean color_mask_[4];
105  GLfloat color_clear_[4];
106  GLfloat blend_color_[4];
107  GLfloat depth_clear_;
108  GLint current_program_;
109  GLint depth_func_;
110  GLboolean depth_mask_;
111  GLfloat depth_rage_[2];
112  GLint front_face_;
113  GLint hint_generate_mipmap_;
114  GLfloat line_width_;
115  GLfloat polygon_offset_factor_;
116  GLfloat polygon_offset_units_;
117  GLfloat sample_coverage_value_;
118  GLboolean sample_coverage_invert_;
119  GLint blend_equation_rgb_;
120  GLint blend_equation_alpha_;
121
122  GLboolean enable_dither_;
123  GLboolean enable_polygon_offset_fill_;
124  GLboolean enable_sample_alpha_to_coverage_;
125  GLboolean enable_sample_coverage_;
126
127  // Not saved/restored in MODE_DRAW.
128  GLboolean blend_enabled_;
129  GLint blend_src_rgb_;
130  GLint blend_src_alpha_;
131  GLint blend_dest_rgb_;
132  GLint blend_dest_alpha_;
133  GLint active_texture_;
134  GLint viewport_[4];
135  GLboolean scissor_test_;
136  GLint scissor_box_[4];
137
138  GLboolean stencil_test_;
139  GLint stencil_front_func_;
140  GLint stencil_front_ref_;
141  GLint stencil_front_mask_;
142  GLint stencil_back_func_;
143  GLint stencil_back_ref_;
144  GLint stencil_back_mask_;
145  GLint stencil_clear_;
146  GLint stencil_front_writemask_;
147  GLint stencil_back_writemask_;
148  GLint stencil_front_fail_op_;
149  GLint stencil_front_z_fail_op_;
150  GLint stencil_front_z_pass_op_;
151  GLint stencil_back_fail_op_;
152  GLint stencil_back_z_fail_op_;
153  GLint stencil_back_z_pass_op_;
154
155  GLint framebuffer_binding_ext_;
156
157  struct TextureBindings {
158    GLint texture_2d;
159    GLint texture_cube_map;
160    GLint texture_external_oes;
161    // TODO(boliu): TEXTURE_RECTANGLE_ARB
162  };
163
164  std::vector<TextureBindings> texture_bindings_;
165
166  GLint vertex_array_bindings_oes_;
167
168  DISALLOW_COPY_AND_ASSIGN(ScopedAppGLStateRestoreImpl);
169};
170
171ScopedAppGLStateRestoreImpl::ScopedAppGLStateRestoreImpl(
172    ScopedAppGLStateRestore::CallMode mode)
173    : mode_(mode) {
174  TRACE_EVENT0("android_webview", "AppGLStateSave");
175  MakeAppContextCurrent();
176
177  ClearGLErrors(true, "Incoming GLError");
178
179  if (!g_globals_initialized) {
180    g_globals_initialized = true;
181
182    glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &g_gl_max_texture_units);
183    DCHECK_GT(g_gl_max_texture_units, 0);
184
185    std::string extensions(
186        reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)));
187    g_supports_oes_vertex_array_object =
188        extensions.find("GL_OES_vertex_array_object") != std::string::npos;
189  }
190
191  glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vertex_array_buffer_binding_);
192  glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &index_array_buffer_binding_);
193
194  switch(mode_) {
195    case ScopedAppGLStateRestore::MODE_DRAW:
196      DCHECK_EQ(0, vertex_array_buffer_binding_);
197      DCHECK_EQ(0, index_array_buffer_binding_);
198      break;
199    case ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT:
200      glGetBooleanv(GL_BLEND, &blend_enabled_);
201      glGetIntegerv(GL_BLEND_SRC_RGB, &blend_src_rgb_);
202      glGetIntegerv(GL_BLEND_SRC_ALPHA, &blend_src_alpha_);
203      glGetIntegerv(GL_BLEND_DST_RGB, &blend_dest_rgb_);
204      glGetIntegerv(GL_BLEND_DST_ALPHA, &blend_dest_alpha_);
205      glGetIntegerv(GL_VIEWPORT, viewport_);
206      glGetBooleanv(GL_SCISSOR_TEST, &scissor_test_);
207      glGetIntegerv(GL_SCISSOR_BOX, scissor_box_);
208      break;
209  }
210
211  glGetIntegerv(GL_PACK_ALIGNMENT, &pack_alignment_);
212  glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_alignment_);
213
214  glGetBooleanv(GL_DEPTH_TEST, &depth_test_);
215  glGetBooleanv(GL_CULL_FACE, &cull_face_);
216  glGetIntegerv(GL_CULL_FACE_MODE, &cull_face_mode_);
217  glGetBooleanv(GL_COLOR_WRITEMASK, color_mask_);
218  glGetIntegerv(GL_CURRENT_PROGRAM, &current_program_);
219  glGetFloatv(GL_COLOR_CLEAR_VALUE, color_clear_);
220  glGetFloatv(GL_DEPTH_CLEAR_VALUE, &depth_clear_);
221  glGetFloatv(GL_BLEND_COLOR, blend_color_);
222  glGetIntegerv(GL_DEPTH_FUNC, &depth_func_);
223  glGetBooleanv(GL_DEPTH_WRITEMASK, &depth_mask_);
224  glGetFloatv(GL_DEPTH_RANGE, depth_rage_);
225  glGetIntegerv(GL_FRONT_FACE, &front_face_);
226  glGetIntegerv(GL_GENERATE_MIPMAP_HINT, &hint_generate_mipmap_);
227  glGetFloatv(GL_LINE_WIDTH, &line_width_);
228  glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &polygon_offset_factor_);
229  glGetFloatv(GL_POLYGON_OFFSET_UNITS, &polygon_offset_units_);
230  glGetFloatv(GL_SAMPLE_COVERAGE_VALUE, &sample_coverage_value_);
231  glGetBooleanv(GL_SAMPLE_COVERAGE_INVERT, &sample_coverage_invert_);
232  glGetIntegerv(GL_BLEND_EQUATION_RGB, &blend_equation_rgb_);
233  glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &blend_equation_alpha_);
234
235  glGetBooleanv(GL_DITHER, &enable_dither_);
236  glGetBooleanv(GL_POLYGON_OFFSET_FILL, &enable_polygon_offset_fill_);
237  glGetBooleanv(GL_SAMPLE_ALPHA_TO_COVERAGE, &enable_sample_alpha_to_coverage_);
238  glGetBooleanv(GL_SAMPLE_COVERAGE, &enable_sample_coverage_);
239
240  glGetBooleanv(GL_STENCIL_TEST, &stencil_test_);
241  glGetIntegerv(GL_STENCIL_FUNC, &stencil_front_func_);
242  glGetIntegerv(GL_STENCIL_VALUE_MASK, &stencil_front_mask_);
243  glGetIntegerv(GL_STENCIL_REF, &stencil_front_ref_);
244  glGetIntegerv(GL_STENCIL_BACK_FUNC, &stencil_back_func_);
245  glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, &stencil_back_mask_);
246  glGetIntegerv(GL_STENCIL_BACK_REF, &stencil_back_ref_);
247  glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &stencil_clear_);
248  glGetIntegerv(GL_STENCIL_WRITEMASK, &stencil_front_writemask_);
249  glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &stencil_back_writemask_);
250  glGetIntegerv(GL_STENCIL_FAIL, &stencil_front_fail_op_);
251  glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &stencil_front_z_fail_op_);
252  glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &stencil_front_z_pass_op_);
253  glGetIntegerv(GL_STENCIL_BACK_FAIL, &stencil_back_fail_op_);
254  glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, &stencil_back_z_fail_op_);
255  glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, &stencil_back_z_pass_op_);
256
257  glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &framebuffer_binding_ext_);
258
259  glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture_);
260
261  texture_bindings_.resize(g_gl_max_texture_units);
262  for (int ii = 0; ii < g_gl_max_texture_units; ++ii) {
263    glActiveTexture(GL_TEXTURE0 + ii);
264    TextureBindings& bindings = texture_bindings_[ii];
265    glGetIntegerv(GL_TEXTURE_BINDING_2D, &bindings.texture_2d);
266    glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &bindings.texture_cube_map);
267    glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES,
268                  &bindings.texture_external_oes);
269  }
270
271  if (g_supports_oes_vertex_array_object) {
272    glGetIntegerv(GL_VERTEX_ARRAY_BINDING_OES, &vertex_array_bindings_oes_);
273    glBindVertexArrayOES(0);
274  }
275
276  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(vertex_attrib_); ++i) {
277    glGetVertexAttribiv(
278        i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &vertex_attrib_[i].enabled);
279    glGetVertexAttribiv(
280        i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &vertex_attrib_[i].size);
281    glGetVertexAttribiv(
282        i, GL_VERTEX_ATTRIB_ARRAY_TYPE, &vertex_attrib_[i].type);
283    glGetVertexAttribiv(
284        i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &vertex_attrib_[i].normalized);
285    glGetVertexAttribiv(
286        i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &vertex_attrib_[i].stride);
287    glGetVertexAttribPointerv(
288        i, GL_VERTEX_ATTRIB_ARRAY_POINTER, &vertex_attrib_[i].pointer);
289    glGetVertexAttribPointerv(
290        i, GL_VERTEX_ATTRIB_ARRAY_POINTER, &vertex_attrib_[i].pointer);
291    glGetVertexAttribiv(i,
292                        GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING,
293                        &vertex_attrib_[i].vertex_attrib_array_buffer_binding);
294    glGetVertexAttribfv(
295        i, GL_CURRENT_VERTEX_ATTRIB, vertex_attrib_[i].current_vertex_attrib);
296  }
297
298  // Android 5.0.0 specific qualcomm workaround. See crbug.com/434570.
299  glBindRenderbufferEXT(GL_RENDERBUFFER, 0);
300  DCHECK(ClearGLErrors(false, NULL));
301}
302
303ScopedAppGLStateRestoreImpl::~ScopedAppGLStateRestoreImpl() {
304  TRACE_EVENT0("android_webview", "AppGLStateRestore");
305  MakeAppContextCurrent();
306
307  DCHECK(ClearGLErrors(false, NULL));
308
309  glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer_binding_ext_);
310  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_buffer_binding_);
311
312  if (g_supports_oes_vertex_array_object)
313    glBindVertexArrayOES(0);
314
315  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(vertex_attrib_); ++i) {
316    glBindBuffer(GL_ARRAY_BUFFER,
317                 vertex_attrib_[i].vertex_attrib_array_buffer_binding);
318    glVertexAttribPointer(i,
319                          vertex_attrib_[i].size,
320                          vertex_attrib_[i].type,
321                          vertex_attrib_[i].normalized,
322                          vertex_attrib_[i].stride,
323                          vertex_attrib_[i].pointer);
324
325    glVertexAttrib4fv(i, vertex_attrib_[i].current_vertex_attrib);
326
327    if (vertex_attrib_[i].enabled) {
328      glEnableVertexAttribArray(i);
329    } else {
330      glDisableVertexAttribArray(i);
331    }
332  }
333
334  if (g_supports_oes_vertex_array_object && vertex_array_bindings_oes_ != 0)
335    glBindVertexArrayOES(vertex_array_bindings_oes_);
336
337  glBindBuffer(GL_ARRAY_BUFFER, vertex_array_buffer_binding_);
338
339  for (int ii = 0; ii < g_gl_max_texture_units; ++ii) {
340    glActiveTexture(GL_TEXTURE0 + ii);
341    TextureBindings& bindings = texture_bindings_[ii];
342    glBindTexture(GL_TEXTURE_2D, bindings.texture_2d);
343    glBindTexture(GL_TEXTURE_CUBE_MAP, bindings.texture_cube_map);
344    glBindTexture(GL_TEXTURE_EXTERNAL_OES, bindings.texture_external_oes);
345  }
346  glActiveTexture(active_texture_);
347
348  glPixelStorei(GL_PACK_ALIGNMENT, pack_alignment_);
349  glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment_);
350
351  GLEnableDisable(GL_DEPTH_TEST, depth_test_);
352
353  GLEnableDisable(GL_CULL_FACE, cull_face_);
354  glCullFace(cull_face_mode_);
355
356  glColorMask(color_mask_[0], color_mask_[1], color_mask_[2], color_mask_[3]);
357
358  glUseProgram(current_program_);
359
360  glClearColor(
361      color_clear_[0], color_clear_[1], color_clear_[2], color_clear_[3]);
362  glBlendColor(
363      blend_color_[0], blend_color_[1], blend_color_[2], blend_color_[3]);
364  glClearDepth(depth_clear_);
365  glDepthFunc(depth_func_);
366  glDepthMask(depth_mask_);
367  glDepthRange(depth_rage_[0], depth_rage_[1]);
368  glFrontFace(front_face_);
369  glHint(GL_GENERATE_MIPMAP_HINT, hint_generate_mipmap_);
370  // TODO(boliu): GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES ??
371  glLineWidth(line_width_);
372  glPolygonOffset(polygon_offset_factor_, polygon_offset_units_);
373  glSampleCoverage(sample_coverage_value_, sample_coverage_invert_);
374  glBlendEquationSeparate(blend_equation_rgb_, blend_equation_alpha_);
375
376  GLEnableDisable(GL_DITHER, enable_dither_);
377  GLEnableDisable(GL_POLYGON_OFFSET_FILL, enable_polygon_offset_fill_);
378  GLEnableDisable(GL_SAMPLE_ALPHA_TO_COVERAGE,
379                  enable_sample_alpha_to_coverage_);
380  GLEnableDisable(GL_SAMPLE_COVERAGE, enable_sample_coverage_);
381
382  switch(mode_) {
383    case ScopedAppGLStateRestore::MODE_DRAW:
384      // No-op.
385      break;
386    case ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT:
387      GLEnableDisable(GL_BLEND, blend_enabled_);
388      glBlendFuncSeparate(
389          blend_src_rgb_, blend_dest_rgb_, blend_src_alpha_, blend_dest_alpha_);
390
391      glViewport(viewport_[0], viewport_[1], viewport_[2], viewport_[3]);
392
393      GLEnableDisable(GL_SCISSOR_TEST, scissor_test_);
394
395      glScissor(
396          scissor_box_[0], scissor_box_[1], scissor_box_[2], scissor_box_[3]);
397      break;
398  }
399
400  GLEnableDisable(GL_STENCIL_TEST, stencil_test_);
401  glStencilFuncSeparate(
402      GL_FRONT, stencil_front_func_, stencil_front_mask_, stencil_front_ref_);
403  glStencilFuncSeparate(
404      GL_BACK, stencil_back_func_, stencil_back_mask_, stencil_back_ref_);
405  glClearStencil(stencil_clear_);
406  glStencilMaskSeparate(GL_FRONT, stencil_front_writemask_);
407  glStencilMaskSeparate(GL_BACK, stencil_back_writemask_);
408  glStencilOpSeparate(GL_FRONT,
409                      stencil_front_fail_op_,
410                      stencil_front_z_fail_op_,
411                      stencil_front_z_pass_op_);
412  glStencilOpSeparate(GL_BACK,
413                      stencil_back_fail_op_,
414                      stencil_back_z_fail_op_,
415                      stencil_back_z_pass_op_);
416
417  // Do not leak GLError out of chromium.
418  ClearGLErrors(true, "Chromium GLError");
419}
420
421}  // namespace internal
422
423ScopedAppGLStateRestore::ScopedAppGLStateRestore(CallMode mode)
424    : impl_(new internal::ScopedAppGLStateRestoreImpl(mode)) {
425}
426
427ScopedAppGLStateRestore::~ScopedAppGLStateRestore() {}
428
429bool ScopedAppGLStateRestore::stencil_enabled() const {
430  return impl_->stencil_enabled();
431}
432int ScopedAppGLStateRestore::framebuffer_binding_ext() const {
433  return impl_->framebuffer_binding_ext();
434}
435
436}  // namespace android_webview
437