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