\r
class ogl_device\r
{ \r
-public: \r
ogl_device();\r
+public: \r
+ static safe_ptr<ogl_device> create()\r
+ {\r
+ static safe_ptr<ogl_device> instance(new ogl_device());\r
+ return instance;\r
+ }\r
\r
template<typename Func>\r
auto begin_invoke(Func&& func) -> boost::unique_future<decltype(func())> // noexcept\r
\r
image_kernel kernel_;\r
\r
- ogl_device context_;\r
+ safe_ptr<ogl_device> context_;\r
\r
public:\r
implementation(const video_format_desc& format_desc) \r
: format_desc_(format_desc)\r
+ , context_(ogl_device::create())\r
{\r
- context_.begin_invoke([=]\r
+ context_->begin_invoke([=]\r
{\r
transform_stack_.push(image_transform());\r
transform_stack_.top().set_mode(video_mode::progressive);\r
GL(glEnable(GL_TEXTURE_2D));\r
GL(glDisable(GL_DEPTH_TEST)); \r
\r
- render_targets_[0] = context_.create_device_buffer(format_desc.width, format_desc.height, 4);\r
- render_targets_[1] = context_.create_device_buffer(format_desc.width, format_desc.height, 4);\r
+ render_targets_[0] = context_->create_device_buffer(format_desc.width, format_desc.height, 4);\r
+ render_targets_[1] = context_->create_device_buffer(format_desc.width, format_desc.height, 4);\r
\r
GL(glGenFramebuffers(1, &fbo_)); \r
GL(glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo_));\r
GL(glReadBuffer(GL_COLOR_ATTACHMENT0_EXT));\r
\r
- reading_ = context_.create_host_buffer(format_desc_.size, host_buffer::read_only);\r
+ reading_ = context_->create_host_buffer(format_desc_.size, host_buffer::read_only);\r
});\r
}\r
\r
void render(const pixel_format_desc& desc, std::vector<safe_ptr<host_buffer>>& buffers)\r
{\r
auto transform = transform_stack_.top();\r
- context_.begin_invoke([=]\r
+ context_->begin_invoke([=]\r
{\r
GL(glLoadIdentity());\r
GL(glTranslated(transform.get_position()[0]*2.0, transform.get_position()[1]*2.0, 0.0));\r
std::vector<safe_ptr<device_buffer>> device_buffers;\r
for(size_t n = 0; n < buffers.size(); ++n)\r
{\r
- auto texture = context_.create_device_buffer(desc.planes[n].width, desc.planes[n].height, desc.planes[n].channels);\r
+ auto texture = context_->create_device_buffer(desc.planes[n].width, desc.planes[n].height, desc.planes[n].channels);\r
texture->read(*buffers[n]);\r
device_buffers.push_back(texture);\r
}\r
\r
boost::unique_future<safe_ptr<const host_buffer>> begin_pass()\r
{\r
- return context_.begin_invoke([=]() -> safe_ptr<const host_buffer>\r
+ return context_->begin_invoke([=]() -> safe_ptr<const host_buffer>\r
{\r
reading_->map();\r
render_targets_[0]->attach(0);\r
\r
void end_pass()\r
{\r
- context_.begin_invoke([=]\r
+ context_->begin_invoke([=]\r
{\r
- reading_ = context_.create_host_buffer(format_desc_.size, host_buffer::read_only);\r
+ reading_ = context_->create_host_buffer(format_desc_.size, host_buffer::read_only);\r
render_targets_[0]->write(*reading_);\r
std::rotate(render_targets_.begin(), render_targets_.begin() + 1, render_targets_.end());\r
});\r
std::vector<safe_ptr<host_buffer>> buffers;\r
std::transform(format.planes.begin(), format.planes.end(), std::back_inserter(buffers), [&](const pixel_format_desc::plane& plane) -> safe_ptr<host_buffer>\r
{\r
- return context_.create_host_buffer(plane.size, host_buffer::write_only);\r
+ return context_->create_host_buffer(plane.size, host_buffer::write_only);\r
});\r
return buffers;\r
}\r
#pragma once\r
\r
#include "../frame_producer.h"\r
+#include "../frame_producer_device.h"\r
#include "../../video_format.h"\r
#include "../../channel.h"\r
\r
class cg_producer : public frame_producer\r
{\r
public:\r
- static const unsigned int DEFAULT_LAYER = 5000;\r
+ static const unsigned int DEFAULT_LAYER = frame_producer_device::MAX_LAYER;\r
\r
explicit cg_producer();\r
cg_producer(cg_producer&& other);\r
#include <common/concurrency/executor.h>\r
\r
#include <boost/range/algorithm_ext/erase.hpp>\r
+#include <boost/lexical_cast.hpp>\r
\r
#include <tbb/parallel_for.h>\r
\r
-#include <map>\r
+#include <array>\r
#include <memory>\r
\r
namespace caspar { namespace core {\r
\r
struct frame_producer_device::implementation : boost::noncopyable\r
{ \r
- std::map<int, layer> layers_; \r
+ std::array<layer, frame_producer_device::MAX_LAYER> layers_; \r
\r
const output_func output_;\r
const safe_ptr<frame_factory> factory_;\r
tbb::parallel_for(tbb::blocked_range<size_t>(0, frames.size()), \r
[&](const tbb::blocked_range<size_t>& r)\r
{\r
- auto it = layers_.begin();\r
- std::advance(it, r.begin());\r
- for(size_t i = r.begin(); i != r.end(); ++i, ++it)\r
- frames[i] = it->second.receive();\r
+ for(size_t i = r.begin(); i != r.end(); ++i)\r
+ frames[i] = layers_[i].receive();\r
}); \r
boost::range::remove_erase(frames, draw_frame::eof());\r
boost::range::remove_erase(frames, draw_frame::empty());\r
return frames;\r
}\r
\r
- void load(int index, const safe_ptr<frame_producer>& producer, bool play_on_load)\r
+ void load(size_t index, const safe_ptr<frame_producer>& producer, bool play_on_load)\r
{\r
+ check_bounds(index);\r
producer->initialize(factory_);\r
- executor_.begin_invoke([=]\r
+ executor_.invoke([&]\r
{\r
- auto it = layers_.insert(std::make_pair(index, layer(index))).first;\r
- it->second.load(producer, play_on_load);\r
+ layers_[index] = layer(index);\r
+ layers_[index].load(producer, play_on_load);\r
});\r
}\r
\r
- void preview(int index, const safe_ptr<frame_producer>& producer)\r
+ void preview(size_t index, const safe_ptr<frame_producer>& producer)\r
{\r
+ check_bounds(index);\r
producer->initialize(factory_);\r
- executor_.begin_invoke([=]\r
- {\r
- auto it = layers_.insert(std::make_pair(index, layer(index))).first;\r
- it->second.preview(producer);\r
+ executor_.invoke([&]\r
+ { \r
+ layers_[index] = layer(index);\r
+ layers_[index].preview(producer);\r
});\r
}\r
\r
- void pause(int index)\r
+ void pause(size_t index)\r
{ \r
- begin_invoke_layer(index, std::mem_fn(&layer::pause));\r
+ check_bounds(index);\r
+ executor_.invoke([&]\r
+ {\r
+ layers_[index].pause();\r
+ });\r
}\r
\r
- void play(int index)\r
+ void play(size_t index)\r
{ \r
- begin_invoke_layer(index, std::mem_fn(&layer::play));\r
+ check_bounds(index);\r
+ executor_.invoke([&]\r
+ {\r
+ layers_[index].play();\r
+ });\r
}\r
\r
- void stop(int index)\r
+ void stop(size_t index)\r
{ \r
- begin_invoke_layer(index, std::mem_fn(&layer::stop));\r
+ check_bounds(index);\r
+ executor_.invoke([&]\r
+ {\r
+ layers_[index].stop();\r
+ });\r
}\r
\r
- void clear(int index)\r
+ void clear(size_t index)\r
{\r
- executor_.begin_invoke([=]\r
- { \r
- auto it = layers_.find(index);\r
- if(it != layers_.end())\r
- {\r
- it->second.clear(); \r
- layers_.erase(it);\r
- }\r
+ check_bounds(index);\r
+ executor_.invoke([&]\r
+ {\r
+ layers_[index] = std::move(layer());\r
});\r
}\r
\r
void clear()\r
{\r
- executor_.begin_invoke([=]\r
- { \r
- layers_.clear();\r
+ executor_.invoke([&]\r
+ {\r
+ for(auto it = layers_.begin(); it != layers_.end(); ++it)\r
+ *it = std::move(layer());\r
});\r
- } \r
+ } \r
+ \r
+ void swap(size_t index, size_t other_index)\r
+ {\r
+ check_bounds(index);\r
+ check_bounds(other_index);\r
\r
- template<typename F>\r
- void begin_invoke_layer(int index, F&& func)\r
+ executor_.invoke([&]\r
+ {\r
+ layers_[index].swap(layers_[other_index]);\r
+ });\r
+ }\r
+\r
+ void swap(size_t index, size_t other_index, frame_producer_device& other)\r
{\r
- executor_.begin_invoke([=]\r
+ check_bounds(index);\r
+ check_bounds(other_index);\r
+\r
+ executor_.invoke([&]\r
{\r
- auto it = layers_.find(index);\r
- if(it != layers_.end())\r
- func(it->second); \r
+ layers_[index].swap(other.impl_->layers_[other_index]);\r
});\r
}\r
\r
- boost::unique_future<safe_ptr<frame_producer>> foreground(int index) const\r
+ void check_bounds(size_t index)\r
+ {\r
+ if(index < 0 || index >= frame_producer_device::MAX_LAYER)\r
+ BOOST_THROW_EXCEPTION(out_of_range() << msg_info("Valid range is [0..100]") << arg_name_info("index") << arg_value_info(boost::lexical_cast<std::string>(index)));\r
+ }\r
+ \r
+ boost::unique_future<safe_ptr<frame_producer>> foreground(size_t index) const\r
{\r
return executor_.begin_invoke([=]() -> safe_ptr<frame_producer>\r
{ \r
- auto it = layers_.find(index);\r
- return it != layers_.end() ? it->second.foreground() : frame_producer::empty();\r
+ return layers_[index].foreground();\r
});\r
}\r
};\r
\r
frame_producer_device::frame_producer_device(const safe_ptr<frame_factory>& factory, const output_func& output) : impl_(new implementation(factory, output)){}\r
frame_producer_device::frame_producer_device(frame_producer_device&& other) : impl_(std::move(other.impl_)){}\r
-void frame_producer_device::load(int index, const safe_ptr<frame_producer>& producer, bool play_on_load){impl_->load(index, producer, play_on_load);}\r
-void frame_producer_device::preview(int index, const safe_ptr<frame_producer>& producer){impl_->preview(index, producer);}\r
-void frame_producer_device::pause(int index){impl_->pause(index);}\r
-void frame_producer_device::play(int index){impl_->play(index);}\r
-void frame_producer_device::stop(int index){impl_->stop(index);}\r
-void frame_producer_device::clear(int index){impl_->clear(index);}\r
+void frame_producer_device::load(size_t index, const safe_ptr<frame_producer>& producer, bool play_on_load){impl_->load(index, producer, play_on_load);}\r
+void frame_producer_device::preview(size_t index, const safe_ptr<frame_producer>& producer){impl_->preview(index, producer);}\r
+void frame_producer_device::pause(size_t index){impl_->pause(index);}\r
+void frame_producer_device::play(size_t index){impl_->play(index);}\r
+void frame_producer_device::stop(size_t index){impl_->stop(index);}\r
+void frame_producer_device::clear(size_t index){impl_->clear(index);}\r
void frame_producer_device::clear(){impl_->clear();}\r
-boost::unique_future<safe_ptr<frame_producer>> frame_producer_device::foreground(int index) const{ return impl_->foreground(index);}\r
+void frame_producer_device::swap(size_t index, size_t other_index){impl_->swap(index, other_index);}\r
+void frame_producer_device::swap(size_t index, size_t other_index, frame_producer_device& other){impl_->swap(index, other_index, other);}\r
+boost::unique_future<safe_ptr<frame_producer>> frame_producer_device::foreground(size_t index) const{ return impl_->foreground(index);}\r
}}
\ No newline at end of file
class frame_producer_device : boost::noncopyable\r
{\r
public:\r
+ static const int MAX_LAYER = 101;\r
+\r
typedef std::function<void(const std::vector<safe_ptr<draw_frame>>&)> output_func;\r
\r
explicit frame_producer_device(const safe_ptr<frame_factory>& factory, const output_func& output);\r
frame_producer_device(frame_producer_device&& other);\r
\r
- void load(int index, const safe_ptr<frame_producer>& producer, bool play_on_load = false);\r
- void preview(int index, const safe_ptr<frame_producer>& producer);\r
- void pause(int index);\r
- void play(int index);\r
- void stop(int index);\r
- void clear(int index);\r
+ void load(size_t index, const safe_ptr<frame_producer>& producer, bool play_on_load = false);\r
+ void preview(size_t index, const safe_ptr<frame_producer>& producer);\r
+ void pause(size_t index);\r
+ void play(size_t index);\r
+ void stop(size_t index);\r
+ void clear(size_t index);\r
void clear(); \r
- boost::unique_future<safe_ptr<frame_producer>> foreground(int index) const;\r
+ void swap(size_t index, size_t other_index);\r
+ void swap(size_t index, size_t other_index, frame_producer_device& other);\r
+ boost::unique_future<safe_ptr<frame_producer>> foreground(size_t index) const;\r
\r
private:\r
struct implementation;\r
std::wstring print() const { return L"layer[" + boost::lexical_cast<std::wstring>(index_) + L"]"; }\r
};\r
\r
-layer::layer(int index) : impl_(new implementation(index)){}\r
+layer::layer(int index) \r
+{\r
+ impl_ = new implementation(index);\r
+}\r
layer::layer(layer&& other) : impl_(std::move(other.impl_)){other.impl_ = nullptr;}\r
+layer::~layer()\r
+{\r
+ delete impl_.fetch_and_store(nullptr);\r
+}\r
layer& layer::operator=(layer&& other)\r
{\r
impl_ = std::move(other.impl_); \r
other.impl_ = nullptr;\r
return *this;\r
}\r
+void layer::swap(layer& other)\r
+{\r
+ impl_ = other.impl_.compare_and_swap(impl_, other.impl_);\r
+}\r
void layer::load(const safe_ptr<frame_producer>& frame_producer, bool play_on_load){return impl_->load(frame_producer, play_on_load);} \r
void layer::preview(const safe_ptr<frame_producer>& frame_producer){return impl_->preview(frame_producer);} \r
void layer::play(){impl_->play();}\r
\r
#include <boost/noncopyable.hpp>\r
\r
+#include <tbb/atomic.h>\r
+\r
namespace caspar { namespace core {\r
\r
class frame_producer;\r
public:\r
layer(int index = -1); // nothrow\r
layer(layer&& other); // nothrow\r
+ ~layer();\r
layer& operator=(layer&& other); // nothrow\r
+\r
+ //NOTE: swap is thread-safe on "other", NOT on "this".\r
+ void swap(layer& other); // nothrow \r
\r
void load(const safe_ptr<frame_producer>& producer, bool play_on_load = false); // nothrow\r
void preview(const safe_ptr<frame_producer>& producer); // nothrow\r
safe_ptr<draw_frame> receive(); // nothrow\r
private:\r
struct implementation;\r
- std::shared_ptr<implementation> impl_;\r
+ tbb::atomic<implementation*> impl_;\r
};\r
\r
}}
\ No newline at end of file
//Perform loading of the clip\r
try\r
{\r
- //std::vector<std::string> strs;\r
- //boost::split(strs, _parameters[0], boost::is_any_of("-"));\r
- // \r
- //auto ch1 = GetChannel();\r
- //auto ch2 = GetChannels().at(boost::lexical_cast<int>(strs.at(0))-1);\r
-\r
- //int l1 = GetLayerIndex();\r
- //int l2 = boost::lexical_cast<int>(strs.at(1));\r
- //\r
- //auto c1 = ch1->consumer().get(l1);\r
- //auto c2 = ch2->consumer().get(l2);\r
-\r
- //ch2->consumer().add(l1, c1);\r
- //ch1->consumer().add(l2, c2);\r
- //CASPAR_LOG(info) << "Swapped successfully";\r
-\r
- SetReplyString(TEXT("600 SWAP NOT IMPLEMENTED\r\n"));\r
+ std::vector<std::string> strs;\r
+ boost::split(strs, _parameters[0], boost::is_any_of("-"));\r
+ \r
+ auto ch1 = GetChannel();\r
+ auto ch2 = GetChannels().at(boost::lexical_cast<int>(strs.at(0))-1);\r
+\r
+ int l1 = GetLayerIndex();\r
+ int l2 = boost::lexical_cast<int>(strs.at(1));\r
+\r
+ ch1->producer().swap(l1, l2, ch2->producer());\r
+\r
+ CASPAR_LOG(info) << "Swapped successfully";\r
+\r
+ SetReplyString(TEXT("202 SWAP OK\r\n"));\r
\r
return false;\r
}\r