public void Run() { FLLog.Info("Engine", "Version: " + Platform.GetInformationalVersion <Game>()); //TODO: This makes i5-7200U on mesa 18 faster, but this should probably be a configurable option Environment.SetEnvironmentVariable("mesa_glthread", "true"); SSEMath.Load(); if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO) != 0) { FLLog.Error("SDL", "SDL_Init failed, exiting."); return; } SDL.SDL_SetHint(SDL.SDL_HINT_IME_INTERNAL_EDITING, "1"); SDL.SDL_SetHint(SDL.SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); //Set GL states SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, (int)SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE); SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_DEPTH_SIZE, 24); //Create Window var flags = SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE; if (fullscreen) { flags |= SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP; } var sdlWin = SDL.SDL_CreateWindow( "LibreLancer", SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED, width, height, flags ); //Cursors curArrow = SDL.SDL_CreateSystemCursor(SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_ARROW); curMove = SDL.SDL_CreateSystemCursor(SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_CROSSHAIR); curTextInput = SDL.SDL_CreateSystemCursor(SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_IBEAM); curResizeNS = SDL.SDL_CreateSystemCursor(SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENS); curResizeEW = SDL.SDL_CreateSystemCursor(SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZEWE); curResizeNESW = SDL.SDL_CreateSystemCursor(SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENESW); curResizeNWSE = SDL.SDL_CreateSystemCursor(SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENWSE); //Window sizing if (minWindowSize != Point.Zero) { SDL.SDL_SetWindowMinimumSize(sdlWin, minWindowSize.X, minWindowSize.Y); } if (sdlWin == IntPtr.Zero) { FLLog.Error("SDL", "Failed to create window, exiting."); return; } SDL.SDL_EventState(SDL.SDL_EventType.SDL_DROPFILE, SDL.SDL_ENABLE); windowptr = sdlWin; var glcontext = SDL.SDL_GL_CreateContext(sdlWin); if (glcontext == IntPtr.Zero || !GL.CheckStringSDL()) { SDL.SDL_GL_DeleteContext(glcontext); if (Platform.RunningOS == OS.Windows) { SDL.SDL_ShowSimpleMessageBox(SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR, "Librelancer", "Failed to create OpenGL context, exiting.", IntPtr.Zero); } FLLog.Error("OpenGL", "Failed to create OpenGL context, exiting."); return; } else { GL.LoadSDL(); Renderer = string.Format("{0} ({1})", GL.GetString(GL.GL_VERSION), GL.GetString(GL.GL_RENDERER)); } SetVSync(true); //Init game state RenderState = new RenderState(); Load(); //Start game running = true; timer = new Stopwatch(); timer.Start(); double last = 0; double elapsed = 0; SDL.SDL_Event e; SDL.SDL_StopTextInput(); while (running) { //Pump message queue while (SDL.SDL_PollEvent(out e) != 0) { switch (e.type) { case SDL.SDL_EventType.SDL_QUIT: { if (WillClose != null) { WillClose(); } running = false; //TODO: Raise Event break; } case SDL.SDL_EventType.SDL_MOUSEMOTION: { Mouse.X = e.motion.x; Mouse.Y = e.motion.y; Mouse.OnMouseMove(); break; } case SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN: { Mouse.X = e.button.x; Mouse.Y = e.button.y; var btn = GetMouseButton(e.button.button); Mouse.Buttons |= btn; Mouse.OnMouseDown(btn); break; } case SDL.SDL_EventType.SDL_MOUSEBUTTONUP: { Mouse.X = e.button.x; Mouse.Y = e.button.y; var btn = GetMouseButton(e.button.button); Mouse.Buttons &= ~btn; Mouse.OnMouseUp(btn); break; } case SDL.SDL_EventType.SDL_MOUSEWHEEL: { Mouse.OnMouseWheel(e.wheel.y); break; } case SDL.SDL_EventType.SDL_TEXTINPUT: { Keyboard.OnTextInput(GetEventText(ref e)); break; } case SDL.SDL_EventType.SDL_KEYDOWN: { Keyboard.OnKeyDown((Keys)e.key.keysym.scancode, (KeyModifiers)e.key.keysym.mod, e.key.repeat != 0); break; } case SDL.SDL_EventType.SDL_KEYUP: { Keyboard.OnKeyUp((Keys)e.key.keysym.scancode, (KeyModifiers)e.key.keysym.mod); break; } case SDL.SDL_EventType.SDL_KEYMAPCHANGED: KeysExtensions.ResetKeyNames(); break; case SDL.SDL_EventType.SDL_WINDOWEVENT: if (e.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_RESIZED) { SDL.SDL_GetWindowSize(windowptr, out width, out height); OnResize(); } break; case SDL.SDL_EventType.SDL_DROPFILE: { var file = UnsafeHelpers.PtrToStringUTF8(e.drop.file); OnDrop(file); SDL.SDL_free(e.drop.file); break; } } } //Do game things if (!running) { break; } Action work; while (actions.TryDequeue(out work)) { work(); } totalTime = timer.Elapsed.TotalSeconds; Update(elapsed); if (!running) { break; } Draw(elapsed); //Frame time before, FPS after var tk = timer.Elapsed.TotalSeconds - totalTime; frameTime = CalcAverageTime(tk); if (_screenshot) { TakeScreenshot(); _screenshot = false; } SDL.SDL_GL_SwapWindow(sdlWin); if (GL.FrameHadErrors()) //If there was a GL error, track it down. { GL.ErrorChecking = true; } elapsed = timer.Elapsed.TotalSeconds - last; renderFrequency = (1.0 / CalcAverageTick(elapsed)); last = timer.Elapsed.TotalSeconds; totalTime = timer.Elapsed.TotalSeconds; if (elapsed < 0) { elapsed = 0; FLLog.Warning("Timing", "Stopwatch returned negative time!"); } } Cleanup(); SDL.SDL_Quit(); }
public void Run() { //Try to set DPI Awareness on Win32 if (Platform.RunningOS == OS.Windows) { try { SetProcessDPIAware(); } catch { } } FLLog.Info("Engine", "Version: " + Platform.GetInformationalVersion <Game>()); //TODO: This makes i5-7200U on mesa 18 faster, but this should probably be a configurable option bool setMesaThread = string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("mesa_glthread")); if (setMesaThread) { Environment.SetEnvironmentVariable("mesa_glthread", "true"); } if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO) != 0) { FLLog.Error("SDL", "SDL_Init failed, exiting."); return; } SDL.SDL_SetHint(SDL.SDL_HINT_IME_INTERNAL_EDITING, "1"); SDL.SDL_SetHint(SDL.SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); //Set GL states SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_DEPTH_SIZE, 24); //Create Window var flags = SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE | SDL.SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI; if (fullscreen) { flags |= SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP; } var sdlWin = SDL.SDL_CreateWindow( "LibreLancer", SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED, width, height, flags ); //Cursors curArrow = SDL.SDL_CreateSystemCursor(SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_ARROW); curMove = SDL.SDL_CreateSystemCursor(SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_CROSSHAIR); curTextInput = SDL.SDL_CreateSystemCursor(SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_IBEAM); curResizeNS = SDL.SDL_CreateSystemCursor(SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENS); curResizeEW = SDL.SDL_CreateSystemCursor(SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZEWE); curResizeNESW = SDL.SDL_CreateSystemCursor(SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENESW); curResizeNWSE = SDL.SDL_CreateSystemCursor(SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENWSE); //Window sizing if (minWindowSize != Point.Zero) { SDL.SDL_SetWindowMinimumSize(sdlWin, minWindowSize.X, minWindowSize.Y); } if (sdlWin == IntPtr.Zero) { Dialogs.CrashWindow.Run("Librelancer", "Failed to create SDL window", "SDL Error: " + (SDL.SDL_GetError() ?? "")); return; } SDL.SDL_EventState(SDL.SDL_EventType.SDL_DROPFILE, SDL.SDL_ENABLE); windowptr = sdlWin; IntPtr glcontext = IntPtr.Zero; if (Environment.GetEnvironmentVariable("LIBRELANCER_RENDERER") == "GLES" || !CreateContextCore(sdlWin, out glcontext)) { if (!CreateContextES(sdlWin, out glcontext)) { Dialogs.CrashWindow.Run("Librelancer", "Failed to create OpenGL context", "Your driver or gpu does not support at least OpenGL 3.2 or OpenGL ES 3.1\n" + SDL.SDL_GetError() ?? ""); return; } } GL.LoadSDL(); Renderer = string.Format("{0} ({1})", GL.GetString(GL.GL_VERSION), GL.GetString(GL.GL_RENDERER)); FLLog.Info("GL", $"Renderer: {GL.GetString(GL.GL_RENDERER)}"); SetVSync(true); //Init game state RenderContext = new RenderContext(); SDL.SDL_GetWindowSize(sdlWin, out int windowWidth, out int windowHeight); SDL.SDL_GL_GetDrawableSize(sdlWin, out width, out height); var scaleW = (float)width / windowWidth; var scaleH = (float)height / windowHeight; if (Platform.RunningOS != OS.Windows) { DpiScale = scaleH; } else { if (SDL.SDL_GetDisplayDPI(0, out float ddpi, out _, out _) == 0) { DpiScale = ddpi / 96.0f; } } FLLog.Info("GL", $"Dpi Scale: {DpiScale:F4}"); Load(); //kill the value we set so it doesn't crash child processes if (setMesaThread) { Environment.SetEnvironmentVariable("mesa_glthread", null); } //Start game running = true; timer = new Stopwatch(); timer.Start(); double last = 0; double elapsed = 0; SDL.SDL_Event e = new SDL.SDL_Event(); SDL.SDL_StopTextInput(); MouseButtons doRelease = 0; while (running) { //Window State var winFlags = (SDL.SDL_WindowFlags)SDL.SDL_GetWindowFlags(sdlWin); Focused = (winFlags & SDL.SDL_WindowFlags.SDL_WINDOW_MOUSE_FOCUS) == SDL.SDL_WindowFlags.SDL_WINDOW_MOUSE_FOCUS || (winFlags & SDL.SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS) == SDL.SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS; EventsThisFrame = false; //Get Size SDL.SDL_GetWindowSize(sdlWin, out windowWidth, out windowHeight); SDL.SDL_GL_GetDrawableSize(sdlWin, out width, out height); scaleW = (float)width / windowWidth; scaleH = (float)height / windowHeight; if (Platform.RunningOS != OS.Windows) { DpiScale = scaleH; } //This allows for press/release in same frame to have //button down for one frame, e.g. trackpoint middle click on Linux/libinput. MouseButtons pressedThisFrame = 0; Mouse.Buttons &= ~doRelease; doRelease = 0; bool eventWaited = false; if (waitForEvent) { waitForEvent = false; if (SDL.SDL_WaitEventTimeout(out e, 2000) != 0) { eventWaited = true; } } Mouse.Wheel = 0; //Pump message queue while (eventWaited || SDL.SDL_PollEvent(out e) != 0) { eventWaited = false; EventsThisFrame = true; switch (e.type) { case SDL.SDL_EventType.SDL_QUIT: { if (WillClose != null) { WillClose(); } running = false; //TODO: Raise Event break; } case SDL.SDL_EventType.SDL_MOUSEMOTION: { Mouse.X = (int)(scaleW * e.motion.x); Mouse.Y = (int)(scaleH * e.motion.y); Mouse.OnMouseMove(); break; } case SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN: { Mouse.X = (int)(scaleW * e.button.x); Mouse.Y = (int)(scaleH * e.button.y); var btn = GetMouseButton(e.button.button); Mouse.Buttons |= btn; pressedThisFrame |= btn; Mouse.OnMouseDown(btn); break; } case SDL.SDL_EventType.SDL_MOUSEBUTTONUP: { Mouse.X = (int)(scaleW * e.button.x); Mouse.Y = (int)(scaleH * e.button.y); var btn = GetMouseButton(e.button.button); if ((pressedThisFrame & btn) == btn) { doRelease |= btn; } else { Mouse.Buttons &= ~btn; } Mouse.OnMouseUp(btn); break; } case SDL.SDL_EventType.SDL_MOUSEWHEEL: { Mouse.Wheel += e.wheel.y; break; } case SDL.SDL_EventType.SDL_TEXTINPUT: { Keyboard.OnTextInput(GetEventText(ref e)); break; } case SDL.SDL_EventType.SDL_KEYDOWN: { Keyboard.OnKeyDown((Keys)e.key.keysym.scancode, (KeyModifiers)e.key.keysym.mod, e.key.repeat != 0); break; } case SDL.SDL_EventType.SDL_KEYUP: { Keyboard.OnKeyUp((Keys)e.key.keysym.scancode, (KeyModifiers)e.key.keysym.mod); break; } case SDL.SDL_EventType.SDL_KEYMAPCHANGED: KeysExtensions.ResetKeyNames(); break; case SDL.SDL_EventType.SDL_WINDOWEVENT: if (e.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_RESIZED) { OnResize(); } break; case SDL.SDL_EventType.SDL_DROPFILE: { var file = UnsafeHelpers.PtrToStringUTF8(e.drop.file); OnDrop(file); SDL.SDL_free(e.drop.file); break; } } } Mouse.Wheel /= 2.5f; //Do game things if (!running) { break; } Action work; while (actions.TryDequeue(out work)) { work(); } totalTime = timer.Elapsed.TotalSeconds; Update(elapsed); if (!running) { break; } Draw(elapsed); RenderContext.EndFrame(); //Frame time before, FPS after var tk = timer.Elapsed.TotalSeconds - totalTime; frameTime = CalcAverageTime(tk); if (_screenshot) { TakeScreenshot(); _screenshot = false; } SDL.SDL_GL_SwapWindow(sdlWin); if (GL.FrameHadErrors()) //If there was a GL error, track it down. { GL.ErrorChecking = true; } elapsed = timer.Elapsed.TotalSeconds - last; renderFrequency = (1.0 / CalcAverageTick(elapsed)); last = timer.Elapsed.TotalSeconds; totalTime = timer.Elapsed.TotalSeconds; if (elapsed < 0) { elapsed = 0; FLLog.Warning("Timing", "Stopwatch returned negative time!"); } } Cleanup(); SDL.SDL_Quit(); }