OpenGLWindow.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. using OpenTK;
  2. using OpenTK.Graphics.OpenGL;
  3. using Ryujinx.Common.Configuration;
  4. using Ryujinx.Common.Logging;
  5. using Ryujinx.Graphics.OpenGL;
  6. using Ryujinx.Input.HLE;
  7. using System;
  8. using static SDL2.SDL;
  9. namespace Ryujinx.Headless.SDL2.OpenGL
  10. {
  11. class OpenGLWindow : WindowBase
  12. {
  13. private static void SetupOpenGLAttributes(bool sharedContext, GraphicsDebugLevel debugLevel)
  14. {
  15. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3);
  16. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 3);
  17. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
  18. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_FLAGS, debugLevel != GraphicsDebugLevel.None ? (int)SDL_GLcontext.SDL_GL_CONTEXT_DEBUG_FLAG : 0);
  19. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, sharedContext ? 1 : 0);
  20. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ACCELERATED_VISUAL, 1);
  21. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_RED_SIZE, 8);
  22. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_GREEN_SIZE, 8);
  23. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_BLUE_SIZE, 8);
  24. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ALPHA_SIZE, 8);
  25. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DEPTH_SIZE, 16);
  26. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STENCIL_SIZE, 0);
  27. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DOUBLEBUFFER, 1);
  28. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STEREO, 0);
  29. }
  30. private class OpenToolkitBindingsContext : IBindingsContext
  31. {
  32. public IntPtr GetProcAddress(string procName)
  33. {
  34. return SDL_GL_GetProcAddress(procName);
  35. }
  36. }
  37. private class SDL2OpenGLContext : IOpenGLContext
  38. {
  39. private IntPtr _context;
  40. private IntPtr _window;
  41. private bool _shouldDisposeWindow;
  42. public SDL2OpenGLContext(IntPtr context, IntPtr window, bool shouldDisposeWindow = true)
  43. {
  44. _context = context;
  45. _window = window;
  46. _shouldDisposeWindow = shouldDisposeWindow;
  47. }
  48. public static SDL2OpenGLContext CreateBackgroundContext(SDL2OpenGLContext sharedContext)
  49. {
  50. sharedContext.MakeCurrent();
  51. // Ensure we share our contexts.
  52. SetupOpenGLAttributes(true, GraphicsDebugLevel.None);
  53. IntPtr windowHandle = SDL_CreateWindow("Ryujinx background context window", 0, 0, 1, 1, SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_HIDDEN);
  54. IntPtr context = SDL_GL_CreateContext(windowHandle);
  55. GL.LoadBindings(new OpenToolkitBindingsContext());
  56. SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0);
  57. SDL_GL_MakeCurrent(windowHandle, IntPtr.Zero);
  58. return new SDL2OpenGLContext(context, windowHandle);
  59. }
  60. public void MakeCurrent()
  61. {
  62. if (SDL_GL_GetCurrentContext() == _context || SDL_GL_GetCurrentWindow() == _window)
  63. {
  64. return;
  65. }
  66. int res = SDL_GL_MakeCurrent(_window, _context);
  67. if (res != 0)
  68. {
  69. string errorMessage = $"SDL_GL_CreateContext failed with error \"{SDL_GetError()}\"";
  70. Logger.Error?.Print(LogClass.Application, errorMessage);
  71. throw new Exception(errorMessage);
  72. }
  73. }
  74. public void Dispose()
  75. {
  76. SDL_GL_DeleteContext(_context);
  77. if (_shouldDisposeWindow)
  78. {
  79. SDL_DestroyWindow(_window);
  80. }
  81. }
  82. }
  83. private GraphicsDebugLevel _glLogLevel;
  84. private SDL2OpenGLContext _openGLContext;
  85. public OpenGLWindow(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse) : base(inputManager, glLogLevel, aspectRatio, enableMouse)
  86. {
  87. _glLogLevel = glLogLevel;
  88. }
  89. public override SDL_WindowFlags GetWindowFlags() => SDL_WindowFlags.SDL_WINDOW_OPENGL;
  90. protected override void InitializeWindowRenderer()
  91. {
  92. // Ensure to not share this context with other contexts before this point.
  93. SetupOpenGLAttributes(false, _glLogLevel);
  94. IntPtr context = SDL_GL_CreateContext(WindowHandle);
  95. SDL_GL_SetSwapInterval(1);
  96. if (context == IntPtr.Zero)
  97. {
  98. string errorMessage = $"SDL_GL_CreateContext failed with error \"{SDL_GetError()}\"";
  99. Logger.Error?.Print(LogClass.Application, errorMessage);
  100. throw new Exception(errorMessage);
  101. }
  102. // NOTE: The window handle needs to be disposed by the thread that created it and is handled separately.
  103. _openGLContext = new SDL2OpenGLContext(context, WindowHandle, false);
  104. // First take exclusivity on the OpenGL context.
  105. ((OpenGLRenderer)Renderer).InitializeBackgroundContext(SDL2OpenGLContext.CreateBackgroundContext(_openGLContext));
  106. _openGLContext.MakeCurrent();
  107. GL.ClearColor(0, 0, 0, 1.0f);
  108. GL.Clear(ClearBufferMask.ColorBufferBit);
  109. SwapBuffers(0);
  110. Renderer?.Window.SetSize(DefaultWidth, DefaultHeight);
  111. MouseDriver.SetClientSize(DefaultWidth, DefaultHeight);
  112. }
  113. protected override void InitializeRenderer() { }
  114. protected override void FinalizeWindowRenderer()
  115. {
  116. // Try to bind the OpenGL context before calling the gpu disposal.
  117. _openGLContext.MakeCurrent();
  118. Device.DisposeGpu();
  119. // Unbind context and destroy everything
  120. SDL_GL_MakeCurrent(WindowHandle, IntPtr.Zero);
  121. _openGLContext.Dispose();
  122. }
  123. protected override void SwapBuffers(object image)
  124. {
  125. if ((int)image != 0)
  126. {
  127. // The game's framebruffer is already bound, so blit it to the window's backbuffer
  128. GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0);
  129. GL.Clear(ClearBufferMask.ColorBufferBit);
  130. GL.ClearColor(0, 0, 0, 1);
  131. GL.BlitFramebuffer(0,
  132. 0,
  133. Width,
  134. Height,
  135. 0,
  136. 0,
  137. Width,
  138. Height,
  139. ClearBufferMask.ColorBufferBit,
  140. BlitFramebufferFilter.Linear);
  141. }
  142. SDL_GL_SwapWindow(WindowHandle);
  143. }
  144. }
  145. }