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