#include "MFCGraphicsOperations.h" MFCViewport::MFCViewport(HWND hwnd, HDC dc): _windowHandle(hwnd), _device_context(dc) { Assertion(hwnd != nullptr, "Invalid window handle!"); Assertion(dc != nullptr, "Invalid device context handle!"); if ( !SDL_InitSubSystem(SDL_INIT_VIDEO) ) { Error(LOCATION, "Couldn't init SDL video: %s", SDL_GetError()); return; } } MFCViewport::~MFCViewport() { ReleaseDC(_windowHandle, _device_context); _windowHandle = nullptr; _device_context = nullptr; SDL_QuitSubSystem(SDL_INIT_VIDEO); } SDL_Window* MFCViewport::toSDLWindow() { return nullptr; } std::pair MFCViewport::getSize() { RECT size; if (!GetWindowRect(_windowHandle, &size)) { return std::make_pair(0, 0); } return std::make_pair(size.right - size.left, size.bottom - size.top); } void MFCViewport::swapBuffers() { SwapBuffers(_device_context); } void MFCViewport::setState(os::ViewportState state) { // Not implemented } void MFCViewport::minimize() { // Not implemented } void MFCViewport::restore() { // Not implemented } HDC MFCViewport::getHDC() { return _device_context; } SDL_SharedObject* MFCOpenGLContext::_oglDllHandle = nullptr; size_t MFCOpenGLContext::_oglDllReferenceCount = 0; MFCOpenGLContext::MFCOpenGLContext(HGLRC hglrc): _render_context(hglrc) { if (_oglDllHandle == nullptr) { _oglDllHandle = SDL_LoadObject("OPENGL32.DLL"); } ++_oglDllReferenceCount; } MFCOpenGLContext::~MFCOpenGLContext() { if (!wglDeleteContext(_render_context)) { mprintf(("Failed to delete render context!\n")); } --_oglDllReferenceCount; if (_oglDllReferenceCount == 0) { SDL_UnloadObject(_oglDllHandle); _oglDllHandle = nullptr; } } void* MFCOpenGLContext::wglLoader(const char* name) { auto wglAddr = reinterpret_cast(wglGetProcAddress(name)); if (wglAddr == nullptr && _oglDllHandle != nullptr) { wglAddr = SDL_LoadFunction(_oglDllHandle, name); } return wglAddr; } os::OpenGLLoadProc MFCOpenGLContext::getLoaderFunction() { return wglLoader; } bool MFCOpenGLContext::setSwapInterval(int status) { if (GLAD_WGL_EXT_swap_control) { wglSwapIntervalEXT(status); } return true; } HGLRC MFCOpenGLContext::getHandle() { return _render_context; } MFCGraphicsOperations::MFCGraphicsOperations(HWND hwnd): _windowHandle(hwnd) { } MFCGraphicsOperations::~MFCGraphicsOperations() { } std::unique_ptr MFCGraphicsOperations::createViewport(const os::ViewPortProperties& props) { int PixelFormat; PIXELFORMATDESCRIPTOR pfd_test; PIXELFORMATDESCRIPTOR GL_pfd; mprintf((" Initializing WGL...\n")); memset(&GL_pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); memset(&pfd_test, 0, sizeof(PIXELFORMATDESCRIPTOR)); GL_pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); GL_pfd.nVersion = 1; GL_pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; if (props.enable_opengl) { GL_pfd.dwFlags |= PFD_SUPPORT_OPENGL; } GL_pfd.iPixelType = PFD_TYPE_RGBA; GL_pfd.cColorBits = (BYTE)(props.pixel_format.red_size + props.pixel_format.green_size + props.pixel_format.blue_size + props.pixel_format.alpha_size); GL_pfd.cRedBits = (BYTE)(props.pixel_format.red_size); GL_pfd.cGreenBits = (BYTE)(props.pixel_format.green_size); GL_pfd.cBlueBits = (BYTE)(props.pixel_format.blue_size); GL_pfd.cAlphaBits = (BYTE)(props.pixel_format.alpha_size); GL_pfd.cDepthBits = (BYTE)(props.pixel_format.depth_size); GL_pfd.cStencilBits = (BYTE)(props.pixel_format.stencil_size); Assert(_windowHandle != NULL); auto device_context = GetDC(_windowHandle); if (!device_context) { mprintf(("Unable to get device context for OpenGL W32!\n")); return nullptr; } PixelFormat = ChoosePixelFormat(device_context, &GL_pfd); if (!PixelFormat) { mprintf(("Unable to choose pixel format for OpenGL W32!\n")); ReleaseDC(_windowHandle, device_context); return nullptr; } else { DescribePixelFormat(device_context, PixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd_test); } if (!SetPixelFormat(device_context, PixelFormat, &GL_pfd)) { mprintf(("Unable to set pixel format for OpenGL W32!\n")); ReleaseDC(_windowHandle, device_context); return nullptr; } mprintf((" Requested SDL Video values = R: %d, G: %d, B: %d, depth: %d, stencil: %d\n", props.pixel_format.red_size, props.pixel_format.green_size, props.pixel_format.blue_size, props.pixel_format.depth_size, props.pixel_format.stencil_size)); return std::unique_ptr(new MFCViewport(_windowHandle, device_context)); } static void* gladWGLLoader(const char* name) { return wglGetProcAddress(name); } std::unique_ptr MFCGraphicsOperations::createOpenGLContext(os::Viewport* port, const os::OpenGLContextAttributes& ctx) { auto mfcView = reinterpret_cast(port); auto temp_ctx = wglCreateContext(mfcView->getHDC()); if (!temp_ctx) { mprintf(("Unable to create fake context for OpenGL W32!\n")); return nullptr; } if (!wglMakeCurrent(mfcView->getHDC(), temp_ctx)) { mprintf(("Unable to make current thread for OpenGL W32!\n")); if (!wglDeleteContext(temp_ctx)) { mprintf(("Failed to delete render context!\n")); } return nullptr; } if (!gladLoadWGLLoader(gladWGLLoader, mfcView->getHDC())) { mprintf(("Failed to load WGL functions!\n")); wglMakeCurrent(nullptr, nullptr); if (!wglDeleteContext(temp_ctx)) { mprintf(("Failed to delete render context!\n")); } return nullptr; } if (!GLAD_WGL_ARB_create_context) { // No support for 3.x if (ctx.major_version >= 3) { mprintf(("Can't create requested OpenGL context!\n")); wglMakeCurrent(nullptr, nullptr); if (!wglDeleteContext(temp_ctx)) { mprintf(("Failed to delete fake context!\n")); } return nullptr; } // Major version is low enough -> just use the fake context return std::unique_ptr(new MFCOpenGLContext(temp_ctx)); } SCP_vector attrib_list; attrib_list.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB); attrib_list.push_back(ctx.major_version); attrib_list.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); attrib_list.push_back(ctx.minor_version); if (GLAD_WGL_ARB_create_context_profile) { attrib_list.push_back(WGL_CONTEXT_PROFILE_MASK_ARB); attrib_list.push_back(ctx.profile == os::OpenGLProfile::Core ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB); } int flags = 0; if (ctx.flags[os::OpenGLContextFlags::Debug]) { flags |= WGL_CONTEXT_DEBUG_BIT_ARB; } if (ctx.flags[os::OpenGLContextFlags::ForwardCompatible]) { flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; } if (flags != 0) { attrib_list.push_back(WGL_CONTEXT_FLAGS_ARB); attrib_list.push_back(flags); } attrib_list.push_back(0); auto attrib_ctx = wglCreateContextAttribsARB(mfcView->getHDC(), nullptr, attrib_list.data()); // We don't need the fake context anymore wglMakeCurrent(nullptr, nullptr); if (!wglDeleteContext(temp_ctx)) { mprintf(("Failed to delete fake context!\n")); } if (attrib_ctx == nullptr) { mprintf(("Failed to create OpenGL context!\n")); return nullptr; } if (!wglMakeCurrent(mfcView->getHDC(), attrib_ctx)) { mprintf(("Unable to make current thread for OpenGL W32!\n")); if (!wglDeleteContext(attrib_ctx)) { mprintf(("Failed to delete render context!\n")); } return nullptr; } if (!gladLoadWGLLoader(gladWGLLoader, mfcView->getHDC())) { mprintf(("Failed to load WGL functions!\n")); wglMakeCurrent(nullptr, nullptr); if (!wglDeleteContext(temp_ctx)) { mprintf(("Failed to delete render context!\n")); } return nullptr; } return std::unique_ptr(new MFCOpenGLContext(attrib_ctx)); } void MFCGraphicsOperations::makeOpenGLContextCurrent(os::Viewport* view, os::OpenGLContext* ctx) { if (view == nullptr && ctx == nullptr) { if (!wglMakeCurrent(nullptr, nullptr)) { mprintf(("Failed to make OpenGL context current!\n")); } return; } Assertion(view != nullptr, "Both viewport of context must be valid at this point!"); Assertion(ctx != nullptr, "Both viewport of context must be valid at this point!"); auto mfcCtx = reinterpret_cast(ctx); auto mfcView = reinterpret_cast(view); if (!wglMakeCurrent(mfcView->getHDC(), mfcCtx->getHandle())) { mprintf(("Failed to make OpenGL context current!\n")); } }