1 /**
2 * ae.ui.video.threadedvideo
3 *
4 * License:
5 * This Source Code Form is subject to the terms of
6 * the Mozilla Public License, v. 2.0. If a copy of
7 * the MPL was not distributed with this file, You
8 * can obtain one at http://mozilla.org/MPL/2.0/.
9 *
10 * Authors:
11 * Vladimir Panteleev <ae@cy.md>
12 */
13
14 module ae.ui.video.threadedvideo;
15
16 import core.thread;
17
18 import ae.ui.video.video;
19 import ae.ui.app.application;
20 import ae.ui.video.renderer;
21
22 /// Base `Video` class for implementations
23 /// which run in a dedicated thread.
24 class ThreadedVideo : Video
25 {
26 this()
27 {
28 starting = false;
29 renderThread = new Thread(&renderThreadProc);
30 renderThread.start();
31 } ///
32
33 override void shutdown()
34 {
35 stopping = quitting = true;
36 renderThread.join();
37 } ///
38
39 override void start(Application application)
40 {
41 initMain(application);
42
43 if (!initializeVideoInRenderThread)
44 initVary();
45
46 renderCallback.bind(&application.render);
47
48 started = stopping = false;
49 starting = true;
50 while (!started) wait();
51 } ///
52
53 override void stop()
54 {
55 stopped = false;
56 stopping = true;
57 while (!stopped) wait();
58
59 if (!initializeVideoInRenderThread)
60 doneVary();
61 doneMain();
62 } ///
63
64 override void stopAsync(AppCallback callback)
65 {
66 stopCallback = callback;
67 stopped = false;
68 stopping = true;
69 } ///
70
71 protected:
72 /// Allows varying the thread from which initVary gets called.
73 abstract @property bool initializeVideoInRenderThread();
74
75 /// When initializing video in the render thread, block main thread while video is initializing?
76 /// Some renderers may require the main thread to be responsive during graphics initialization,
77 /// to pump events - thus, initialization must be asynchronous.
78 @property bool initializeVideoSynchronously() { return true; }
79
80 abstract Renderer getRenderer();
81
82 /// Main thread initialization.
83 abstract void initMain(Application application);
84
85 /// Main/render thread initialization (depends on initializeVideoInRenderThread).
86 abstract void initVary();
87
88 /// Main thread finalization.
89 abstract void doneMain();
90
91 /// Main/render thread finalization (depends on initializeVideoInRenderThread).
92 abstract void doneVary();
93
94 private:
95 final void wait()
96 {
97 if (error)
98 renderThread.join(); // collect exception
99 nop();
100 }
101
102 final void nop() { Thread.sleep(msecs(1)); }
103
104 Thread renderThread;
105 shared bool starting, started, stopping, stopped, quitting, quit, error;
106 AppCallback stopCallback;
107 AppCallbackEx!(Renderer) renderCallback;
108
109 final void renderThreadProc()
110 {
111 scope(failure) error = true;
112
113 // SDL expects that only one thread across the program's
114 // lifetime will do OpenGL initialization.
115 // Thus, re-initialization must happen from only one thread.
116 // This thread sleeps and polls while it's not told to run.
117 outer:
118 while (!quitting)
119 {
120 while (!starting)
121 {
122 // TODO: use proper semaphores
123 if (quitting) return;
124 nop();
125 }
126
127 if (initializeVideoInRenderThread && initializeVideoSynchronously)
128 initVary();
129
130 started = true; starting = false;
131 scope(failure) if (errorCallback) try { errorCallback.call(); } catch (Exception) {}
132
133 if (initializeVideoInRenderThread && !initializeVideoSynchronously)
134 initVary();
135
136 auto renderer = getRenderer();
137
138 while (!stopping)
139 {
140 // TODO: predict flip (vblank wait) duration and render at the last moment
141 renderCallback.call(renderer);
142 renderer.present();
143 }
144 renderer.shutdown();
145
146 if (initializeVideoInRenderThread)
147 doneVary();
148
149 if (stopCallback)
150 stopCallback.call();
151
152 stopped = true; stopping = false;
153 }
154 }
155 }