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
18namespace gfx {
19
20// The GL Api being used. This could be g_real_gl or gl_trace_gl
21static GLApi* g_gl;
22// A GL Api that calls directly into the driver.
23static RealGLApi* g_real_gl;
24// A GL Api that calls TRACE and then calls another GL api.
25static TraceGLApi* g_trace_gl;
26
27namespace {
28
29static inline GLenum GetTexInternalFormat(GLenum internal_format) {
30  if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) {
31    if (internal_format == GL_BGRA_EXT || internal_format == GL_BGRA8_EXT)
32      return GL_RGBA8;
33  }
34  return internal_format;
35}
36
37// TODO(epenner): Could the above function be merged into this and removed?
38static inline GLenum GetTexInternalFormat(GLenum internal_format,
39                                          GLenum format,
40                                          GLenum type) {
41  GLenum gl_internal_format = GetTexInternalFormat(internal_format);
42
43  if (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2)
44    return gl_internal_format;
45
46  if (type == GL_FLOAT) {
47    switch (format) {
48      case GL_RGBA:
49        gl_internal_format = GL_RGBA32F_ARB;
50        break;
51      case GL_RGB:
52        gl_internal_format = GL_RGB32F_ARB;
53        break;
54      case GL_LUMINANCE_ALPHA:
55        gl_internal_format = GL_LUMINANCE_ALPHA32F_ARB;
56        break;
57      case GL_LUMINANCE:
58        gl_internal_format = GL_LUMINANCE32F_ARB;
59        break;
60      case GL_ALPHA:
61        gl_internal_format = GL_ALPHA32F_ARB;
62        break;
63      default:
64        NOTREACHED();
65        break;
66    }
67  } else if (type == GL_HALF_FLOAT_OES) {
68    switch (format) {
69      case GL_RGBA:
70        gl_internal_format = GL_RGBA16F_ARB;
71        break;
72      case GL_RGB:
73        gl_internal_format = GL_RGB16F_ARB;
74        break;
75      case GL_LUMINANCE_ALPHA:
76        gl_internal_format = GL_LUMINANCE_ALPHA16F_ARB;
77        break;
78      case GL_LUMINANCE:
79        gl_internal_format = GL_LUMINANCE16F_ARB;
80        break;
81      case GL_ALPHA:
82        gl_internal_format = GL_ALPHA16F_ARB;
83        break;
84      default:
85        NOTREACHED();
86        break;
87    }
88  }
89  return gl_internal_format;
90}
91
92static inline GLenum GetTexType(GLenum type) {
93   if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) {
94     if (type == GL_HALF_FLOAT_OES)
95       return GL_HALF_FLOAT_ARB;
96   }
97   return type;
98}
99
100static void GL_BINDING_CALL CustomTexImage2D(
101    GLenum target, GLint level, GLint internalformat,
102    GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type,
103    const void* pixels) {
104  GLenum gl_internal_format = GetTexInternalFormat(
105      internalformat, format, type);
106  GLenum gl_type = GetTexType(type);
107  return g_driver_gl.orig_fn.glTexImage2DFn(
108      target, level, gl_internal_format, width, height, border, format, gl_type,
109      pixels);
110}
111
112static void GL_BINDING_CALL CustomTexSubImage2D(
113      GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
114      GLsizei height, GLenum format, GLenum type, const void* pixels) {
115  GLenum gl_type = GetTexType(type);
116  return g_driver_gl.orig_fn.glTexSubImage2DFn(
117      target, level, xoffset, yoffset, width, height, format, gl_type, pixels);
118}
119
120static void GL_BINDING_CALL CustomTexStorage2DEXT(
121    GLenum target, GLsizei levels, GLenum internalformat, GLsizei width,
122    GLsizei height) {
123  GLenum gl_internal_format = GetTexInternalFormat(internalformat);
124  return g_driver_gl.orig_fn.glTexStorage2DEXTFn(
125      target, levels, gl_internal_format, width, height);
126}
127
128}  // anonymous namespace
129
130void DriverGL::Initialize() {
131  InitializeBindings();
132}
133
134void DriverGL::InitializeExtensions(GLContext* context) {
135  InitializeExtensionBindings(context);
136  orig_fn = fn;
137  fn.glTexImage2DFn =
138      reinterpret_cast<glTexImage2DProc>(CustomTexImage2D);
139  fn.glTexSubImage2DFn =
140      reinterpret_cast<glTexSubImage2DProc>(CustomTexSubImage2D);
141  fn.glTexStorage2DEXTFn =
142      reinterpret_cast<glTexStorage2DEXTProc>(CustomTexStorage2DEXT);
143}
144
145void InitializeGLBindingsGL() {
146  g_current_gl_context_tls = new base::ThreadLocalPointer<GLApi>;
147  g_driver_gl.Initialize();
148  if (!g_real_gl) {
149    g_real_gl = new RealGLApi();
150    g_trace_gl = new TraceGLApi(g_real_gl);
151  }
152  g_real_gl->Initialize(&g_driver_gl);
153  g_gl = g_real_gl;
154  if (CommandLine::ForCurrentProcess()->HasSwitch(
155      switches::kEnableGPUServiceTracing)) {
156    g_gl = g_trace_gl;
157  }
158  SetGLToRealGLApi();
159}
160
161GLApi* GetCurrentGLApi() {
162  return g_current_gl_context_tls->Get();
163}
164
165void SetGLApi(GLApi* api) {
166  g_current_gl_context_tls->Set(api);
167}
168
169void SetGLToRealGLApi() {
170  SetGLApi(g_gl);
171}
172
173void InitializeGLExtensionBindingsGL(GLContext* context) {
174  g_driver_gl.InitializeExtensions(context);
175}
176
177void InitializeDebugGLBindingsGL() {
178  g_driver_gl.InitializeDebugBindings();
179}
180
181void ClearGLBindingsGL() {
182  if (g_real_gl) {
183    delete g_real_gl;
184    g_real_gl = NULL;
185  }
186  if (g_trace_gl) {
187    delete g_trace_gl;
188    g_trace_gl = NULL;
189  }
190  g_gl = NULL;
191  g_driver_gl.ClearBindings();
192  if (g_current_gl_context_tls) {
193    delete g_current_gl_context_tls;
194    g_current_gl_context_tls = NULL;
195  }
196}
197
198GLApi::GLApi() {
199}
200
201GLApi::~GLApi() {
202  if (GetCurrentGLApi() == this)
203    SetGLApi(NULL);
204}
205
206GLApiBase::GLApiBase()
207    : driver_(NULL) {
208}
209
210GLApiBase::~GLApiBase() {
211}
212
213void GLApiBase::InitializeBase(DriverGL* driver) {
214  driver_ = driver;
215}
216
217RealGLApi::RealGLApi() {
218}
219
220RealGLApi::~RealGLApi() {
221}
222
223void RealGLApi::Initialize(DriverGL* driver) {
224  InitializeBase(driver);
225}
226
227TraceGLApi::~TraceGLApi() {
228}
229
230VirtualGLApi::VirtualGLApi()
231    : real_context_(NULL),
232      current_context_(NULL) {
233}
234
235VirtualGLApi::~VirtualGLApi() {
236}
237
238void VirtualGLApi::Initialize(DriverGL* driver, GLContext* real_context) {
239  InitializeBase(driver);
240  real_context_ = real_context;
241
242  DCHECK(real_context->IsCurrent(NULL));
243  std::string ext_string(
244      reinterpret_cast<const char*>(driver_->fn.glGetStringFn(GL_EXTENSIONS)));
245  std::vector<std::string> ext;
246  Tokenize(ext_string, " ", &ext);
247
248  std::vector<std::string>::iterator it;
249  // We can't support GL_EXT_occlusion_query_boolean which is
250  // based on GL_ARB_occlusion_query without a lot of work virtualizing
251  // queries.
252  it = std::find(ext.begin(), ext.end(), "GL_EXT_occlusion_query_boolean");
253  if (it != ext.end())
254    ext.erase(it);
255
256  extensions_ = JoinString(ext, " ");
257}
258
259bool VirtualGLApi::MakeCurrent(GLContext* virtual_context, GLSurface* surface) {
260  bool switched_contexts = g_current_gl_context_tls->Get() != this;
261  GLSurface* current_surface = GLSurface::GetCurrent();
262  if (switched_contexts || surface != current_surface) {
263    // MakeCurrent 'lite' path that avoids potentially expensive MakeCurrent()
264    // calls if the GLSurface uses the same underlying surface or renders to
265    // an FBO.
266    if (switched_contexts || !current_surface ||
267        !virtual_context->IsCurrent(surface)) {
268      if (!real_context_->MakeCurrent(surface)) {
269        return false;
270      }
271    }
272  }
273
274  DCHECK_EQ(real_context_, GLContext::GetRealCurrent());
275  DCHECK(real_context_->IsCurrent(NULL));
276  DCHECK(virtual_context->IsCurrent(surface));
277
278  if (switched_contexts || virtual_context != current_context_) {
279    // There should be no errors from the previous context leaking into the
280    // new context.
281    DCHECK_EQ(glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
282
283    current_context_ = virtual_context;
284    // Set all state that is different from the real state
285    // NOTE: !!! This is a temporary implementation that just restores all
286    // state to let us test that it works.
287    // TODO: ASAP, change this to something that only restores the state
288    // needed for individual GL calls.
289    GLApi* temp = GetCurrentGLApi();
290    SetGLToRealGLApi();
291    if (virtual_context->GetGLStateRestorer()->IsInitialized())
292      virtual_context->GetGLStateRestorer()->RestoreState();
293    SetGLApi(temp);
294  }
295  SetGLApi(this);
296
297  virtual_context->SetCurrent(surface);
298  if (!surface->OnMakeCurrent(virtual_context)) {
299    LOG(ERROR) << "Could not make GLSurface current.";
300    return false;
301  }
302  return true;
303}
304
305void VirtualGLApi::OnReleaseVirtuallyCurrent(GLContext* virtual_context) {
306  if (current_context_ == virtual_context)
307    current_context_ = NULL;
308}
309
310const GLubyte* VirtualGLApi::glGetStringFn(GLenum name) {
311  switch (name) {
312    case GL_EXTENSIONS:
313      return reinterpret_cast<const GLubyte*>(extensions_.c_str());
314    default:
315      return driver_->fn.glGetStringFn(name);
316  }
317}
318
319}  // namespace gfx
320