Example #1
0
        /// <summary>
        /// Check for a Win32 error.
        /// </summary>
        /// <param name="msg">A message to display with the error.</param>
        /// <param name="sureError">Whether you are sure an error occured.</param>
        public static void CheckError(string msg, bool sureError = false)
        {
            uint errorCheck = Kernel32.GetLastError();

            if (errorCheck == 0 && !sureError)
            {
                return;
            }

            // Check predefined errors.
            switch (errorCheck)
            {
            case ERROR_INVALID_VERSION_ARB:
                Engine.CriticalError(new Exception($"Driver doesn't support version of {msg}"));
                break;

            case ERROR_INVALID_PROFILE_ARB:
                Engine.CriticalError(new Exception($"Driver doesn't support profile of {msg}"));
                break;

            default:
                Engine.CriticalError(new Exception(msg, new Win32Exception((int)errorCheck)));
                break;
            }
        }
Example #2
0
        private void RegisterWindowClass()
        {
            _wndProcDelegate = WndProc;
            var wc = new WindowClassEx
            {
                Styles         = WindowClassStyles.CS_HREDRAW | WindowClassStyles.CS_VREDRAW | WindowClassStyles.CS_OWNDC,
                WindowProc     = _wndProcDelegate,
                InstanceHandle = Kernel32.GetModuleHandle(null),
                CursorHandle   = User32.LoadCursor(IntPtr.Zero, (IntPtr)SystemCursor.IDC_ARROW),
                ClassName      = CLASS_NAME
            };

            wc.Size = (uint)Marshal.SizeOf(wc);

            // Load user icon - if any.
            wc.IconHandle = User32.LoadImage(Kernel32.GetModuleHandle(null), "#32512", ResourceImageType.IMAGE_ICON, 0, 0, LoadResourceFlags.LR_DEFAULTSIZE | LoadResourceFlags.LR_SHARED);
            if (wc.IconHandle == IntPtr.Zero)
            {
                Kernel32.SetLastError(0);

                // None loaded - load default.
                wc.IconHandle = User32.LoadImage(IntPtr.Zero, (IntPtr)SystemIcon.IDI_APPLICATION, ResourceImageType.IMAGE_ICON, 0, 0, LoadResourceFlags.LR_DEFAULTSIZE | LoadResourceFlags.LR_SHARED);
            }

            ushort windowClass = User32.RegisterClassEx(ref wc);

            if (windowClass == 0)
            {
                CheckError("Win32: Failed to register window class.", true);
            }

            CheckError("Win32: Could not register class.");
        }
Example #3
0
        private void CreateHelperWindow()
        {
            _helperWindowHandle = User32.CreateWindowEx(
                WindowExStyles.WS_EX_OVERLAPPEDWINDOW,
                CLASS_NAME,
                "Emotion Helper Window",
                WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_CLIPCHILDREN,
                0, 0, 100, 100,
                IntPtr.Zero, IntPtr.Zero,
                Kernel32.GetModuleHandle(null),
                IntPtr.Zero
                );

            if (_helperWindowHandle == IntPtr.Zero)
            {
                CheckError("Win32: Failed to create helper window.", true);
            }

            // HACK: The command to the first ShowWindow call is ignored if the parent
            //       process passed along a STARTUPINFO, so clear that with a no-op call
            User32.ShowWindow(_helperWindowHandle, ShowWindowCommands.SW_HIDE);

            // Register for HID device notifications
            var dbi = new DevBroadcastDeviceInterfaceW();

            dbi.DbccSize              = (uint)Marshal.SizeOf(dbi);
            dbi.DbccDeviceType        = DeviceType.DeviceInterface;
            dbi.DbccClassGuid         = User32Guids.GuidDevInterfaceHid;
            _deviceNotificationHandle = User32.RegisterDeviceNotificationW(_helperWindowHandle, ref dbi, DeviceNotificationFlags.WindowHandle);

            CheckError("Registering for device notifications.");

            while (User32.PeekMessage(out Message msg, _helperWindowHandle, 0, 0, PeekMessageFlags.PM_REMOVE))
            {
                User32.TranslateMessage(ref msg);
                User32.DispatchMessage(ref msg);
            }

            Kernel32.SetLastError(0);

            CheckError("Creating helper window.");
        }
Example #4
0
        public void Init(IntPtr windowHandle, Win32Platform platform)
        {
            _platform = platform;

            // This is how you request a context version from MESA - lol
            // This needs to be pInvoked as Environment.SetEnvironmentVariable doesn't affect native getenv calls.
            _putenv_s("MESA_GL_VERSION_OVERRIDE", "3.3FC");

            // Unload old OpenGL, if any.
            IntPtr loadedOpenGl = Kernel32.GetModuleHandle("opengl32.dll");
            var    counter      = 0;

            if (loadedOpenGl != IntPtr.Zero)
            {
                Engine.Log.Info("OpenGL32.dll is already loaded. Attempting to unload...", MessageSource.WGallium);
            }
            while (loadedOpenGl != IntPtr.Zero)
            {
                Kernel32.FreeLibrary(loadedOpenGl);
                loadedOpenGl = Kernel32.GetModuleHandle("opengl32.dll");
                counter++;
                if (counter < 100)
                {
                    continue;
                }
                Engine.Log.Error("Couldn't unload the loaded OpenGL32.dll. Gallium context couldn't be created.", MessageSource.WGallium);
                return;
            }

            if (counter > 0)
            {
                Engine.Log.Info($"OpenGL32.dll was unloaded after {counter} attempts.", MessageSource.WGallium);
            }

            // Load library.
            _openGlLibrary = _platform.LoadLibrary("mesa");
            if (_openGlLibrary == IntPtr.Zero)
            {
                Engine.Log.Error("Couldn't load mesa.", MessageSource.WGallium);
                return;
            }

            var createContext = _platform.GetFunctionByName <WglFunctions.WglCreateContext>(_openGlLibrary, "wglCreateContext");

            _deleteContext   = _platform.GetFunctionByName <WglFunctions.WglDeleteContext>(_openGlLibrary, "wglDeleteContext");
            _getProcAddress  = _platform.GetFunctionByName <WglFunctions.WglGetProcAddress>(_openGlLibrary, "wglGetProcAddress");
            _makeCurrent     = _platform.GetFunctionByName <WglFunctions.WglMakeCurrent>(_openGlLibrary, "wglMakeCurrent");
            _swapIntervalExt = NativeHelpers.GetFunctionByPtr <WglFunctions.SwapInternalExt>(_getProcAddress("wglSwapIntervalEXT"));

            // Setup context.
            _dc = User32.GetDC(windowHandle);
            if (_dc == IntPtr.Zero)
            {
                Win32Platform.CheckError("WGL: Could not get window dc.", true);
            }

            int pixelFormatIdx = SupportedPixelFormat(_dc);

            if (pixelFormatIdx == 0)
            {
                return;
            }

            var pfd = new PixelFormatDescriptor();

            pfd.NSize = (ushort)Marshal.SizeOf(pfd);
            if (Gdi32.DescribePixelFormat(_dc, pixelFormatIdx, (uint)sizeof(PixelFormatDescriptor), ref pfd) == 0)
            {
                Win32Platform.CheckError("WGL: Failed to retrieve PFD for the selected pixel format.", true);
                return;
            }

            if (!Gdi32.SetPixelFormat(_dc, pixelFormatIdx, ref pfd))
            {
                Win32Platform.CheckError("WGL: Could not set pixel format.", true);
            }

            const string contextName = "Gallium-OpenGL";

            _contextHandle = createContext(_dc);
            if (_contextHandle == IntPtr.Zero)
            {
                Win32Platform.CheckError(contextName, true);
            }

            Win32Platform.CheckError("Checking if context creation passed.");
            Engine.Log.Trace($"Requested {contextName} using pixel format id {pixelFormatIdx}", MessageSource.Win32);

            Valid = true;
        }
        public void Init(IntPtr windowHandle, Win32Platform platform)
        {
            _platform = platform;

            // Check for RenderDoc
            _renderDocModule = Kernel32.GetModuleHandle("renderdoc.dll");
            if (_renderDocModule != IntPtr.Zero)
            {
                // Get a handle to the RenderDoc API
                IntPtr api = Kernel32.GetProcAddress(_renderDocModule, "RENDERDOC_GetAPI");
                if (api != IntPtr.Zero)
                {
                    var   getApiFunc = Marshal.GetDelegateForFunctionPointer <RenderDocGetApi>(api);
                    void *apiPointers;
                    int   ret = getApiFunc(10102, &apiPointers);
                    if (ret == 1)
                    {
                        Debug.Assert(ret == 1);
                        RenderDoc = Marshal.PtrToStructure <RenderDocAPI>((IntPtr)apiPointers);
                    }
                }
            }

            // Load WGL.
            _openGlLibrary = _platform.LoadLibrary("opengl32.dll");
            if (_openGlLibrary == IntPtr.Zero)
            {
                Engine.Log.Error("opengl32.dll not found.", MessageSource.WGallium);
                return;
            }

            var createContext = _platform.GetFunctionByName <WglFunctions.WglCreateContext>(_openGlLibrary, "wglCreateContext");

            _deleteContext     = _platform.GetFunctionByName <WglFunctions.WglDeleteContext>(_openGlLibrary, "wglDeleteContext");
            _getProcAddress    = _platform.GetFunctionByName <WglFunctions.WglGetProcAddress>(_openGlLibrary, "wglGetProcAddress");
            _getCurrentDc      = _platform.GetFunctionByName <WglFunctions.WglGetCurrentDc>(_openGlLibrary, "wglGetCurrentDC");
            _getCurrentContext = _platform.GetFunctionByName <WglFunctions.WglGetCurrentContext>(_openGlLibrary, "wglGetCurrentContext");
            _makeCurrent       = _platform.GetFunctionByName <WglFunctions.WglMakeCurrent>(_openGlLibrary, "wglMakeCurrent");

            // A dummy context has to be created for opengl32.dll to load the. OpenGL ICD, from which we can then query WGL extensions.
            // This code will accept the Microsoft GDI ICD; accelerated context, creation failure occurs during manual pixel format enumeration
            Debug.Assert(Win32Platform.HelperWindowHandle != IntPtr.Zero);
            IntPtr dc = User32.GetDC(Win32Platform.HelperWindowHandle);

            Debug.Assert(dc != IntPtr.Zero);
            var pfd = new PixelFormatDescriptor();

            pfd.NSize      = (ushort)Marshal.SizeOf(pfd);
            pfd.NVersion   = 1;
            pfd.DwFlags    = PixelFormatFlags.DrawToWindow | PixelFormatFlags.SupportOpenGl | PixelFormatFlags.DoubleBuffer;
            pfd.PixelType  = (byte)PixelFormatFlags.RGBA;
            pfd.CColorBits = 24;

            if (!Gdi32.SetPixelFormat(dc, Gdi32.ChoosePixelFormat(dc, ref pfd), ref pfd))
            {
                Win32Platform.CheckError("WGL: Could not set pixel format on dummy context.", true);
            }

            // Establish dummy context.
            IntPtr rc = createContext(dc);

            if (rc == IntPtr.Zero)
            {
                Win32Platform.CheckError("WGL: Could not create dummy context.", true);
            }
            if (!_makeCurrent(dc, rc))
            {
                _deleteContext(rc);
                Win32Platform.CheckError("Could not make dummy context current.", true);
                return;
            }

            // Check supported version.
            KhronosVersion glGetString = Gl.QueryVersionExternal(GetProcAddress);

            if (glGetString != null)
            {
                if (glGetString.Major < 3)
                {
                    _deleteContext(rc);
                    Engine.Log.Error("Wgl support is lower than 3.0", MessageSource.Wgl);
                    return;
                }
            }
            else
            {
                Engine.Log.Warning("Couldn't verify context version.", MessageSource.Wgl);
            }

            Engine.Log.Trace("Loaded functions.", MessageSource.Wgl);

            // Functions must be loaded first as they're needed to retrieve the extension string that tells us whether the functions are supported
            _getExtensionsStringExt    = NativeHelpers.GetFunctionByPtr <WglFunctions.GetExtensionsStringExt>(_getProcAddress("wglGetExtensionsStringEXT"));
            _getExtensionsStringArb    = NativeHelpers.GetFunctionByPtr <WglFunctions.GetExtensionsStringArb>(_getProcAddress("wglGetExtensionsStringARB"));
            _createContextAttribs      = NativeHelpers.GetFunctionByPtr <WglFunctions.CreateContextAttribs>(_getProcAddress("wglCreateContextAttribsARB"));
            _swapIntervalExt           = NativeHelpers.GetFunctionByPtr <WglFunctions.SwapInternalExt>(_getProcAddress("wglSwapIntervalEXT"));
            _getPixelFormatAttribivArb = NativeHelpers.GetFunctionByPtr <WglFunctions.GetPixelFormatAttributes>(_getProcAddress("wglGetPixelFormatAttribivARB"));

            WglGetSupportedExtensions();
            _arbMultisample          = WglSupportedExtension("WGL_ARB_multisample");
            _arbFramebufferSRgb      = WglSupportedExtension("WGL_ARB_framebuffer_sRGB");
            _extFramebufferSRgb      = WglSupportedExtension("WGL_EXT_framebuffer_sRGB");
            _arbCreateContextProfile = WglSupportedExtension("WGL_ARB_create_context_profile");
            _arbPixelFormat          = WglSupportedExtension("WGL_ARB_pixel_format");
            bool arbCreateContext = WglSupportedExtension("WGL_ARB_create_context");

            // Dispose of dummy context.
            _deleteContext(rc);
            Engine.Log.Trace("Extensions loaded.", MessageSource.Wgl);

            // Start creating actual context.
            _dc = User32.GetDC(windowHandle);
            if (_dc == IntPtr.Zero)
            {
                Win32Platform.CheckError("WGL: Could not get window dc.", true);
            }
            int pixelFormatIdx = SupportedPixelFormat(_dc);

            if (pixelFormatIdx == 0)
            {
                return;
            }

            if (Gdi32.DescribePixelFormat(_dc, pixelFormatIdx, (uint)sizeof(PixelFormatDescriptor), ref pfd) == 0)
            {
                Win32Platform.CheckError("WGL: Failed to retrieve PFD for the selected pixel format.", true);
                return;
            }

            if (!Gdi32.SetPixelFormat(_dc, pixelFormatIdx, ref pfd))
            {
                Win32Platform.CheckError("WGL: Could not set pixel format.", true);
                return;
            }

            Engine.Log.Trace($"Context ARB: {arbCreateContext}, Profile ARB: {_arbCreateContextProfile}", MessageSource.Wgl);
            if (arbCreateContext)
            {
                // Context configurations to try.
                var contextFactory = new List <GLContextDescription>(2)
                {
                    new GLContextDescription {
                        Profile = GLProfile.Core, Debug = Engine.Configuration.GlDebugMode
                    },
                    new GLContextDescription {
                        Profile = GLProfile.Any
                    }
                };

                for (var i = 0; i < contextFactory.Count; i++)
                {
                    GLContextDescription current = contextFactory[i];

                    IntPtr handle = CreateContextArb(contextFactory[i]);
                    if (handle == IntPtr.Zero)
                    {
                        continue;
                    }

                    _contextHandle = handle;
                    Engine.Log.Info($"Created WGL context - {current}", MessageSource.Wgl);
                    break;
                }

                // If that failed too, look for errors.
                // Fallback to legacy creation.
                if (_contextHandle == IntPtr.Zero)
                {
                    Win32Platform.CheckError("Creating WGL context");
                }
            }

            if (_contextHandle == IntPtr.Zero)
            {
                _contextHandle = createContext(_dc);
                if (_contextHandle == IntPtr.Zero)
                {
                    Win32Platform.CheckError("Creating WGL legacy context", true);
                }
                Engine.Log.Info("WGL legacy context created.", MessageSource.Wgl);
            }

            Win32Platform.CheckError("Checking if context creation passed.");
            Valid = true;
        }
Example #6
0
        /// <inheritdoc />
        protected override void SetupInternal(Configurator config)
        {
            IsWindows7OrGreater = IsWindowsVersionOrGreaterWin32(
                NativeHelpers.HiByte((ushort)NtDll.WinVer.Win32WinNTWin7),
                NativeHelpers.LoByte((ushort)NtDll.WinVer.Win32WinNTWin7),
                0);
            IsWindows8OrGreater = IsWindowsVersionOrGreaterWin32(
                NativeHelpers.HiByte((ushort)NtDll.WinVer.Win32WinNTWin8),
                NativeHelpers.LoByte((ushort)NtDll.WinVer.Win32WinNTWin8),
                0);
            IsWindows81OrGreater = IsWindowsVersionOrGreaterWin32(
                NativeHelpers.HiByte((ushort)NtDll.WinVer.Win32WinNTWinBlue),
                NativeHelpers.LoByte((ushort)NtDll.WinVer.Win32WinNTWinBlue),
                0);
            IsWindows10AnniversaryUpdateOrGreaterWin32 = IsWindows10BuildOrGreaterWin32(14393);
            IsWindows10CreatorsUpdateOrGreaterWin32    = IsWindows10BuildOrGreaterWin32(15063);

            var windowsVersionFlags = new List <string>();

            if (IsWindows7OrGreater)
            {
                windowsVersionFlags.Add(nameof(IsWindows7OrGreater));
            }
            if (IsWindows8OrGreater)
            {
                windowsVersionFlags.Add(nameof(IsWindows8OrGreater));
            }
            if (IsWindows81OrGreater)
            {
                windowsVersionFlags.Add(nameof(IsWindows81OrGreater));
            }
            if (IsWindows10AnniversaryUpdateOrGreaterWin32)
            {
                windowsVersionFlags.Add(nameof(IsWindows10AnniversaryUpdateOrGreaterWin32));
            }
            if (IsWindows10CreatorsUpdateOrGreaterWin32)
            {
                windowsVersionFlags.Add(nameof(IsWindows10CreatorsUpdateOrGreaterWin32));
            }
            Engine.Log.Trace(string.Join(", ", windowsVersionFlags), MessageSource.Win32);

            if (IsWindows10CreatorsUpdateOrGreaterWin32)
            {
                User32.SetProcessDpiAwarenessContext(DpiAwarenessContext.DpiAwarenessContextPerMonitorAwareV2);
            }
            else if (IsWindows81OrGreater)
            {
                User32.SetProcessDpiAwareness(ProcessDpiAwareness.ProcessPerMonitorDpiAware);
            }
            else
            {
                User32.SetProcessDPIAware();
            }

            // todo: load libraries - if any, probably XInput?

            // Initialize audio.
            AudioContext ctx = null;

#if OpenAL
            ctx ??= OpenALAudioAdapter.TryCreate(this);
#endif
            ctx ??= WasApiAudioContext.TryCreate(this);
            ctx ??= new NullAudioContext();
            Audio = ctx;
            Engine.Log.Trace("Audio init complete.", MessageSource.Win32);

            PopulateKeyCodes();
            RegisterWindowClass();
            CreateHelperWindow();
            PollMonitors();
            Engine.Log.Trace("Platform helpers created.", MessageSource.Win32);

            var windowInitialSize = new Rect
            {
                Right  = (int)config.HostSize.X,
                Bottom = (int)config.HostSize.Y
            };
            GetFullWindowRect(DEFAULT_WINDOW_STYLE, DEFAULT_WINDOW_STYLE_EX, DEFAULT_DPI, ref windowInitialSize);
            int initialWidth  = windowInitialSize.Right - windowInitialSize.Left;
            int initialHeight = windowInitialSize.Bottom - windowInitialSize.Top;

            WindowStyles windowStyle = DEFAULT_WINDOW_STYLE;
            if (config.HiddenWindow)
            {
                windowStyle &= ~WindowStyles.WS_VISIBLE;
                windowStyle &= ~WindowStyles.WS_MINIMIZE;

                // This will override the hide otherwise
                config.InitialDisplayMode = DisplayMode.Initial;
            }

            IntPtr windowHandle = User32.CreateWindowEx(
                DEFAULT_WINDOW_STYLE_EX,
                CLASS_NAME,
                config.HostTitle,
                windowStyle,
                (int)CreateWindowFlags.CW_USEDEFAULT, (int)CreateWindowFlags.CW_USEDEFAULT, // Position - default
                initialWidth, initialHeight,                                                // Size - initial
                IntPtr.Zero,                                                                // No parent window
                IntPtr.Zero,                                                                // No window menu
                Kernel32.GetModuleHandle(null),
                IntPtr.Zero
                );
            if (windowHandle == IntPtr.Zero)
            {
                CheckError("Couldn't create window.", true);
                return;
            }

            Engine.Log.Trace("Window created.", MessageSource.Win32);

            string[] args       = Environment.GetCommandLineArgs();
            bool     forceAngle = CommandLineParser.FindArgument(args, "angle", out string _);
            bool     forceMesa  = CommandLineParser.FindArgument(args, "software", out string _);

            // Create graphics context.

            if (!forceAngle && !forceMesa)
            {
                try
                {
                    var wgl = new WglGraphicsContext();
                    wgl.Init(User32.GetDC(_helperWindowHandle), windowHandle, this);
                    if (wgl.Valid)
                    {
                        Context = wgl;
                    }
                }
                catch (Exception ex)
                {
                    Engine.Log.Warning($"Couldn't create WGL context, falling back if possible.\n{ex}", MessageSource.Win32);
                }
            }

#if ANGLE
            if (Context == null && !forceMesa)
            {
                try
                {
                    var egl = new EglGraphicsContext();
                    egl.Init(User32.GetDC(windowHandle), windowHandle, this);
                    if (egl.Valid)
                    {
                        Context = egl;
                    }
                }
                catch (Exception ex)
                {
                    Engine.Log.Warning($"Couldn't create EGL context, falling back if possible.\n{ex}", MessageSource.Win32);
                }
            }
#endif

            if (Context == null)
            {
                try
                {
                    var gallium = new GalliumGraphicsContext();
                    gallium.Init(windowHandle, this);
                    if (gallium.Valid)
                    {
                        Context = gallium;
                    }
                }
                catch (Exception ex)
                {
                    Engine.CriticalError(new Exception("Couldn't create MESA context.", ex));
                }
            }

            if (Context == null)
            {
                Engine.CriticalError(new Exception("Couldn't create graphics context!"));
                return;
            }

            // Adjust window size to account for DPI scaling of the window frame and optionally DPI scaling of the content area.
            // This cannot be done until we know what monitor it was placed on - so it's done post creation.
            var rect = new Rect
            {
                Right  = (int)config.HostSize.X,
                Bottom = (int)config.HostSize.Y
            };
            rect.ClientToScreen(windowHandle);
            GetFullWindowRect(ref rect);
            User32.SetWindowPos(_windowHandle, IntPtr.Zero,
                                rect.Left, rect.Top,
                                rect.Right - rect.Left, rect.Bottom - rect.Top,
                                WindowPositionFlags.SWP_NOACTIVATE | WindowPositionFlags.SWP_NOZORDER);

            _windowHandle = windowHandle;
        }