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 <vladimir@thecybershadow.net> 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 class ThreadedVideo : Video 23 { 24 this() 25 { 26 starting = false; 27 renderThread = new Thread(&renderThreadProc); 28 renderThread.start(); 29 } 30 31 override void shutdown() 32 { 33 stopping = quitting = true; 34 renderThread.join(); 35 } 36 37 override void start(Application application) 38 { 39 initMain(application); 40 41 if (!initializeVideoInRenderThread) 42 initVary(); 43 44 renderCallback.bind(&application.render); 45 46 started = stopping = false; 47 starting = true; 48 while (!started) wait(); 49 } 50 51 override void stop() 52 { 53 stopped = false; 54 stopping = true; 55 while (!stopped) wait(); 56 57 if (!initializeVideoInRenderThread) 58 doneVary(); 59 doneMain(); 60 } 61 62 override void stopAsync(AppCallback callback) 63 { 64 stopCallback = callback; 65 stopped = false; 66 stopping = true; 67 } 68 69 protected: 70 /// Allows varying the thread from which initVary gets called. 71 abstract @property bool initializeVideoInRenderThread(); 72 73 /// When initializing video in the render thread, block main thread while video is initializing? 74 /// Some renderers may require the main thread to be responsive during graphics initialization, 75 /// to pump events - thus, initialization must be asynchronous. 76 @property bool initializeVideoSynchronously() { return true; } 77 78 abstract Renderer getRenderer(); 79 80 /// Main thread initialization. 81 abstract void initMain(Application application); 82 83 /// Main/render thread initialization (depends on initializeVideoInRenderThread). 84 abstract void initVary(); 85 86 /// Main thread finalization. 87 abstract void doneMain(); 88 89 /// Main/render thread finalization (depends on initializeVideoInRenderThread). 90 abstract void doneVary(); 91 92 private: 93 final void wait() 94 { 95 if (error) 96 renderThread.join(); // collect exception 97 nop(); 98 } 99 100 final void nop() { Thread.sleep(msecs(1)); } 101 102 Thread renderThread; 103 shared bool starting, started, stopping, stopped, quitting, quit, error; 104 AppCallback stopCallback; 105 AppCallbackEx!(Renderer) renderCallback; 106 107 final void renderThreadProc() 108 { 109 scope(failure) error = true; 110 111 // SDL expects that only one thread across the program's 112 // lifetime will do OpenGL initialization. 113 // Thus, re-initialization must happen from only one thread. 114 // This thread sleeps and polls while it's not told to run. 115 outer: 116 while (!quitting) 117 { 118 while (!starting) 119 { 120 // TODO: use proper semaphores 121 if (quitting) return; 122 nop(); 123 } 124 125 if (initializeVideoInRenderThread && initializeVideoSynchronously) 126 initVary(); 127 128 started = true; starting = false; 129 scope(failure) if (errorCallback) try { errorCallback.call(); } catch (Exception) {} 130 131 if (initializeVideoInRenderThread && !initializeVideoSynchronously) 132 initVary(); 133 134 auto renderer = getRenderer(); 135 136 while (!stopping) 137 { 138 // TODO: predict flip (vblank wait) duration and render at the last moment 139 renderCallback.call(renderer); 140 renderer.present(); 141 } 142 renderer.shutdown(); 143 144 if (initializeVideoInRenderThread) 145 doneVary(); 146 147 if (stopCallback) 148 stopCallback.call(); 149 150 stopped = true; stopping = false; 151 } 152 } 153 }