//================================================================================================= void eve::ogl::Context::updateFormatVersion(void) { const GLubyte *s = glGetString(GL_VERSION); if (!(s && s[0] >= '0' && s[0] <= '9' && s[1] == '.' && s[2] >= '0' && s[2] <= '9')) { if (!s) { EVE_LOG_ERROR("OpenGL version string is null."); } else { EVE_LOG_ERROR("Unexpected OpenGL version string format."); } m_pixelFormat.setVersion(0, 0); m_pixelFormat.setProfile(pf_profNoProfile); m_pixelFormat.setOption(pf_optDeprecatedFunctions); return; } int32_t major = s[0] - '0'; int32_t minor = s[2] - '0'; m_pixelFormat.setVersion(major, minor); if (major < 3) { m_pixelFormat.setProfile(pf_profNoProfile); m_pixelFormat.setOption(pf_optDeprecatedFunctions); } else { GLint value = 0; if (major > 3 || minor >= 2) { glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value); } switch (value) { case WGL_CONTEXT_CORE_PROFILE_BIT_ARB: m_pixelFormat.setProfile(pf_profCoreProfile); break; case WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB: m_pixelFormat.setProfile(pf_profCompatibilityProfile); break; default: m_pixelFormat.setProfile(pf_profNoProfile); break; } glGetIntegerv(GL_CONTEXT_FLAGS, &value); if (value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) m_pixelFormat.setOption(pf_optNoDeprecatedFunctions); else m_pixelFormat.setOption(pf_optDeprecatedFunctions); } }
//================================================================================================= void eve::ogl::SubContext::init(void) { // Get DC from window handle. m_hDC = ::GetDC(m_hWnd); if (m_hDC == 0) { EVE_LOG_ERROR("Paint device cannot be null. GetDC() failed %s", eve::mess::get_error_msg().c_str()); EVE_ASSERT_FAILURE; } // Apply pixel format to DC. if (::SetPixelFormat(m_hDC, eve::ogl::Context::get_pixel_format_ID(), &eve::ogl::Context::get_pixel_format_descriptor()) == 0) { EVE_LOG_ERROR("Unable to link pixel format to DC, SetPixelFormat() failed %s", eve::mess::get_error_msg().c_str()); EVE_ASSERT_FAILURE; } #ifndef NDEBUG static const int contextAttribs[] = { WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB, 0 }; #else static const int contextAttribs[] = { 0 }; #endif // Create context (GLRC). m_hGLRC = wglCreateContextAttribsARB(m_hDC, eve::ogl::Context::get_handle(), contextAttribs); if (m_hGLRC == 0) { EVE_LOG_ERROR("Unable to create rendering context, wglCreateContext() failed %s", eve::mess::get_error_msg().c_str()); EVE_ASSERT_FAILURE; } // Make context current (has to be activated here to enforce DC bound). if (::wglMakeCurrent(m_hDC, m_hGLRC) == 0) { EVE_LOG_ERROR("Unable to attach context, wglMakeCurrent() failed %s", eve::mess::get_error_msg().c_str()); EVE_ASSERT_FAILURE; } // Init OpenGL extensions for this context. eve::ogl::Context::init_OpenGL(); // Release context. if (::wglMakeCurrent(0, 0) == 0) { EVE_LOG_ERROR("Unable to detach context, wglMakeCurrent(0, 0) failed %s", eve::mess::get_error_msg().c_str()); EVE_ASSERT_FAILURE; } }
//================================================================================================= void eve::ogl::check_frame_buffer(wchar_t * p_pFunction, wchar_t * p_pFile, int32_t p_line, GLuint p_framebufferId) { GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { std::wstring mess; switch (status) { case GL_FRAMEBUFFER_UNDEFINED: mess = EVE_TXT("GL_FRAMEBUFFER_UNDEFINED"); break; case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: mess = EVE_TXT("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); break; case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: mess = EVE_TXT("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); break; case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: mess = EVE_TXT("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"); break; case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: mess = EVE_TXT("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"); break; case GL_FRAMEBUFFER_UNSUPPORTED: mess = EVE_TXT("GL_FRAMEBUFFER_UNSUPPORTED"); break; case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: mess = EVE_TXT("GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"); break; #if !defined(EVE_OS_DARWIN) case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: mess = EVE_TXT("GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS"); break; #endif } EVE_LOG_ERROR("OpenGL Error: %s in function %s in file %s at line %d.", mess.c_str(), p_pFunction, p_pFile, p_line); EVE_ASSERT_FAILURE; } }
//================================================================================================= void eve::ogl::check_shader_program(wchar_t * p_pFunction, wchar_t * p_pFile, int32_t p_line, GLuint p_programId) { EVE_ASSERT(p_programId != 0); GLint link = GL_FALSE; glGetProgramiv(p_programId, GL_LINK_STATUS, &link); if (link != GL_TRUE) { int32_t logLength; glGetProgramiv(p_programId, GL_INFO_LOG_LENGTH, &logLength); size_t size = std::max(logLength, 1); char * buf = (char*)eve::mem::malloc(size *sizeof(char)); glGetProgramInfoLog(p_programId, logLength, NULL, buf); wchar_t * wbuf = (wchar_t*)eve::mem::malloc(size * sizeof(wchar_t)); mbstowcs(wbuf, buf, size); EVE_LOG_ERROR("OpenGL Error: in function %s in file %s at line %d Shader program link failed: %s.", p_pFunction, p_pFile, p_line, wbuf); eve::mem::free(wbuf); eve::mem::free(buf); EVE_ASSERT_FAILURE; } }
//============================================================================================= void eve::thr::Lock::lock(void) { #if defined(EVE_OS_WIN) ::EnterCriticalSection(&m_criticalSections); #elif defined(EVE_OS_DARWIN) if (pthread_mutex_lock(&m_mutex) != 0) { EVE_LOG_ERROR("Unable to lock mutex pthread_mutex_lock() failed."); EVE_ASSERT_FAILURE; } #endif }
//================================================================================================= void eve::thr::Lock::release(void) { #if defined(EVE_OS_WIN) ::DeleteCriticalSection(&m_criticalSections); #elif defined(EVE_OS_DARWIN) if (pthread_mutex_destroy(&m_mutex) != 0) { EVE_LOG_ERROR("Unable to release mutex pthread_mutex_destroy() failed."); EVE_ASSERT_FAILURE; } #endif }
//================================================================================================= bool eve::ogl::SubContext::makeCurrent(void) { eve::ogl::Context::get_fence()->lock(); bool ret = true; if (/*eve::ogl::Context::get_handle()*/ m_hGLRC == ::wglGetCurrentContext()) { EVE_LOG_ERROR("Context already current."); ret = false; } if (ret && (::wglMakeCurrent(m_hDC, m_hGLRC/*eve::ogl::Context::get_handle()*/) == TRUE)) { eve::ogl::SubContext::set_current_context(this); } else { EVE_LOG_ERROR("Unable to attach context, wglMakeCurrent() failed %s", eve::mess::get_error_msg().c_str()); ret = false; } return ret; }
//================================================================================================= void eve::thr::Lock::init(void) { #if defined(EVE_OS_WIN) eve::mem::memset(&m_criticalSections, 0, sizeof(CRITICAL_SECTION)); ::InitializeCriticalSection(&m_criticalSections); #elif defined(EVE_OS_DARWIN) static const pthread_mutexattr_t attr = { PTHREAD_MUTEX_RECURSIVE }; if (pthread_mutex_init(&m_mutex, &attr) != 0) { EVE_LOG_ERROR("Unable to create mutex pthread_mutex_init() failed."); EVE_ASSERT_FAILURE; } #endif }
//================================================================================================= bool eve::ogl::SubContext::doneCurrent(void) { bool ret = true; if (::wglMakeCurrent(0, 0) == TRUE) { eve::ogl::SubContext::set_current_context(nullptr); } else { EVE_LOG_ERROR("Unable to detach context, wglMakeCurrent(0, 0) failed %s", eve::mess::get_error_msg().c_str()); ret = false; } eve::ogl::Context::get_fence()->unlock(); return ret; }
//================================================================================================= void eve::ogl::Context::init_OpenGL(void) { glewInit(); static bool firstLaunch = true; if (firstLaunch) { GLenum err = glGetError(); if (err != GL_NO_ERROR) { // Log error EVE_LOG_ERROR("OpenGL extensions initialization failed (GLEW)."); // Create fatal error window and exit app eve::sys::notify_fatal_error(EVE_TXT("OpenGL initialization failed.\n \n Please check your hardware and drivers capabilities.")); } // Retrieve OpenGL version GLint majorVersion; GLint minorVersion; glGetIntegerv(GL_MAJOR_VERSION, &majorVersion); glGetIntegerv(GL_MINOR_VERSION, &minorVersion); EVE_LOG_INFO("Detected OpenGL version %d.%d.", majorVersion, minorVersion); // Test OpenGL version if (majorVersion < EVE_WINDOWS_OPENGL_MAJOR_VERSION) { // Create fatal error window and exit app eve::sys::notify_fatal_error(EVE_TXT("OpenGL initialization failed.\n \n Please check your hardware and drivers capabilities.")); } else { if (minorVersion < EVE_WINDOWS_OPENGL_MINOR_VERSION) { // Create fatal error window and exit app eve::sys::notify_fatal_error(EVE_TXT("OpenGL initialization failed.\n \n Please check your hardware and drivers capabilities.")); } } eve::ogl::init_debug_stream(); firstLaunch = false; } }
//================================================================================================= void eve::ogl::check_error(wchar_t * p_pFunction, wchar_t * p_pFile, int32_t p_line) { int32_t Error = glGetError(); if (Error != GL_NO_ERROR) { std::wstring mess; switch (Error) { case GL_INVALID_ENUM: mess = EVE_TXT("GL_INVALID_ENUM"); break; case GL_INVALID_VALUE: mess = EVE_TXT("GL_INVALID_VALUE"); break; case GL_INVALID_OPERATION: mess = EVE_TXT("GL_INVALID_OPERATION"); break; case GL_INVALID_FRAMEBUFFER_OPERATION: mess = EVE_TXT("GL_INVALID_FRAMEBUFFER_OPERATION"); break; case GL_STACK_OVERFLOW: mess = EVE_TXT("GL_STACK_OVERFLOW"); break; case GL_STACK_UNDERFLOW: mess = EVE_TXT("GL_STACK_UNDERFLOW"); break; case GL_OUT_OF_MEMORY: mess = EVE_TXT("GL_OUT_OF_MEMORY"); break; default: mess = EVE_TXT("UNKNOWN"); break; } EVE_LOG_ERROR("OpenGL Error: %s in function %s in file %s at line %d.", mess.c_str(), p_pFunction, p_pFile, p_line); EVE_ASSERT_FAILURE; } }
//================================================================================================= void eve::app::App::init(void) { // Messaging server (log). eve::mess::Server::create_instance(); // OpenGL master context. eve::ogl::Context::create_instance(); // Win32 COM #if defined(EVE_OS_WIN) HRESULT rtn = ::CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE | COINIT_SPEED_OVER_MEMORY); if (!SUCCEEDED(rtn)) { EVE_LOG_ERROR("Can't initialize Win32 COM CoInitializeEx() failed."); } #endif // View container. m_pVecViews = new std::vector<eve::sys::View*>(); // Fence. m_pFence = EVE_CREATE_PTR(eve::thr::SpinLock); // Register to application events. eve::evt::register_events_application(this); }
//================================================================================================= void eve::ogl::Context::init(void) { m_pixelFormat = eve::ogl::PixelFormat::default_format(); // Create dummy window. eve::sys::Window * win = eve::sys::Window::create_ptr(0, 0, 1, 1); // Initialize OpenGL drawing device. m_hDC = ::GetDC(win->getHandle()); if (m_hDC == 0) { EVE_LOG_ERROR("Paint device cannot be null. GetDC() failed: %s", eve::mess::get_error_msg().c_str()); EVE_ASSERT_FAILURE; } // Initialize and choose best fitting Pixel Format. m_pixelFormatId = this->choosePixelFormat(); if (m_pixelFormatId == 0) { EVE_LOG_ERROR("Unable to get pixel format for device."); EVE_ASSERT_FAILURE; } // Apply pixel format to DC. if (::SetPixelFormat(m_hDC, m_pixelFormatId, &m_pixelFormatDecriptor) == FALSE) { EVE_LOG_ERROR("Unable to link pixel format to DC, SetPixelFormat() failed %s", eve::mess::get_error_msg().c_str()); EVE_ASSERT_FAILURE; } // Create context (GLRC). m_hGLRC = ::wglCreateContext(m_hDC); if (m_hGLRC == 0) { EVE_LOG_ERROR("Unable to create rendering context, wglCreateContext() failed %s", eve::mess::get_error_msg().c_str()); EVE_ASSERT_FAILURE; } // Make context current (has to be activated here to enforce DC bound). if (::wglMakeCurrent(m_hDC, m_hGLRC) == 0) { EVE_LOG_ERROR("Unable to attach context, wglMakeCurrent() failed %s.", eve::mess::get_error_msg().c_str()); EVE_ASSERT_FAILURE; } // Init OpenGL extension for this context eve::ogl::Context::init_OpenGL(); // Stock DC auto updated format. this->updateFormatVersion(); // Release context. if (::wglMakeCurrent(0, 0) == 0) { EVE_LOG_ERROR("Unable to detach context, wglMakeCurrent(0, 0) failed %s", eve::mess::get_error_msg().c_str()); EVE_ASSERT_FAILURE; } // Release dummy window. EVE_RELEASE_PTR(win); }
//================================================================================================= int32_t eve::ogl::Context::choosePixelFormat(void) { BYTE pmDepth = 0; // Pixel format descriptor m_pixelFormatDecriptor = eve::ogl::PixelFormat::pixelFormatToPfd(&m_pixelFormat); // Choose pixel format int32_t chosenPfi = ::ChoosePixelFormat(m_hDC, &m_pixelFormatDecriptor); if (chosenPfi == 0) { EVE_LOG_ERROR("Unable to retrieve pixel format, ChoosePixelFormat() failed %s", eve::mess::get_error_msg().c_str()); EVE_ASSERT_FAILURE; } // GDI function ChoosePixelFormat() does not handle overlay and direct-rendering requests bool doSearch = (chosenPfi <= 0); PIXELFORMATDESCRIPTOR pfd; eve::ogl::PixelFormat fmt; if (!doSearch) { ::DescribePixelFormat(m_hDC, chosenPfi, sizeof(PIXELFORMATDESCRIPTOR), &pfd); fmt = eve::ogl::PixelFormat::pfdToPixelFormat(&pfd); if (m_pixelFormat.hasOverlay() && !fmt.hasOverlay()) doSearch = true; else if (!logEq(m_pixelFormat.directRendering(), fmt.directRendering())) doSearch = true; else if ((!(pfd.dwFlags & PFD_DRAW_TO_BITMAP) || pfd.cColorBits != pmDepth)) doSearch = true; else if (!(pfd.dwFlags & PFD_DRAW_TO_WINDOW)) doSearch = true; else if (!logEq(m_pixelFormat.rgba(), fmt.rgba())) doSearch = true; } if (doSearch) { int32_t pfiMax = ::DescribePixelFormat(m_hDC, 0, 0, NULL); int32_t bestScore = -1; int32_t bestPfi = -1; for (int32_t pfi = 1; pfi <= pfiMax; pfi++) { ::DescribePixelFormat(m_hDC, pfi, sizeof(PIXELFORMATDESCRIPTOR), &pfd); if (!(pfd.dwFlags & PFD_SUPPORT_OPENGL)) continue; if ((!(pfd.dwFlags & PFD_DRAW_TO_BITMAP) || pfd.cColorBits != pmDepth)) continue; if (!(pfd.dwFlags & PFD_DRAW_TO_WINDOW)) continue; fmt = eve::ogl::PixelFormat::pfdToPixelFormat(&pfd); if (m_pixelFormat.hasOverlay() && !fmt.hasOverlay()) continue; int32_t score = pfd.cColorBits; if (logEq(m_pixelFormat.depth(), fmt.depth())) score += pfd.cDepthBits; if (logEq(m_pixelFormat.alpha(), fmt.alpha())) score += pfd.cAlphaBits; if (logEq(m_pixelFormat.accum(), fmt.accum())) score += pfd.cAccumBits; if (logEq(m_pixelFormat.stencil(), fmt.stencil())) score += pfd.cStencilBits; if (logEq(m_pixelFormat.doubleBuffer(), fmt.doubleBuffer())) score += 1000; if (logEq(m_pixelFormat.stereo(), fmt.stereo())) score += 2000; if (logEq(m_pixelFormat.directRendering(), fmt.directRendering())) score += 4000; if (logEq(m_pixelFormat.rgba(), fmt.rgba())) score += 8000; if (score > bestScore) { bestScore = score; bestPfi = pfi; } } if (bestPfi > 0) { // Stock chosen pfi ID chosenPfi = bestPfi; // Stock selected pixel format m_pixelFormat = fmt; } } return chosenPfi; }
//================================================================================================= std::string eve::io::load_program(const std::string & p_filePath, const char * p_preamble, size_t * p_finalLength) { // locals FILE * pFileStream = nullptr; size_t szSourceLength; // Open source code file. #if defined(EVE_OS_WIN) if (fopen_s(&pFileStream, p_filePath.c_str(), "rb") != 0) { // File open failed, path issue? EVE_ASSERT_FAILURE; EVE_LOG_ERROR("File opening failed, path is %s", eve::str::to_wstring(p_filePath).c_str()); return NULL; } #else pFileStream = fopen(p_filePath.c_str(), "rb"); if (pFileStream == 0) { // File open failed, path issue? EVE_LOG_ERROR("File opening failed, path is %s", eve::str::to_wstring(p_filePath).c_str()); EVE_ASSERT_FAILURE; return NULL; } #endif // Get source code length. fseek(pFileStream, 0, SEEK_END); szSourceLength = ftell(pFileStream); fseek(pFileStream, 0, SEEK_SET); // Allocate string buffer. size_t szPreambleLength = 0; if (p_preamble) { szPreambleLength = strlen(p_preamble); } char * cSourceString = (char*)eve::mem::malloc(szSourceLength + szPreambleLength + 1); if (p_preamble) { eve::mem::memcpy(cSourceString, p_preamble, szPreambleLength); } if (fread((cSourceString)+szPreambleLength, szSourceLength, 1, pFileStream) != 1) { fclose(pFileStream); eve::mem::free(cSourceString); // File read failed. EVE_ASSERT_FAILURE; return NULL; } // Close the file and retrieve the total length of combined (preamble + source) string. fclose(pFileStream); if (p_finalLength) { *p_finalLength = szSourceLength + szPreambleLength; } cSourceString[szSourceLength + szPreambleLength] = '\0'; std::string ret(cSourceString); eve::mem::free(cSourceString); return ret; }