/* * Copyright 2009, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // This file implements the GAPIGL class. #include #include "command_buffer/service/gapi_gl.h" #ifdef OS_LINUX #include "command_buffer/service/linux/x_utils.h" #endif // OS_LINUX namespace command_buffer { namespace o3d { GAPIGL::GAPIGL() : cg_context_(NULL), #ifdef OS_LINUX window_(NULL), #endif #ifdef OS_WIN hwnd_(NULL), device_context_(NULL), gl_context_(NULL), #endif anti_aliased_(false), current_vertex_struct_(kInvalidResource), validate_streams_(true), max_vertices_(0), current_effect_id_(kInvalidResource), validate_effect_(true), current_effect_(NULL) { } GAPIGL::~GAPIGL() { } bool GAPIGL::Initialize() { if (!InitPlatformSpecific()) return false; if (!InitCommon()) return false; CHECK_GL_ERROR(); return true; } #if defined(OS_WIN) static const PIXELFORMATDESCRIPTOR kPixelFormatDescriptor = { sizeof(kPixelFormatDescriptor), // Size of structure. 1, // Default version. PFD_DRAW_TO_WINDOW | // Window drawing support. PFD_SUPPORT_OPENGL | // OpenGL support. PFD_DOUBLEBUFFER, // Double buffering support (not stereo). PFD_TYPE_RGBA, // RGBA color mode (not indexed). 24, // 24 bit color mode. 0, 0, 0, 0, 0, 0, // Don't set RGB bits & shifts. 8, 0, // 8 bit alpha 0, // No accumulation buffer. 0, 0, 0, 0, // Ignore accumulation bits. 24, // 24 bit z-buffer size. 8, // 8-bit stencil buffer. 0, // No aux buffer. PFD_MAIN_PLANE, // Main drawing plane (not overlay). 0, // Reserved. 0, 0, 0, // Layer masks ignored. }; LRESULT CALLBACK IntermediateWindowProc(HWND window, UINT message, WPARAM w_param, LPARAM l_param) { return ::DefWindowProc(window, message, w_param, l_param); } // Helper routine that returns the highest quality pixel format supported on // the current platform. Returns true upon success. static bool GetWindowsPixelFormat(HWND window, bool anti_aliased, int* pixel_format) { // We must initialize a GL context before we can determine the multi-sampling // supported on the current hardware, so we create an intermediate window // and context here. HINSTANCE module_handle; if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(IntermediateWindowProc), &module_handle)) { return false; } WNDCLASS intermediate_class; intermediate_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; intermediate_class.lpfnWndProc = IntermediateWindowProc; intermediate_class.cbClsExtra = 0; intermediate_class.cbWndExtra = 0; intermediate_class.hInstance = module_handle; intermediate_class.hIcon = LoadIcon(NULL, IDI_APPLICATION); intermediate_class.hCursor = LoadCursor(NULL, IDC_ARROW); intermediate_class.hbrBackground = NULL; intermediate_class.lpszMenuName = NULL; intermediate_class.lpszClassName = L"Intermediate GL Window"; ATOM class_registration = ::RegisterClass(&intermediate_class); if (!class_registration) { return false; } HWND intermediate_window = ::CreateWindow( reinterpret_cast(class_registration), L"", WS_OVERLAPPEDWINDOW, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL); if (!intermediate_window) { ::UnregisterClass(reinterpret_cast(class_registration), module_handle); return false; } HDC intermediate_dc = ::GetDC(intermediate_window); int format_index = ::ChoosePixelFormat(intermediate_dc, &kPixelFormatDescriptor); if (format_index == 0) { DLOG(ERROR) << "Unable to get the pixel format for GL context."; ::ReleaseDC(intermediate_window, intermediate_dc); ::DestroyWindow(intermediate_window); ::UnregisterClass(reinterpret_cast(class_registration), module_handle); return false; } if (!::SetPixelFormat(intermediate_dc, format_index, &kPixelFormatDescriptor)) { DLOG(ERROR) << "Unable to set the pixel format for GL context."; ::ReleaseDC(intermediate_window, intermediate_dc); ::DestroyWindow(intermediate_window); ::UnregisterClass(reinterpret_cast(class_registration), module_handle); return false; } // Store the pixel format without multisampling. *pixel_format = format_index; HGLRC gl_context = ::wglCreateContext(intermediate_dc); if (::wglMakeCurrent(intermediate_dc, gl_context)) { // GL context was successfully created and applied to the window's DC. // Startup GLEW, the GL extensions wrangler. GLenum glew_error = ::glewInit(); if (glew_error == GLEW_OK) { DLOG(INFO) << "Initialized GLEW " << ::glewGetString(GLEW_VERSION); } else { DLOG(ERROR) << "Unable to initialise GLEW : " << ::glewGetErrorString(glew_error); ::wglMakeCurrent(intermediate_dc, NULL); ::wglDeleteContext(gl_context); ::ReleaseDC(intermediate_window, intermediate_dc); ::DestroyWindow(intermediate_window); ::UnregisterClass(reinterpret_cast(class_registration), module_handle); return false; } // If the multi-sample extensions are present, query the api to determine // the pixel format. if (anti_aliased && WGLEW_ARB_pixel_format && WGLEW_ARB_multisample) { int pixel_attributes[] = { WGL_SAMPLES_ARB, 4, WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, WGL_SUPPORT_OPENGL_ARB, GL_TRUE, WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, WGL_COLOR_BITS_ARB, 24, WGL_ALPHA_BITS_ARB, 8, WGL_DEPTH_BITS_ARB, 24, WGL_STENCIL_BITS_ARB, 8, WGL_DOUBLE_BUFFER_ARB, GL_TRUE, WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, 0, 0}; float pixel_attributes_f[] = {0, 0}; int msaa_pixel_format; unsigned int num_formats; // Query for the highest sampling rate supported, starting at 4x. static const int kSampleCount[] = {4, 2}; static const int kNumSamples = 2; for (int sample = 0; sample < kNumSamples; ++sample) { pixel_attributes[1] = kSampleCount[sample]; if (GL_TRUE == ::wglChoosePixelFormatARB(intermediate_dc, pixel_attributes, pixel_attributes_f, 1, &msaa_pixel_format, &num_formats)) { *pixel_format = msaa_pixel_format; break; } } } } ::wglMakeCurrent(intermediate_dc, NULL); ::wglDeleteContext(gl_context); ::ReleaseDC(intermediate_window, intermediate_dc); ::DestroyWindow(intermediate_window); ::UnregisterClass(reinterpret_cast(class_registration), module_handle); return true; } #endif bool GAPIGL::InitPlatformSpecific() { #if defined(OS_WIN) device_context_ = ::GetDC(hwnd_); int pixel_format; if (!GetWindowsPixelFormat(hwnd_, anti_aliased(), &pixel_format)) { DLOG(ERROR) << "Unable to determine optimal pixel format for GL context."; return false; } if (!::SetPixelFormat(device_context_, pixel_format, &kPixelFormatDescriptor)) { DLOG(ERROR) << "Unable to set the pixel format for GL context."; return false; } gl_context_ = ::wglCreateContext(device_context_); if (!gl_context_) { DLOG(ERROR) << "Failed to create GL context."; return false; } if (!::wglMakeCurrent(device_context_, gl_context_)) { DLOG(ERROR) << "Unable to make gl context current."; return false; } #elif defined(OS_LINUX) DCHECK(window_); if (!window_->Initialize()) return false; if (!window_->MakeCurrent()) return false; #endif return true; } bool GAPIGL::InitCommon() { if (!InitGlew()) return false; InitCG(); // Initialize global GL settings. // Tell GL that texture buffers can be single-byte aligned. glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); // Get the initial viewport (set to the window size) to set up the helper // constant. GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); SetViewport(viewport[0], viewport[1], viewport[2], viewport[3], 0.f, 1.f); CHECK_GL_ERROR(); ::glGenFramebuffersEXT(1, &render_surface_framebuffer_); CHECK_GL_ERROR(); return true; } void GAPIGL::InitCG() { cg_context_ = cgCreateContext(); // Set up all Cg State Assignments for OpenGL. cgGLRegisterStates(cg_context_); cgGLSetDebugMode(CG_FALSE); // Enable the profiles we use. cgGLEnableProfile(CG_PROFILE_ARBVP1); cgGLEnableProfile(CG_PROFILE_ARBFP1); } bool GAPIGL::InitGlew() { DLOG(INFO) << "Initializing GL and GLEW for GAPI."; GLenum glew_error = glewInit(); if (glew_error != GLEW_OK) { DLOG(ERROR) << "Unable to initialise GLEW : " << ::glewGetErrorString(glew_error); return false; } // Check to see that we can use the OpenGL vertex attribute APIs // TODO(petersont): Return false if this check fails, but because some // Intel hardware does not support OpenGL 2.0, yet does support all of the // extensions we require, we only log an error. A future CL should change // this check to ensure that all of the extension strings we require are // present. if (!GLEW_VERSION_2_0) { DLOG(ERROR) << "GL drivers do not have OpenGL 2.0 functionality."; } bool extensions_found = true; if (!GLEW_ARB_vertex_buffer_object) { // NOTE: Linux NVidia drivers claim to support OpenGL 2.0 when using // indirect rendering (e.g. remote X), but it is actually lying. The // ARB_vertex_buffer_object functions silently no-op (!) when using // indirect rendering, leading to crashes. Fortunately, in that case, the // driver claims to not support ARB_vertex_buffer_object, so fail in that // case. DLOG(ERROR) << "GL drivers do not support vertex buffer objects."; extensions_found = false; } if (!GLEW_EXT_framebuffer_object) { DLOG(ERROR) << "GL drivers do not support framebuffer objects."; extensions_found = false; } // Check for necessary extensions if (!GLEW_VERSION_2_0 && !GLEW_EXT_stencil_two_side) { DLOG(ERROR) << "Two sided stencil extension missing."; extensions_found = false; } if (!GLEW_VERSION_1_4 && !GLEW_EXT_blend_func_separate) { DLOG(ERROR) <<"Separate blend func extension missing."; extensions_found = false; } if (!GLEW_VERSION_2_0 && !GLEW_EXT_blend_equation_separate) { DLOG(ERROR) << "Separate blend function extension missing."; extensions_found = false; } if (!extensions_found) return false; return true; } void GAPIGL::Destroy() { vertex_buffers_.DestroyAllResources(); index_buffers_.DestroyAllResources(); vertex_structs_.DestroyAllResources(); effects_.DestroyAllResources(); effect_params_.DestroyAllResources(); // textures_.DestroyAllResources(); // samplers_.DestroyAllResources(); cgDestroyContext(cg_context_); cg_context_ = NULL; #ifdef OS_LINUX DCHECK(window_); window_->Destroy(); #endif } void GAPIGL::BeginFrame() { } void GAPIGL::EndFrame() { #ifdef OS_WIN ::SwapBuffers(device_context_); #endif #ifdef OS_LINUX DCHECK(window_); window_->SwapBuffers(); #endif CHECK_GL_ERROR(); } void GAPIGL::Clear(unsigned int buffers, const RGBA &color, float depth, unsigned int stencil) { glClearColor(color.red, color.green, color.blue, color.alpha); glClearDepth(depth); glClearStencil(stencil); glClear((buffers & kColor ? GL_COLOR_BUFFER_BIT : 0) | (buffers & kDepth ? GL_DEPTH_BUFFER_BIT : 0) | (buffers & kStencil ? GL_STENCIL_BUFFER_BIT : 0)); CHECK_GL_ERROR(); } } // namespace o3d } // namespace command_buffer