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 "ui/gl/gl_gl_api_implementation.h"
6
7#include <algorithm>
8#include <vector>
9
10#include "base/command_line.h"
11#include "base/strings/string_util.h"
12#include "ui/gl/gl_context.h"
13#include "ui/gl/gl_implementation.h"
14#include "ui/gl/gl_state_restorer.h"
15#include "ui/gl/gl_surface.h"
16#include "ui/gl/gl_switches.h"
17#include "ui/gl/gl_version_info.h"
18
19namespace gfx {
20
21// The GL Api being used. This could be g_real_gl or gl_trace_gl
22static GLApi* g_gl = NULL;
23// A GL Api that calls directly into the driver.
24static RealGLApi* g_real_gl = NULL;
25// A GL Api that does nothing but warn about illegal GL calls without a context
26// current.
27static NoContextGLApi* g_no_context_gl = NULL;
28// A GL Api that calls TRACE and then calls another GL api.
29static TraceGLApi* g_trace_gl = NULL;
30// GL version used when initializing dynamic bindings.
31static GLVersionInfo* g_version_info = NULL;
32
33namespace {
34
35static inline GLenum GetInternalFormat(GLenum internal_format) {
36  if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) {
37    if (internal_format == GL_BGRA_EXT || internal_format == GL_BGRA8_EXT)
38      return GL_RGBA8;
39  }
40  return internal_format;
41}
42
43// TODO(epenner): Could the above function be merged into this and removed?
44static inline GLenum GetTexInternalFormat(GLenum internal_format,
45                                          GLenum format,
46                                          GLenum type) {
47  GLenum gl_internal_format = GetInternalFormat(internal_format);
48
49  // g_version_info must be initialized when this function is bound.
50  DCHECK(gfx::g_version_info);
51  if (type == GL_FLOAT && gfx::g_version_info->is_angle &&
52      gfx::g_version_info->is_es2) {
53    // It's possible that the texture is using a sized internal format, and
54    // ANGLE exposing GLES2 API doesn't support those.
55    // TODO(oetuaho@nvidia.com): Remove these conversions once ANGLE has the
56    // support.
57    // http://code.google.com/p/angleproject/issues/detail?id=556
58    switch (format) {
59      case GL_RGBA:
60        gl_internal_format = GL_RGBA;
61        break;
62      case GL_RGB:
63        gl_internal_format = GL_RGB;
64        break;
65      default:
66        break;
67    }
68  }
69
70  if (gfx::g_version_info->is_es)
71    return gl_internal_format;
72
73  if (type == GL_FLOAT) {
74    switch (format) {
75      case GL_RGBA:
76        gl_internal_format = GL_RGBA32F_ARB;
77        break;
78      case GL_RGB:
79        gl_internal_format = GL_RGB32F_ARB;
80        break;
81      case GL_LUMINANCE_ALPHA:
82        gl_internal_format = GL_LUMINANCE_ALPHA32F_ARB;
83        break;
84      case GL_LUMINANCE:
85        gl_internal_format = GL_LUMINANCE32F_ARB;
86        break;
87      case GL_ALPHA:
88        gl_internal_format = GL_ALPHA32F_ARB;
89        break;
90      default:
91        NOTREACHED();
92        break;
93    }
94  } else if (type == GL_HALF_FLOAT_OES) {
95    switch (format) {
96      case GL_RGBA:
97        gl_internal_format = GL_RGBA16F_ARB;
98        break;
99      case GL_RGB:
100        gl_internal_format = GL_RGB16F_ARB;
101        break;
102      case GL_LUMINANCE_ALPHA:
103        gl_internal_format = GL_LUMINANCE_ALPHA16F_ARB;
104        break;
105      case GL_LUMINANCE:
106        gl_internal_format = GL_LUMINANCE16F_ARB;
107        break;
108      case GL_ALPHA:
109        gl_internal_format = GL_ALPHA16F_ARB;
110        break;
111      default:
112        NOTREACHED();
113        break;
114    }
115  }
116  return gl_internal_format;
117}
118
119static inline GLenum GetTexType(GLenum type) {
120   if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) {
121     if (type == GL_HALF_FLOAT_OES)
122       return GL_HALF_FLOAT_ARB;
123   }
124   return type;
125}
126
127static void GL_BINDING_CALL CustomTexImage2D(
128    GLenum target, GLint level, GLint internalformat,
129    GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type,
130    const void* pixels) {
131  GLenum gl_internal_format = GetTexInternalFormat(
132      internalformat, format, type);
133  GLenum gl_type = GetTexType(type);
134  g_driver_gl.orig_fn.glTexImage2DFn(
135      target, level, gl_internal_format, width, height, border, format, gl_type,
136      pixels);
137}
138
139static void GL_BINDING_CALL CustomTexSubImage2D(
140      GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
141      GLsizei height, GLenum format, GLenum type, const void* pixels) {
142  GLenum gl_type = GetTexType(type);
143  g_driver_gl.orig_fn.glTexSubImage2DFn(
144      target, level, xoffset, yoffset, width, height, format, gl_type, pixels);
145}
146
147static void GL_BINDING_CALL CustomTexStorage2DEXT(
148    GLenum target, GLsizei levels, GLenum internalformat, GLsizei width,
149    GLsizei height) {
150  GLenum gl_internal_format = GetInternalFormat(internalformat);
151  g_driver_gl.orig_fn.glTexStorage2DEXTFn(
152      target, levels, gl_internal_format, width, height);
153}
154
155static void GL_BINDING_CALL CustomRenderbufferStorageEXT(
156    GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
157  GLenum gl_internal_format = GetInternalFormat(internalformat);
158  g_driver_gl.orig_fn.glRenderbufferStorageEXTFn(
159      target, gl_internal_format, width, height);
160}
161
162// The ANGLE and IMG variants of glRenderbufferStorageMultisample currently do
163// not support BGRA render buffers so only the EXT one is customized. If
164// GL_CHROMIUM_renderbuffer_format_BGRA8888 support is added to ANGLE then the
165// ANGLE version should also be customized.
166static void GL_BINDING_CALL CustomRenderbufferStorageMultisampleEXT(
167    GLenum target, GLsizei samples, GLenum internalformat, GLsizei width,
168    GLsizei height) {
169  GLenum gl_internal_format = GetInternalFormat(internalformat);
170  g_driver_gl.orig_fn.glRenderbufferStorageMultisampleEXTFn(
171      target, samples, gl_internal_format, width, height);
172}
173
174}  // anonymous namespace
175
176void DriverGL::InitializeCustomDynamicBindings(GLContext* context) {
177  InitializeDynamicBindings(context);
178
179  DCHECK(orig_fn.glTexImage2DFn == NULL);
180  orig_fn.glTexImage2DFn = fn.glTexImage2DFn;
181  fn.glTexImage2DFn =
182      reinterpret_cast<glTexImage2DProc>(CustomTexImage2D);
183
184  DCHECK(orig_fn.glTexSubImage2DFn == NULL);
185  orig_fn.glTexSubImage2DFn = fn.glTexSubImage2DFn;
186  fn.glTexSubImage2DFn =
187      reinterpret_cast<glTexSubImage2DProc>(CustomTexSubImage2D);
188
189  DCHECK(orig_fn.glTexStorage2DEXTFn == NULL);
190  orig_fn.glTexStorage2DEXTFn = fn.glTexStorage2DEXTFn;
191  fn.glTexStorage2DEXTFn =
192      reinterpret_cast<glTexStorage2DEXTProc>(CustomTexStorage2DEXT);
193
194  DCHECK(orig_fn.glRenderbufferStorageEXTFn == NULL);
195  orig_fn.glRenderbufferStorageEXTFn = fn.glRenderbufferStorageEXTFn;
196  fn.glRenderbufferStorageEXTFn =
197      reinterpret_cast<glRenderbufferStorageEXTProc>(
198      CustomRenderbufferStorageEXT);
199
200  DCHECK(orig_fn.glRenderbufferStorageMultisampleEXTFn == NULL);
201  orig_fn.glRenderbufferStorageMultisampleEXTFn =
202      fn.glRenderbufferStorageMultisampleEXTFn;
203  fn.glRenderbufferStorageMultisampleEXTFn =
204      reinterpret_cast<glRenderbufferStorageMultisampleEXTProc>(
205      CustomRenderbufferStorageMultisampleEXT);
206}
207
208static void GL_BINDING_CALL NullDrawClearFn(GLbitfield mask) {
209  if (!g_driver_gl.null_draw_bindings_enabled)
210    g_driver_gl.orig_fn.glClearFn(mask);
211}
212
213static void GL_BINDING_CALL
214NullDrawDrawArraysFn(GLenum mode, GLint first, GLsizei count) {
215  if (!g_driver_gl.null_draw_bindings_enabled)
216    g_driver_gl.orig_fn.glDrawArraysFn(mode, first, count);
217}
218
219static void GL_BINDING_CALL NullDrawDrawElementsFn(GLenum mode,
220                                                   GLsizei count,
221                                                   GLenum type,
222                                                   const void* indices) {
223  if (!g_driver_gl.null_draw_bindings_enabled)
224    g_driver_gl.orig_fn.glDrawElementsFn(mode, count, type, indices);
225}
226
227void DriverGL::InitializeNullDrawBindings() {
228  DCHECK(orig_fn.glClearFn == NULL);
229  orig_fn.glClearFn = fn.glClearFn;
230  fn.glClearFn = NullDrawClearFn;
231
232  DCHECK(orig_fn.glDrawArraysFn == NULL);
233  orig_fn.glDrawArraysFn = fn.glDrawArraysFn;
234  fn.glDrawArraysFn = NullDrawDrawArraysFn;
235
236  DCHECK(orig_fn.glDrawElementsFn == NULL);
237  orig_fn.glDrawElementsFn = fn.glDrawElementsFn;
238  fn.glDrawElementsFn = NullDrawDrawElementsFn;
239
240  null_draw_bindings_enabled = true;
241}
242
243bool DriverGL::HasInitializedNullDrawBindings() {
244  return orig_fn.glClearFn != NULL && orig_fn.glDrawArraysFn != NULL &&
245         orig_fn.glDrawElementsFn != NULL;
246}
247
248bool DriverGL::SetNullDrawBindingsEnabled(bool enabled) {
249  DCHECK(orig_fn.glClearFn != NULL);
250  DCHECK(orig_fn.glDrawArraysFn != NULL);
251  DCHECK(orig_fn.glDrawElementsFn != NULL);
252
253  bool before = null_draw_bindings_enabled;
254  null_draw_bindings_enabled = enabled;
255  return before;
256}
257
258void InitializeStaticGLBindingsGL() {
259  g_current_gl_context_tls = new base::ThreadLocalPointer<GLApi>;
260  g_driver_gl.InitializeStaticBindings();
261  if (!g_real_gl) {
262    g_real_gl = new RealGLApi();
263    g_trace_gl = new TraceGLApi(g_real_gl);
264    g_no_context_gl = new NoContextGLApi();
265  }
266  g_real_gl->Initialize(&g_driver_gl);
267  g_gl = g_real_gl;
268  if (CommandLine::ForCurrentProcess()->HasSwitch(
269      switches::kEnableGPUServiceTracing)) {
270    g_gl = g_trace_gl;
271  }
272  SetGLToRealGLApi();
273}
274
275GLApi* GetCurrentGLApi() {
276  return g_current_gl_context_tls->Get();
277}
278
279void SetGLApi(GLApi* api) {
280  g_current_gl_context_tls->Set(api);
281}
282
283void SetGLToRealGLApi() {
284  SetGLApi(g_gl);
285}
286
287void SetGLApiToNoContext() {
288  SetGLApi(g_no_context_gl);
289}
290
291const GLVersionInfo* GetGLVersionInfo() {
292  return g_version_info;
293}
294
295void InitializeDynamicGLBindingsGL(GLContext* context) {
296  g_driver_gl.InitializeCustomDynamicBindings(context);
297  DCHECK(context && context->IsCurrent(NULL) && !g_version_info);
298  g_version_info = new GLVersionInfo(context->GetGLVersion().c_str(),
299      context->GetGLRenderer().c_str());
300}
301
302void InitializeDebugGLBindingsGL() {
303  g_driver_gl.InitializeDebugBindings();
304}
305
306void InitializeNullDrawGLBindingsGL() {
307  g_driver_gl.InitializeNullDrawBindings();
308}
309
310bool HasInitializedNullDrawGLBindingsGL() {
311  return g_driver_gl.HasInitializedNullDrawBindings();
312}
313
314bool SetNullDrawGLBindingsEnabledGL(bool enabled) {
315  return g_driver_gl.SetNullDrawBindingsEnabled(enabled);
316}
317
318void ClearGLBindingsGL() {
319  if (g_real_gl) {
320    delete g_real_gl;
321    g_real_gl = NULL;
322  }
323  if (g_trace_gl) {
324    delete g_trace_gl;
325    g_trace_gl = NULL;
326  }
327  if (g_no_context_gl) {
328    delete g_no_context_gl;
329    g_no_context_gl = NULL;
330  }
331  g_gl = NULL;
332  g_driver_gl.ClearBindings();
333  if (g_current_gl_context_tls) {
334    delete g_current_gl_context_tls;
335    g_current_gl_context_tls = NULL;
336  }
337  if (g_version_info) {
338    delete g_version_info;
339    g_version_info = NULL;
340  }
341}
342
343GLApi::GLApi() {
344}
345
346GLApi::~GLApi() {
347  if (GetCurrentGLApi() == this)
348    SetGLApi(NULL);
349}
350
351GLApiBase::GLApiBase()
352    : driver_(NULL) {
353}
354
355GLApiBase::~GLApiBase() {
356}
357
358void GLApiBase::InitializeBase(DriverGL* driver) {
359  driver_ = driver;
360}
361
362void GLApiBase::SignalFlush() {
363  DCHECK(GLContext::GetCurrent());
364  GLContext::GetCurrent()->OnFlush();
365}
366
367RealGLApi::RealGLApi() {
368}
369
370RealGLApi::~RealGLApi() {
371}
372
373void RealGLApi::Initialize(DriverGL* driver) {
374  InitializeBase(driver);
375}
376
377void RealGLApi::glFlushFn() {
378  GLApiBase::glFlushFn();
379  GLApiBase::SignalFlush();
380}
381
382void RealGLApi::glFinishFn() {
383  GLApiBase::glFinishFn();
384  GLApiBase::SignalFlush();
385}
386
387TraceGLApi::~TraceGLApi() {
388}
389
390NoContextGLApi::NoContextGLApi() {
391}
392
393NoContextGLApi::~NoContextGLApi() {
394}
395
396VirtualGLApi::VirtualGLApi()
397    : real_context_(NULL),
398      current_context_(NULL) {
399}
400
401VirtualGLApi::~VirtualGLApi() {
402}
403
404void VirtualGLApi::Initialize(DriverGL* driver, GLContext* real_context) {
405  InitializeBase(driver);
406  real_context_ = real_context;
407
408  DCHECK(real_context->IsCurrent(NULL));
409  std::string ext_string(
410      reinterpret_cast<const char*>(driver_->fn.glGetStringFn(GL_EXTENSIONS)));
411  std::vector<std::string> ext;
412  Tokenize(ext_string, " ", &ext);
413
414  std::vector<std::string>::iterator it;
415  // We can't support GL_EXT_occlusion_query_boolean which is
416  // based on GL_ARB_occlusion_query without a lot of work virtualizing
417  // queries.
418  it = std::find(ext.begin(), ext.end(), "GL_EXT_occlusion_query_boolean");
419  if (it != ext.end())
420    ext.erase(it);
421
422  extensions_ = JoinString(ext, " ");
423}
424
425bool VirtualGLApi::MakeCurrent(GLContext* virtual_context, GLSurface* surface) {
426  bool switched_contexts = g_current_gl_context_tls->Get() != this;
427  GLSurface* current_surface = GLSurface::GetCurrent();
428  if (switched_contexts || surface != current_surface) {
429    // MakeCurrent 'lite' path that avoids potentially expensive MakeCurrent()
430    // calls if the GLSurface uses the same underlying surface or renders to
431    // an FBO.
432    if (switched_contexts || !current_surface ||
433        !virtual_context->IsCurrent(surface)) {
434      if (!real_context_->MakeCurrent(surface)) {
435        return false;
436      }
437    }
438  }
439
440  DCHECK_EQ(real_context_, GLContext::GetRealCurrent());
441  DCHECK(real_context_->IsCurrent(NULL));
442  DCHECK(virtual_context->IsCurrent(surface));
443
444  if (switched_contexts || virtual_context != current_context_) {
445    // There should be no errors from the previous context leaking into the
446    // new context.
447    DCHECK_EQ(glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
448
449    // Set all state that is different from the real state
450    GLApi* temp = GetCurrentGLApi();
451    SetGLToRealGLApi();
452    if (virtual_context->GetGLStateRestorer()->IsInitialized()) {
453      virtual_context->GetGLStateRestorer()->RestoreState(
454          (current_context_ && !switched_contexts)
455              ? current_context_->GetGLStateRestorer()
456              : NULL);
457    }
458    SetGLApi(temp);
459    current_context_ = virtual_context;
460  }
461  SetGLApi(this);
462
463  virtual_context->SetCurrent(surface);
464  if (!surface->OnMakeCurrent(virtual_context)) {
465    LOG(ERROR) << "Could not make GLSurface current.";
466    return false;
467  }
468  return true;
469}
470
471void VirtualGLApi::OnReleaseVirtuallyCurrent(GLContext* virtual_context) {
472  if (current_context_ == virtual_context)
473    current_context_ = NULL;
474}
475
476const GLubyte* VirtualGLApi::glGetStringFn(GLenum name) {
477  switch (name) {
478    case GL_EXTENSIONS:
479      return reinterpret_cast<const GLubyte*>(extensions_.c_str());
480    default:
481      return driver_->fn.glGetStringFn(name);
482  }
483}
484
485void VirtualGLApi::glFlushFn() {
486  GLApiBase::glFlushFn();
487  GLApiBase::SignalFlush();
488}
489
490void VirtualGLApi::glFinishFn() {
491  GLApiBase::glFinishFn();
492  GLApiBase::SignalFlush();
493}
494
495ScopedSetGLToRealGLApi::ScopedSetGLToRealGLApi()
496    : old_gl_api_(GetCurrentGLApi()) {
497  SetGLToRealGLApi();
498}
499
500ScopedSetGLToRealGLApi::~ScopedSetGLToRealGLApi() {
501  SetGLApi(old_gl_api_);
502}
503
504}  // namespace gfx
505