<ClInclude Include="protocol\clk\CLKCommand.h" />\r
<ClInclude Include="protocol\clk\CLKProtocolStrategy.h" />\r
<ClInclude Include="protocol\media.h" />\r
+ <ClInclude Include="renderer\display_device.h" />\r
<ClInclude Include="renderer\renderer_fwd.h" />\r
<ClInclude Include="renderer\render_device.h" />\r
<ClInclude Include="renderer\layer.h" />\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
</ClCompile>\r
+ <ClCompile Include="renderer\display_device.cpp">\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
+ </ClCompile>\r
<ClCompile Include="renderer\render_device.cpp">\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<ClInclude Include="frame\gpu_composite_frame.h">\r
<Filter>Source\frame\gpu</Filter>\r
</ClInclude>\r
+ <ClInclude Include="renderer\display_device.h">\r
+ <Filter>Source\renderer</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="StdAfx.cpp">\r
<ClCompile Include="frame\gpu_composite_frame.cpp">\r
<Filter>Source\frame\gpu</Filter>\r
</ClCompile>\r
+ <ClCompile Include="renderer\display_device.cpp">\r
+ <Filter>Source\renderer</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
<ItemGroup>\r
<Midl Include="consumer\decklink\DeckLinkAPI_v7_3.idl">\r
output_frame_ = std::make_shared<gpu_frame>(format_desc_.width, format_desc_.height);\r
index_ = 0;\r
});\r
+ composite(std::vector<gpu_frame_ptr>());\r
+ composite(std::vector<gpu_frame_ptr>());\r
+ composite(std::vector<gpu_frame_ptr>());\r
}\r
\r
~implementation()\r
\r
// 4. Output to external buffer\r
if(output_frame_->read_unlock())\r
- output_signal_(output_frame_);\r
+ finished_frames_.push(output_frame_);\r
\r
// 3. Draw to framebuffer and start asynchronous DMA transfer to page-locked memory \r
// Clear framebuffer\r
});\r
}\r
\r
- boost::signals2::connection subscribe(const std::function<void(const gpu_frame_ptr&)> func)\r
+ void pop(gpu_frame_ptr& frame)\r
{\r
- return output_signal_.connect(func);\r
+ finished_frames_.pop(frame);\r
}\r
\r
tbb::concurrent_unordered_map<size_t, tbb::concurrent_bounded_queue<gpu_frame_ptr>> reading_frame_pools_;\r
\r
common::executor executor_;\r
\r
- boost::signals2::signal<void(const gpu_frame_ptr&)> output_signal_; \r
+ tbb::concurrent_bounded_queue<gpu_frame_ptr> finished_frames_; \r
};\r
\r
gpu_frame_processor::gpu_frame_processor(const frame_format_desc& format_desc) : impl_(new implementation(format_desc)){}\r
void gpu_frame_processor::push(const std::vector<gpu_frame_ptr>& frames){ impl_->composite(frames);}\r
-boost::signals2::connection gpu_frame_processor::subscribe(const std::function<void(const gpu_frame_ptr&)> func){return impl_->subscribe(func);}\r
+void gpu_frame_processor::pop(gpu_frame_ptr& frame){impl_->pop(frame);}\r
gpu_frame_ptr gpu_frame_processor::create_frame(size_t width, size_t height){return impl_->create_frame(width, height);}\r
\r
}}
\ No newline at end of file
#include "frame_fwd.h"\r
#include "frame_factory.h"\r
\r
-#include <boost/signals2/signal.hpp>\r
-\r
namespace caspar { namespace core {\r
\r
class gpu_frame_processor : public frame_factory, boost::noncopyable\r
{\r
public:\r
gpu_frame_processor(const frame_format_desc& format_desc);\r
-\r
- boost::signals2::connection subscribe(const std::function<void(const gpu_frame_ptr&)> func);\r
+ \r
void push(const std::vector<gpu_frame_ptr>& frames);\r
+ void pop(gpu_frame_ptr& frame);\r
\r
gpu_frame_ptr create_frame(size_t width, size_t height);\r
private:\r
--- /dev/null
+#include "../StdAfx.h"\r
+\r
+#ifdef _MSC_VER\r
+#pragma warning (disable : 4244)\r
+#endif\r
+\r
+#include "display_device.h"\r
+\r
+#include "../frame/frame_format.h"\r
+#include "../frame/gpu_frame.h"\r
+\r
+#include <tbb/concurrent_queue.h>\r
+#include <tbb/atomic.h>\r
+\r
+#include <boost/foreach.hpp>\r
+#include <boost/thread.hpp>\r
+\r
+#include <boost/date_time/posix_time/posix_time.hpp>\r
+\r
+#include <boost/range/algorithm_ext/erase.hpp>\r
+\r
+namespace caspar { namespace core { namespace renderer {\r
+\r
+class video_sync_clock\r
+{\r
+public:\r
+ video_sync_clock(const frame_format_desc& format_desc)\r
+ {\r
+ period_ = static_cast<long>(get_frame_format_period(format_desc)*1000000.0);\r
+ time_ = boost::posix_time::microsec_clock::local_time();\r
+ }\r
+\r
+ void synchronize()\r
+ {\r
+ auto remaining = boost::posix_time::microseconds(period_) - (boost::posix_time::microsec_clock::local_time() - time_);\r
+ if(remaining > boost::posix_time::microseconds(5000))\r
+ boost::this_thread::sleep(remaining - boost::posix_time::microseconds(5000));\r
+ time_ = boost::posix_time::microsec_clock::local_time();\r
+ }\r
+private:\r
+ boost::posix_time::ptime time_;\r
+ long period_;\r
+};\r
+\r
+struct display_device::implementation\r
+{\r
+public:\r
+ implementation(const frame_format_desc& format_desc, const std::vector<frame_consumer_ptr>& consumers) : consumers_(consumers), fmt_(format_desc)\r
+ {\r
+ if(consumers.empty())\r
+ BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("consumer") \r
+ << msg_info("display_device requires atleast one consumer"));\r
+\r
+ if(std::any_of(consumers.begin(), consumers.end(), [&](const frame_consumer_ptr& pConsumer){ return pConsumer->get_frame_format_desc() != format_desc;}))\r
+ BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("consumer") \r
+ << msg_info("All consumers must have same frameformat as display_device."));\r
+ \r
+ needs_clock_ = !std::any_of(consumers.begin(), consumers.end(), std::mem_fn(&frame_consumer::has_sync_clock));\r
+ frame_buffer_.set_capacity(3);\r
+ is_running_ = true;\r
+ display_thread_ = boost::thread([=]{run();});\r
+ }\r
+\r
+ ~implementation()\r
+ {\r
+ is_running_ = false;\r
+ frame_buffer_.clear();\r
+ display_thread_.join();\r
+ }\r
+\r
+ void display(const gpu_frame_ptr& frame)\r
+ {\r
+ if(is_running_)\r
+ frame_buffer_.push(frame);\r
+ }\r
+ \r
+ void run()\r
+ {\r
+ CASPAR_LOG(info) << L"Started display_device thread";\r
+ win32_exception::install_handler();\r
+ \r
+ video_sync_clock clock(fmt_);\r
+ \r
+ while(is_running_)\r
+ {\r
+ if(needs_clock_)\r
+ clock.synchronize();\r
+ \r
+ gpu_frame_ptr frame;\r
+ if(!frame_buffer_.try_pop(frame))\r
+ {\r
+ CASPAR_LOG(trace) << "Display Buffer Underrun";\r
+ frame_buffer_.pop(frame);\r
+ }\r
+ if(frame != nullptr) \r
+ display_frame(frame); \r
+ }\r
+ \r
+ CASPAR_LOG(info) << L"Ended display_device thread";\r
+ }\r
+\r
+ void display_frame(const gpu_frame_ptr& frame)\r
+ {\r
+ BOOST_FOREACH(const frame_consumer_ptr& consumer, consumers_)\r
+ {\r
+ try\r
+ {\r
+ consumer->prepare(frame); // Could block\r
+ prepared_frames_.push_back(frame);\r
+\r
+ if(prepared_frames_.size() > 2)\r
+ {\r
+ consumer->display(prepared_frames_.front()); // Could block\r
+ prepared_frames_.pop_front();\r
+ }\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ boost::range::remove_erase(consumers_, consumer);\r
+ CASPAR_LOG(warning) << "Removed consumer from render-device.";\r
+ if(consumers_.empty())\r
+ {\r
+ CASPAR_LOG(warning) << "No consumers available. Shutting down display-device.";\r
+ is_running_ = false;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ std::deque<gpu_frame_ptr> prepared_frames_;\r
+ \r
+ boost::thread display_thread_;\r
+\r
+ tbb::atomic<bool> is_running_;\r
+ tbb::concurrent_bounded_queue<gpu_frame_ptr> frame_buffer_;\r
+\r
+ bool needs_clock_;\r
+ std::vector<frame_consumer_ptr> consumers_;\r
+\r
+ frame_format_desc fmt_;\r
+};\r
+\r
+display_device::display_device(const frame_format_desc& format_desc, const std::vector<frame_consumer_ptr>& consumers) : impl_(new implementation(format_desc, consumers)){}\r
+void display_device::display(const gpu_frame_ptr& frame){impl_->display(frame);}\r
+}}}
\ No newline at end of file
--- /dev/null
+#pragma once\r
+\r
+#include "../frame/frame_fwd.h"\r
+#include "../consumer/frame_consumer.h"\r
+\r
+#include <vector>\r
+\r
+namespace caspar { namespace core { namespace renderer {\r
+\r
+class display_device\r
+{\r
+public:\r
+ display_device(const frame_format_desc& format_desc, const std::vector<frame_consumer_ptr>& consumers);\r
+ void display(const gpu_frame_ptr& frame);\r
+private:\r
+ struct implementation;\r
+ std::shared_ptr<implementation> impl_;\r
+};\r
+typedef std::shared_ptr<display_device> display_device_ptr;\r
+\r
+}}}
\ No newline at end of file
#endif\r
\r
#include "render_device.h"\r
-#include "layer.h"\r
\r
-#include "../consumer/frame_consumer.h"\r
+#include "display_device.h"\r
+#include "layer.h"\r
\r
#include "../frame/frame_format.h"\r
#include "../frame/gpu_frame_processor.h"\r
#include "../../common/utility/scope_exit.h"\r
#include "../../common/utility/memory.h"\r
\r
-#include <numeric>\r
-\r
-#include <boost/filesystem.hpp>\r
#include <boost/thread.hpp>\r
-#include <boost/date_time/posix_time/posix_time.hpp>\r
-#include <boost/range/algorithm.hpp>\r
#include <boost/range/algorithm_ext/erase.hpp>\r
-#include <boost/range/sub_range.hpp>\r
-#include <boost/range/adaptor/indirected.hpp>\r
#include <boost/foreach.hpp>\r
\r
#include <tbb/parallel_for.h>\r
#include <tbb/mutex.h>\r
-\r
-using namespace boost::assign;\r
\r
namespace caspar { namespace core { namespace renderer {\r
\r
std::advance(it, r.begin());\r
for(size_t i = r.begin(); i != r.end(); ++i, ++it)\r
frames[i] = it->second.get_frame();\r
- }); \r
+ }); \r
return frames;\r
}\r
\r
struct render_device::implementation : boost::noncopyable\r
{ \r
implementation(const frame_format_desc& format_desc, unsigned int index, const std::vector<frame_consumer_ptr>& consumers) \r
- : consumers_(consumers), fmt_(format_desc), frame_processor_(new gpu_frame_processor(format_desc)), needs_clock_(false)\r
+ : display_device_(new display_device(format_desc, consumers)), fmt_(format_desc), frame_processor_(new gpu_frame_processor(format_desc))\r
{ \r
is_running_ = true;\r
- if(consumers.empty())\r
- BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("consumer") \r
- << msg_info("render_device requires atleast one consumer"));\r
-\r
- if(std::any_of(consumers.begin(), consumers.end(), [&](const frame_consumer_ptr& pConsumer){ return pConsumer->get_frame_format_desc() != format_desc;}))\r
- BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("consumer") \r
- << msg_info("All consumers must have same frameformat as renderdevice."));\r
\r
- needs_clock_ = !std::any_of(consumers.begin(), consumers.end(), std::mem_fn(&frame_consumer::has_sync_clock));\r
-\r
- processor_connection_ = frame_processor_->subscribe([=](const gpu_frame_ptr& frame)\r
- {\r
- frame_buffer_.push(std::move(frame));\r
- });\r
-\r
- frame_buffer_.set_capacity(3);\r
- display_thread_ = boost::thread([=]{display();});\r
- render_thread_ = boost::thread([=]{render();});\r
+ render_thread_ = boost::thread([=]{run();});\r
\r
CASPAR_LOG(info) << L"Initialized render_device with " << format_desc;\r
}\r
\r
~implementation()\r
{\r
- processor_connection_.disconnect();\r
is_running_ = false;\r
- frame_buffer_.clear();\r
- frame_buffer_.push(nullptr);\r
+ display_device_.reset();\r
render_thread_.join();\r
- display_thread_.join();\r
}\r
\r
- void render()\r
+ void run()\r
{ \r
- CASPAR_LOG(info) << L"Started render_device::render thread";\r
+ CASPAR_LOG(info) << L"Started render_device thread";\r
win32_exception::install_handler();\r
\r
while(is_running_)\r
try\r
{ \r
std::vector<gpu_frame_ptr> next_frames;\r
- gpu_frame_ptr composite_frame; \r
-\r
{\r
tbb::mutex::scoped_lock lock(layers_mutex_); \r
next_frames = render_frames(layers_);\r
}\r
frame_processor_->push(next_frames);\r
+ \r
+ gpu_frame_ptr frame; \r
+ frame_processor_->pop(frame);\r
+ display_device_->display(frame);\r
}\r
catch(...)\r
{\r
}\r
}\r
\r
- CASPAR_LOG(info) << L"Ended render_device::render thread";\r
+ CASPAR_LOG(info) << L"Ended render_device thread";\r
}\r
- \r
- struct video_sync_clock\r
- {\r
- video_sync_clock(const frame_format_desc& format_desc)\r
- {\r
- period_ = static_cast<long>(get_frame_format_period(format_desc)*1000000.0);\r
- time_ = boost::posix_time::microsec_clock::local_time();\r
- }\r
\r
- void sync_video()\r
- {\r
- auto remaining = boost::posix_time::microseconds(period_) - (boost::posix_time::microsec_clock::local_time() - time_);\r
- if(remaining > boost::posix_time::microseconds(5000))\r
- boost::this_thread::sleep(remaining - boost::posix_time::microseconds(5000));\r
- time_ = boost::posix_time::microsec_clock::local_time();\r
- }\r
-\r
- boost::posix_time::ptime time_;\r
- long period_;\r
-\r
- };\r
- \r
- void display()\r
- {\r
- CASPAR_LOG(info) << L"Started render_device::display thread";\r
- win32_exception::install_handler();\r
- \r
- gpu_frame_ptr frame = frame_processor_->create_frame(fmt_.width, fmt_.height);\r
- common::clear(frame->data(), frame->size());\r
- std::deque<gpu_frame_ptr> prepared(3, frame);\r
-\r
- video_sync_clock clock(fmt_);\r
- \r
- while(is_running_)\r
- {\r
- if(needs_clock_)\r
- clock.sync_video();\r
-\r
- if(!frame_buffer_.try_pop(frame))\r
- {\r
- CASPAR_LOG(trace) << "Display Buffer Underrun";\r
- frame_buffer_.pop(frame);\r
- }\r
- if(frame != nullptr)\r
- {\r
- display_frame(prepared.front(), frame);\r
- prepared.push_back(frame);\r
- prepared.pop_front();\r
- }\r
- }\r
- \r
- CASPAR_LOG(info) << L"Ended render_device::display thread";\r
- }\r
-\r
- void display_frame(const gpu_frame_ptr& prepared_frame, const gpu_frame_ptr& next_frame)\r
- {\r
- BOOST_FOREACH(const frame_consumer_ptr& consumer, consumers_)\r
- {\r
- try\r
- {\r
- consumer->prepare(next_frame); // Could block\r
- consumer->display(prepared_frame); // Could block\r
- }\r
- catch(...)\r
- {\r
- CASPAR_LOG_CURRENT_EXCEPTION();\r
- boost::range::remove_erase(consumers_, consumer);\r
- CASPAR_LOG(warning) << "Removed consumer from render-device.";\r
- if(consumers_.empty())\r
- {\r
- CASPAR_LOG(warning) << "No consumers available. Shutting down render-device.";\r
- is_running_ = false;\r
- }\r
- }\r
- }\r
- }\r
- \r
void load(int exLayer, const frame_producer_ptr& producer, load_option option)\r
{\r
if(producer->get_frame_format_desc() != fmt_)\r
return it != layers_.end() ? it->second.background() : nullptr;\r
}\r
\r
+ display_device_ptr display_device_;\r
boost::thread render_thread_;\r
- boost::thread display_thread_;\r
\r
frame_format_desc fmt_;\r
- tbb::concurrent_bounded_queue<gpu_frame_ptr> frame_buffer_;\r
- \r
- std::vector<frame_consumer_ptr> consumers_;\r
- \r
+ \r
mutable tbb::mutex layers_mutex_;\r
std::map<int, layer> layers_;\r
\r
tbb::atomic<bool> is_running_; \r
\r
gpu_frame_processor_ptr frame_processor_;\r
-\r
- bool needs_clock_;\r
-\r
- boost::signals2::scoped_connection processor_connection_;\r
};\r
\r
render_device::render_device(const frame_format_desc& format_desc, unsigned int index, const std::vector<frame_consumer_ptr>& consumers) \r