aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.OpenGL/BackgroundContextWorker.cs
blob: f22e0df57b914781dde869014c220e115e09c230 (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
using Ryujinx.Common;
using System;
using System.Collections.Generic;
using System.Threading;

namespace Ryujinx.Graphics.OpenGL
{
    unsafe class BackgroundContextWorker : IDisposable
    {
        [ThreadStatic]
        public static bool InBackground;
        private readonly Thread _thread;
        private bool _running;

        private readonly AutoResetEvent _signal;
        private readonly Queue<Action> _work;
        private readonly ObjectPool<ManualResetEventSlim> _invokePool;
        private readonly IOpenGLContext _backgroundContext;

        public BackgroundContextWorker(IOpenGLContext backgroundContext)
        {
            _backgroundContext = backgroundContext;
            _running = true;

            _signal = new AutoResetEvent(false);
            _work = new Queue<Action>();
            _invokePool = new ObjectPool<ManualResetEventSlim>(() => new ManualResetEventSlim(), 10);

            _thread = new Thread(Run);
            _thread.Start();
        }

        public bool HasContext() => _backgroundContext.HasContext();

        private void Run()
        {
            InBackground = true;

            _backgroundContext.MakeCurrent();

            while (_running)
            {
                Action action;

                lock (_work)
                {
                    _work.TryDequeue(out action);
                }

                if (action != null)
                {
                    action();
                }
                else
                {
                    _signal.WaitOne();
                }
            }

            _backgroundContext.Dispose();
        }

        public void Invoke(Action action)
        {
            ManualResetEventSlim actionComplete = _invokePool.Allocate();

            lock (_work)
            {
                _work.Enqueue(() =>
                {
                    action();
                    actionComplete.Set();
                });
            }

            _signal.Set();

            actionComplete.Wait();
            actionComplete.Reset();

            _invokePool.Release(actionComplete);
        }

        public void Dispose()
        {
            _running = false;
            _signal.Set();

            _thread.Join();
            _signal.Dispose();
        }
    }
}