]> git.sesse.net Git - casparcg/blob - core/renderer/render_device.cpp
2.0.0.2:
[casparcg] / core / renderer / render_device.cpp
1 #include "..\StdAfx.h"\r
2 \r
3 #ifdef _MSC_VER\r
4 #pragma warning (disable : 4244)\r
5 #endif\r
6 \r
7 #include "render_device.h"\r
8 #include "layer.h"\r
9 \r
10 #include "../consumer/frame_consumer.h"\r
11 \r
12 #include "../frame/frame_format.h"\r
13 #include "../frame/gpu_frame_processor.h"\r
14 \r
15 #include "../../common/utility/scope_exit.h"\r
16 #include "../../common/utility/memory.h"\r
17 \r
18 #include <numeric>\r
19 \r
20 #include <boost/filesystem.hpp>\r
21 #include <boost/thread.hpp>\r
22 #include <boost/date_time/posix_time/posix_time.hpp>\r
23 #include <boost/range/algorithm.hpp>\r
24 #include <boost/range/algorithm_ext/erase.hpp>\r
25 #include <boost/range/sub_range.hpp>\r
26 #include <boost/range/adaptor/indirected.hpp>\r
27 #include <boost/foreach.hpp>\r
28 \r
29 #include <tbb/parallel_for.h>\r
30 #include <tbb/mutex.h>\r
31 \r
32 using namespace boost::assign;\r
33         \r
34 namespace caspar{ namespace renderer{\r
35         \r
36 std::vector<gpu_frame_ptr> render_frames(std::map<int, layer>& layers)\r
37 {       \r
38         std::vector<gpu_frame_ptr> frames(layers.size(), nullptr);\r
39         tbb::parallel_for(tbb::blocked_range<size_t>(0, frames.size()), [&](const tbb::blocked_range<size_t>& r)\r
40         {\r
41                 auto it = layers.begin();\r
42                 std::advance(it, r.begin());\r
43                 for(size_t i = r.begin(); i != r.end(); ++i, ++it)\r
44                         frames[i] = it->second.get_frame();\r
45         });                                     \r
46         return frames;\r
47 }\r
48 \r
49 struct render_device::implementation : boost::noncopyable\r
50 {       \r
51         implementation(const caspar::frame_format_desc& format_desc, unsigned int index, const std::vector<frame_consumer_ptr>& consumers)  \r
52                 : consumers_(consumers), fmt_(format_desc), frame_processor_(new gpu_frame_processor(format_desc)), needs_clock_(false)\r
53         {       \r
54                 is_running_ = true;\r
55                 if(consumers.empty())\r
56                         BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("consumer") \r
57                                                                                                                 << msg_info("render_device requires atleast one consumer"));\r
58 \r
59                 if(std::any_of(consumers.begin(), consumers.end(), [&](const frame_consumer_ptr& pConsumer){ return pConsumer->get_frame_format_desc() != format_desc;}))\r
60                         BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("consumer") \r
61                                                                                                                 << msg_info("All consumers must have same frameformat as renderdevice."));\r
62                 \r
63                 needs_clock_ = !std::any_of(consumers.begin(), consumers.end(), std::mem_fn(&frame_consumer::has_sync_clock));\r
64 \r
65                 frame_buffer_.set_capacity(3);\r
66                 display_thread_ = boost::thread([=]{display();});\r
67                 render_thread_ = boost::thread([=]{render();});\r
68 \r
69                 CASPAR_LOG(info) << L"Initialized render_device with " << format_desc;\r
70         }\r
71                         \r
72         ~implementation()\r
73         {\r
74                 is_running_ = false;\r
75                 frame_buffer_.clear();\r
76                 frame_buffer_.push(nullptr);\r
77                 render_thread_.join();\r
78                 display_thread_.join();\r
79         }\r
80                 \r
81         void render()\r
82         {               \r
83                 CASPAR_LOG(info) << L"Started render_device::render thread";\r
84                 win32_exception::install_handler();\r
85                 \r
86                 while(is_running_)\r
87                 {\r
88                         try\r
89                         {       \r
90                                 std::vector<gpu_frame_ptr> next_frames;\r
91                                 gpu_frame_ptr composite_frame;          \r
92 \r
93                                 {\r
94                                         tbb::mutex::scoped_lock lock(layers_mutex_);    \r
95                                         next_frames = render_frames(layers_);\r
96                                 }\r
97                                 frame_processor_->push(next_frames);\r
98                                 frame_processor_->pop(composite_frame); \r
99                                 frame_buffer_.push(std::move(composite_frame));\r
100                         }\r
101                         catch(...)\r
102                         {\r
103                                 CASPAR_LOG_CURRENT_EXCEPTION();\r
104                                 layers_.clear();\r
105                                 CASPAR_LOG(error) << "Unexpected exception. Cleared layers in render-device";\r
106                         }\r
107                 }\r
108 \r
109                 CASPAR_LOG(info) << L"Ended render_device::render thread";\r
110         }\r
111 \r
112         struct video_sync_clock\r
113         {\r
114                 video_sync_clock(const caspar::frame_format_desc& format_desc)\r
115                 {\r
116                         period_ = static_cast<long>(get_frame_format_period(format_desc)*1000000.0);\r
117                         time_ = boost::posix_time::microsec_clock::local_time();\r
118                 }\r
119 \r
120                 void sync_video()\r
121                 {\r
122                         auto remaining = boost::posix_time::microseconds(period_) - (boost::posix_time::microsec_clock::local_time() - time_);\r
123                         if(remaining > boost::posix_time::microseconds(5000))\r
124                                 boost::this_thread::sleep(remaining - boost::posix_time::microseconds(5000));\r
125                         time_ = boost::posix_time::microsec_clock::local_time();\r
126                 }\r
127 \r
128                 boost::posix_time::ptime time_;\r
129                 long period_;\r
130 \r
131         };\r
132         \r
133         void display()\r
134         {\r
135                 CASPAR_LOG(info) << L"Started render_device::display thread";\r
136                 win32_exception::install_handler();\r
137                                 \r
138                 gpu_frame_ptr frame = frame_processor_->create_frame(fmt_.width, fmt_.height);\r
139                 common::clear(frame->data(), frame->size());\r
140                 std::deque<gpu_frame_ptr> prepared(3, frame);\r
141 \r
142                 video_sync_clock clock(fmt_);\r
143                                 \r
144                 while(is_running_)\r
145                 {\r
146                         if(needs_clock_)\r
147                                 clock.sync_video();\r
148 \r
149                         if(!frame_buffer_.try_pop(frame))\r
150                         {\r
151                                 CASPAR_LOG(trace) << "Display Buffer Underrun";\r
152                                 frame_buffer_.pop(frame);\r
153                         }\r
154                         if(frame != nullptr)\r
155                         {\r
156                                 send_frame(prepared.front(), frame);\r
157                                 prepared.push_back(frame);\r
158                                 prepared.pop_front();\r
159                         }\r
160                 }\r
161                 \r
162                 CASPAR_LOG(info) << L"Ended render_device::display thread";\r
163         }\r
164 \r
165         void send_frame(const gpu_frame_ptr& prepared_frame, const gpu_frame_ptr& next_frame)\r
166         {\r
167                 BOOST_FOREACH(const frame_consumer_ptr& consumer, consumers_)\r
168                 {\r
169                         try\r
170                         {\r
171                                 consumer->prepare(next_frame); // Could block\r
172                                 consumer->display(prepared_frame); // Could block\r
173                         }\r
174                         catch(...)\r
175                         {\r
176                                 CASPAR_LOG_CURRENT_EXCEPTION();\r
177                                 boost::range::remove_erase(consumers_, consumer);\r
178                                 CASPAR_LOG(warning) << "Removed consumer from render-device.";\r
179                                 if(consumers_.empty())\r
180                                 {\r
181                                         CASPAR_LOG(warning) << "No consumers available. Shutting down render-device.";\r
182                                         is_running_ = false;\r
183                                 }\r
184                         }\r
185                 }\r
186         }\r
187                 \r
188         void load(int exLayer, const frame_producer_ptr& producer, load_option option)\r
189         {\r
190                 if(producer->get_frame_format_desc() != fmt_)\r
191                         BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("pProducer") << msg_info("Invalid frame format"));\r
192 \r
193                 producer->initialize(frame_processor_);\r
194                 tbb::mutex::scoped_lock lock(layers_mutex_);\r
195                 layers_[exLayer].load(producer, option);\r
196         }\r
197                         \r
198         void play(int exLayer)\r
199         {               \r
200                 tbb::mutex::scoped_lock lock(layers_mutex_);\r
201                 auto it = layers_.find(exLayer);\r
202                 if(it != layers_.end())\r
203                         it->second.play();              \r
204         }\r
205 \r
206         void stop(int exLayer)\r
207         {               \r
208                 tbb::mutex::scoped_lock lock(layers_mutex_);\r
209                 auto it = layers_.find(exLayer);\r
210                 if(it != layers_.end())\r
211                         it->second.stop();\r
212         }\r
213 \r
214         void clear(int exLayer)\r
215         {\r
216                 tbb::mutex::scoped_lock lock(layers_mutex_);\r
217                 auto it = layers_.find(exLayer);\r
218                 if(it != layers_.end())\r
219                         it->second.clear();             \r
220         }\r
221                 \r
222         void clear()\r
223         {\r
224                 tbb::mutex::scoped_lock lock(layers_mutex_);\r
225                 layers_.clear();\r
226         }               \r
227 \r
228         frame_producer_ptr active(int exLayer) const\r
229         {\r
230                 tbb::mutex::scoped_lock lock(layers_mutex_);\r
231                 auto it = layers_.find(exLayer);\r
232                 return it != layers_.end() ? it->second.active() : nullptr;\r
233         }\r
234         \r
235         frame_producer_ptr background(int exLayer) const\r
236         {\r
237                 tbb::mutex::scoped_lock lock(layers_mutex_);\r
238                 auto it = layers_.find(exLayer);\r
239                 return it != layers_.end() ? it->second.background() : nullptr;\r
240         }\r
241                         \r
242         boost::thread render_thread_;\r
243         boost::thread display_thread_;\r
244                 \r
245         caspar::frame_format_desc fmt_;\r
246         tbb::concurrent_bounded_queue<gpu_frame_ptr> frame_buffer_;\r
247         \r
248         std::vector<frame_consumer_ptr> consumers_;\r
249         \r
250         mutable tbb::mutex layers_mutex_;\r
251         std::map<int, layer> layers_;\r
252         \r
253         tbb::atomic<bool> is_running_;  \r
254 \r
255         gpu_frame_processor_ptr frame_processor_;\r
256 \r
257         bool needs_clock_;\r
258 };\r
259 \r
260 render_device::render_device(const caspar::frame_format_desc& format_desc, unsigned int index, const std::vector<frame_consumer_ptr>& consumers) \r
261         : impl_(new implementation(format_desc, index, consumers)){}\r
262 void render_device::load(int exLayer, const frame_producer_ptr& pProducer, load_option option){impl_->load(exLayer, pProducer, option);}\r
263 void render_device::play(int exLayer){impl_->play(exLayer);}\r
264 void render_device::stop(int exLayer){impl_->stop(exLayer);}\r
265 void render_device::clear(int exLayer){impl_->clear(exLayer);}\r
266 void render_device::clear(){impl_->clear();}\r
267 frame_producer_ptr render_device::active(int exLayer) const {return impl_->active(exLayer);}\r
268 frame_producer_ptr render_device::background(int exLayer) const {return impl_->background(exLayer);}\r
269 const frame_format_desc& render_device::frame_format_desc() const{return impl_->fmt_;}\r
270 }}\r