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 }