1 #include "..\StdAfx.h"
\r
3 #include "render_device.h"
\r
6 #include "../consumer/frame_consumer.h"
\r
8 #include "../frame/frame_format.h"
\r
9 #include "../frame/gpu_frame_processor.h"
\r
11 #include "../../common/utility/scope_exit.h"
\r
12 #include "../../common/image/image.h"
\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
25 #include <tbb/parallel_for.h>
\r
26 #include <tbb/mutex.h>
\r
28 using namespace boost::assign;
\r
30 namespace caspar{ namespace renderer{
\r
32 std::vector<gpu_frame_ptr> render_frames(std::map<int, layer>& layers)
\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
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
45 struct render_device::implementation : boost::noncopyable
\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
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
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
59 frame_buffer_.set_capacity(3);
\r
60 display_thread_ = boost::thread([=]{display();});
\r
61 render_thread_ = boost::thread([=]{render();});
\r
63 CASPAR_LOG(info) << L"Initialized render_device with " << format_desc;
\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
77 CASPAR_LOG(info) << L"Started render_device::render Thread";
\r
78 win32_exception::install_handler();
\r
84 std::vector<gpu_frame_ptr> next_frames;
\r
85 gpu_frame_ptr composite_frame;
\r
88 tbb::mutex::scoped_lock lock(layers_mutex_);
\r
89 next_frames = render_frames(layers_);
\r
91 frame_processor_->push(next_frames);
\r
92 frame_processor_->pop(composite_frame);
\r
93 frame_buffer_.push(std::move(composite_frame));
\r
97 CASPAR_LOG_CURRENT_EXCEPTION();
\r
99 CASPAR_LOG(error) << "Unexpected exception. Cleared layers in render-device";
\r
103 CASPAR_LOG(info) << L"Ended render_device::render Thread";
\r
108 CASPAR_LOG(info) << L"Started render_device::display Thread";
\r
109 win32_exception::install_handler();
\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
117 if(!frame_buffer_.try_pop(frame))
\r
119 CASPAR_LOG(trace) << "Display Buffer Underrun";
\r
120 frame_buffer_.pop(frame);
\r
122 if(frame != nullptr)
\r
124 send_frame(prepared.front(), frame);
\r
125 prepared.push_back(frame);
\r
126 prepared.pop_front();
\r
130 CASPAR_LOG(info) << L"Ended render_device::display Thread";
\r
133 void send_frame(const gpu_frame_ptr& prepared_frame, const gpu_frame_ptr& next_frame)
\r
135 BOOST_FOREACH(const frame_consumer_ptr& consumer, consumers_)
\r
139 consumer->prepare(next_frame); // Could block
\r
140 consumer->display(prepared_frame); // Could block
\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
149 CASPAR_LOG(warning) << "No consumers available. Shutting down render-device.";
\r
150 is_running_ = false;
\r
156 void load(int exLayer, const frame_producer_ptr& producer, load_option option)
\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
161 producer->initialize(frame_processor_);
\r
162 tbb::mutex::scoped_lock lock(layers_mutex_);
\r
163 layers_[exLayer].load(producer, option);
\r
166 void play(int exLayer)
\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
174 void stop(int exLayer)
\r
176 tbb::mutex::scoped_lock lock(layers_mutex_);
\r
177 auto it = layers_.find(exLayer);
\r
178 if(it != layers_.end())
\r
182 void clear(int exLayer)
\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
192 tbb::mutex::scoped_lock lock(layers_mutex_);
\r
196 frame_producer_ptr active(int exLayer) const
\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
203 frame_producer_ptr background(int exLayer) const
\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
210 boost::thread render_thread_;
\r
211 boost::thread display_thread_;
\r
213 caspar::frame_format_desc fmt_;
\r
214 tbb::concurrent_bounded_queue<gpu_frame_ptr> frame_buffer_;
\r
216 std::vector<frame_consumer_ptr> consumers_;
\r
218 mutable tbb::mutex layers_mutex_;
\r
219 std::map<int, layer> layers_;
\r
221 tbb::atomic<bool> is_running_;
\r
223 gpu_frame_processor_ptr frame_processor_;
\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