aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Gtk3/UI/OpenGLRenderer.cs
blob: 1fdabc754b35b49e26dc552a75ef103bb4b98b13 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Input.HLE;
using SPB.Graphics;
using SPB.Graphics.Exceptions;
using SPB.Graphics.OpenGL;
using SPB.Platform;
using SPB.Platform.GLX;
using SPB.Platform.WGL;
using SPB.Windowing;
using System;
using System.Runtime.InteropServices;

namespace Ryujinx.UI
{
    public partial class OpenGLRenderer : RendererWidgetBase
    {
        private readonly GraphicsDebugLevel _glLogLevel;

        private bool _initializedOpenGL;

        private OpenGLContextBase _openGLContext;
        private SwappableNativeWindowBase _nativeWindow;

        public OpenGLRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel)
        {
            _glLogLevel = glLogLevel;
        }

        protected override bool OnDrawn(Cairo.Context cr)
        {
            if (!_initializedOpenGL)
            {
                IntializeOpenGL();
            }

            return true;
        }

        private void IntializeOpenGL()
        {
            _nativeWindow = RetrieveNativeWindow();

            Window.EnsureNative();

            _openGLContext = PlatformHelper.CreateOpenGLContext(GetGraphicsMode(), 3, 3, _glLogLevel == GraphicsDebugLevel.None ? OpenGLContextFlags.Compat : OpenGLContextFlags.Compat | OpenGLContextFlags.Debug);
            _openGLContext.Initialize(_nativeWindow);
            _openGLContext.MakeCurrent(_nativeWindow);

            // Release the GL exclusivity that SPB gave us as we aren't going to use it in GTK Thread.
            _openGLContext.MakeCurrent(null);

            WaitEvent.Set();

            _initializedOpenGL = true;
        }

        private SwappableNativeWindowBase RetrieveNativeWindow()
        {
            if (OperatingSystem.IsWindows())
            {
                IntPtr windowHandle = gdk_win32_window_get_handle(Window.Handle);

                return new WGLWindow(new NativeHandle(windowHandle));
            }
            else if (OperatingSystem.IsLinux())
            {
                IntPtr displayHandle = gdk_x11_display_get_xdisplay(Display.Handle);
                IntPtr windowHandle = gdk_x11_window_get_xid(Window.Handle);

                return new GLXWindow(new NativeHandle(displayHandle), new NativeHandle(windowHandle));
            }

            throw new NotImplementedException();
        }

        [LibraryImport("libgdk-3-0.dll")]
        private static partial IntPtr gdk_win32_window_get_handle(IntPtr d);

        [LibraryImport("libgdk-3.so.0")]
        private static partial IntPtr gdk_x11_display_get_xdisplay(IntPtr gdkDisplay);

        [LibraryImport("libgdk-3.so.0")]
        private static partial IntPtr gdk_x11_window_get_xid(IntPtr gdkWindow);

        private static FramebufferFormat GetGraphicsMode()
        {
            return Environment.OSVersion.Platform == PlatformID.Unix ? new FramebufferFormat(new ColorFormat(8, 8, 8, 0), 16, 0, ColorFormat.Zero, 0, 2, false) : FramebufferFormat.Default;
        }

        public override void InitializeRenderer()
        {
            // First take exclusivity on the OpenGL context.
            ((Graphics.OpenGL.OpenGLRenderer)Renderer).InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(_openGLContext));

            _openGLContext.MakeCurrent(_nativeWindow);

            GL.ClearColor(0, 0, 0, 1.0f);
            GL.Clear(ClearBufferMask.ColorBufferBit);
            SwapBuffers();
        }

        public override void SwapBuffers()
        {
            _nativeWindow.SwapBuffers();
        }

        protected override string GetGpuBackendName()
        {
            return "OpenGL";
        }

        protected override void Dispose(bool disposing)
        {
            // Try to bind the OpenGL context before calling the shutdown event.
            try
            {
                _openGLContext?.MakeCurrent(_nativeWindow);
            }
            catch (ContextException e)
            {
                Logger.Warning?.Print(LogClass.UI, $"Failed to bind OpenGL context: {e}");
            }

            Device?.DisposeGpu();
            NpadManager.Dispose();

            // Unbind context and destroy everything.
            try
            {
                _openGLContext?.MakeCurrent(null);
            }
            catch (ContextException e)
            {
                Logger.Warning?.Print(LogClass.UI, $"Failed to unbind OpenGL context: {e}");
            }

            _openGLContext?.Dispose();
        }
    }
}