\r
#include <boost/noncopyable.hpp>\r
\r
-namespace caspar { \r
+namespace caspar { namespace core {\r
\r
-class executor;\r
- \r
-namespace core {\r
- \r
-class video_channel_context;;\r
+class video_channel_context;\r
\r
class output : boost::noncopyable\r
{\r
struct image_kernel::implementation : boost::noncopyable\r
{ \r
std::unique_ptr<shader> shader_;\r
+\r
+ std::string vertex_;\r
+ std::string fragment_;\r
\r
implementation()\r
{\r
- std::string vertex = \r
+ vertex_ = \r
"void main() \n"\r
"{ \n"\r
" gl_TexCoord[0] = gl_MultiTexCoord0; \n"\r
" gl_Position = ftransform(); \n"\r
"} \n";\r
\r
- std::string fragment = std::string() +\r
+ fragment_ =\r
"#version 120 \n"\r
"uniform sampler2D background; \n"\r
"uniform sampler2D plane[4]; \n"\r
" if(has_layer_key) \n"\r
" color.a *= texture2D(layer_key, gl_TexCoord[1].st).r; \n"\r
" gl_FragColor = blend_color(color.bgra * gl_Color); \n"\r
- "} \n"\r
- ;\r
-\r
- shader_.reset(new shader(vertex, fragment));\r
+ "} \n";\r
}\r
\r
\r
- void draw(size_t width, \r
- size_t height, \r
- const core::pixel_format_desc& pix_desc, \r
- const core::image_transform& transform,\r
- core::video_mode::type mode, \r
- const std::vector<safe_ptr<device_buffer>>& planes, \r
+ void draw(const render_item& item,\r
const safe_ptr<device_buffer>& background,\r
const std::shared_ptr<device_buffer>& local_key, \r
const std::shared_ptr<device_buffer>& layer_key)\r
{\r
- if(planes.empty())\r
+ static const double epsilon = 0.001;\r
+\r
+ CASPAR_ASSERT(item.pix_desc.planes.size() == item.textures.size());\r
+\r
+ if(item.textures.empty())\r
return;\r
\r
+ if(item.transform.get_opacity() < epsilon)\r
+ return;\r
+\r
+ if(!shader_)\r
+ shader_.reset(new shader(vertex_, fragment_));\r
+\r
GL(glEnable(GL_TEXTURE_2D));\r
GL(glEnable(GL_POLYGON_STIPPLE));\r
\r
- if(mode == core::video_mode::upper)\r
+ if(item.mode == core::video_mode::upper)\r
glPolygonStipple(upper_pattern);\r
- else if(mode == core::video_mode::lower)\r
+ else if(item.mode == core::video_mode::lower)\r
glPolygonStipple(lower_pattern);\r
else\r
GL(glDisable(GL_POLYGON_STIPPLE));\r
\r
// Bind textures\r
\r
- for(size_t n = 0; n < planes.size(); ++n)\r
- planes[n]->bind(n);\r
+ for(size_t n = 0; n < item.textures.size(); ++n)\r
+ item.textures[n]->bind(n);\r
\r
if(local_key)\r
local_key->bind(4);\r
shader_->set("local_key", 4);\r
shader_->set("layer_key", 5);\r
shader_->set("background", 6);\r
- shader_->set("is_hd", pix_desc.planes.at(0).height > 700 ? 1 : 0);\r
+ shader_->set("is_hd", item.pix_desc.planes.at(0).height > 700 ? 1 : 0);\r
shader_->set("has_local_key", local_key ? 1 : 0);\r
shader_->set("has_layer_key", layer_key ? 1 : 0);\r
- shader_->set("blend_mode", transform.get_is_key() ? core::image_transform::blend_mode::normal : transform.get_blend_mode());\r
- shader_->set("alpha_mode", transform.get_alpha_mode());\r
- shader_->set("pixel_format", pix_desc.pix_fmt); \r
+ shader_->set("blend_mode", item.transform.get_is_key() ? core::image_transform::blend_mode::normal : item.transform.get_blend_mode());\r
+ shader_->set("alpha_mode", item.transform.get_alpha_mode());\r
+ shader_->set("pixel_format", item.pix_desc.pix_fmt); \r
\r
- auto levels = transform.get_levels();\r
+ auto levels = item.transform.get_levels();\r
\r
- if(levels.min_input > 0.001 ||\r
- levels.max_input < 0.999 ||\r
- levels.min_output > 0.001 ||\r
- levels.max_output < 0.999 ||\r
- std::abs(levels.gamma - 1.0) > 0.001)\r
+ if(levels.min_input > epsilon ||\r
+ levels.max_input < 1.0-epsilon ||\r
+ levels.min_output > epsilon ||\r
+ levels.max_output < 1.0-epsilon ||\r
+ std::abs(levels.gamma - 1.0) > epsilon)\r
{\r
shader_->set("levels", true); \r
shader_->set("min_input", levels.min_input); \r
else\r
shader_->set("levels", false); \r
\r
- if(std::abs(transform.get_brightness() - 1.0) > 0.001 ||\r
- std::abs(transform.get_saturation() - 1.0) > 0.001 ||\r
- std::abs(transform.get_contrast() - 1.0) > 0.001)\r
+ if(std::abs(item.transform.get_brightness() - 1.0) > epsilon ||\r
+ std::abs(item.transform.get_saturation() - 1.0) > epsilon ||\r
+ std::abs(item.transform.get_contrast() - 1.0) > epsilon)\r
{\r
shader_->set("csb", true); \r
\r
- shader_->set("brt", transform.get_brightness()); \r
- shader_->set("sat", transform.get_saturation());\r
- shader_->set("con", transform.get_contrast());\r
+ shader_->set("brt", item.transform.get_brightness()); \r
+ shader_->set("sat", item.transform.get_saturation());\r
+ shader_->set("con", item.transform.get_contrast());\r
}\r
else\r
shader_->set("csb", false); \r
\r
// Setup drawing area\r
\r
- GL(glColor4d(transform.get_gain(), transform.get_gain(), transform.get_gain(), transform.get_opacity()));\r
- GL(glViewport(0, 0, width, height));\r
+ GL(glColor4d(item.transform.get_gain(), item.transform.get_gain(), item.transform.get_gain(), item.transform.get_opacity()));\r
+ GL(glViewport(0, 0, background->width(), background->height()));\r
\r
- auto m_p = transform.get_clip_translation();\r
- auto m_s = transform.get_clip_scale();\r
- double w = static_cast<double>(width);\r
- double h = static_cast<double>(height);\r
+ auto m_p = item.transform.get_clip_translation();\r
+ auto m_s = item.transform.get_clip_scale();\r
+ double w = static_cast<double>(background->width());\r
+ double h = static_cast<double>(background->height());\r
\r
GL(glEnable(GL_SCISSOR_TEST));\r
GL(glScissor(static_cast<size_t>(m_p[0]*w), static_cast<size_t>(m_p[1]*h), static_cast<size_t>(m_s[0]*w), static_cast<size_t>(m_s[1]*h)));\r
\r
- auto f_p = transform.get_fill_translation();\r
- auto f_s = transform.get_fill_scale();\r
+ auto f_p = item.transform.get_fill_translation();\r
+ auto f_s = item.transform.get_fill_scale();\r
\r
// Draw\r
\r
};\r
\r
image_kernel::image_kernel() : impl_(new implementation()){}\r
+void image_kernel::draw(const render_item& item, const safe_ptr<device_buffer>& background, const std::shared_ptr<device_buffer>& local_key, const std::shared_ptr<device_buffer>& layer_key)\r
+{\r
+ impl_->draw(item, background, local_key, layer_key);\r
+}\r
\r
-void image_kernel::draw(size_t width, size_t height, const core::pixel_format_desc& pix_desc, const core::image_transform& transform, core::video_mode::type mode, const std::vector<safe_ptr<device_buffer>>& planes, \r
- const safe_ptr<device_buffer>& background, const std::shared_ptr<device_buffer>& local_key, const std::shared_ptr<device_buffer>& layer_key)\r
+bool operator==(const render_item& lhs, const render_item& rhs)\r
{\r
- impl_->draw(width, height, pix_desc, transform, mode, planes, background, local_key, layer_key);\r
+ return lhs.textures == rhs.textures && lhs.transform == rhs.transform && lhs.tag == rhs.tag && lhs.mode == rhs.mode;\r
}\r
\r
}}
\ No newline at end of file
#include <core/producer/frame/pixel_format.h>\r
#include <core/producer/frame/image_transform.h>\r
\r
+#include <boost/noncopyable.hpp>\r
+\r
namespace caspar { namespace core {\r
\r
class device_buffer;\r
\r
-class image_kernel\r
+struct render_item\r
+{\r
+ pixel_format_desc pix_desc;\r
+ std::vector<safe_ptr<device_buffer>> textures;\r
+ image_transform transform;\r
+ video_mode::type mode;\r
+ const void* tag;\r
+};\r
+\r
+bool operator==(const render_item& lhs, const render_item& rhs);\r
+\r
+class image_kernel : boost::noncopyable\r
{\r
public:\r
image_kernel();\r
- void draw(size_t width, size_t height, const core::pixel_format_desc& pix_desc, const core::image_transform& transform, core::video_mode::type mode, const std::vector<safe_ptr<device_buffer>>& planes, \r
- const safe_ptr<device_buffer>& background, const std::shared_ptr<device_buffer>& local_key = nullptr, const std::shared_ptr<device_buffer>& layer_key = nullptr);\r
+ void draw(const render_item& item, const safe_ptr<device_buffer>& background, const std::shared_ptr<device_buffer>& local_key = nullptr, const std::shared_ptr<device_buffer>& layer_key = nullptr);\r
private:\r
struct implementation;\r
safe_ptr<implementation> impl_;\r
\r
namespace caspar { namespace core {\r
\r
-struct render_item\r
-{\r
- pixel_format_desc desc;\r
- std::vector<safe_ptr<device_buffer>> textures;\r
- image_transform transform;\r
- video_mode::type mode;\r
- const void* tag;\r
-};\r
-\r
-bool operator==(const render_item& lhs, const render_item& rhs)\r
-{\r
- return lhs.textures == rhs.textures && lhs.transform == rhs.transform && lhs.tag == rhs.tag && lhs.mode == rhs.mode;\r
-}\r
-\r
struct image_mixer::implementation : boost::noncopyable\r
{ \r
- typedef std::deque<render_item> layer;\r
+ typedef std::deque<render_item> layer;\r
\r
- video_channel_context& channel_;\r
+ video_channel_context& channel_;\r
\r
- std::stack<image_transform> transform_stack_;\r
- std::deque<video_mode::type> mode_stack_;\r
+ std::vector<image_transform> transform_stack_;\r
+ std::vector<video_mode::type> mode_stack_;\r
\r
- std::queue<layer> layers_; // layer/stream/items\r
+ std::queue<std::deque<render_item>> layers_; // layer/stream/items\r
\r
- std::unique_ptr<image_kernel> kernel_;\r
+ image_kernel kernel_;\r
\r
- std::array<std::shared_ptr<device_buffer>, 2> draw_buffer_;\r
- std::shared_ptr<device_buffer> write_buffer_;\r
+ std::array<std::shared_ptr<device_buffer>,2> draw_buffer_;\r
+ std::shared_ptr<device_buffer> write_buffer_;\r
\r
- std::array<std::shared_ptr<device_buffer>, 2> stream_key_buffer_;\r
- std::shared_ptr<device_buffer> layer_key_buffer_;\r
+ std::array<std::shared_ptr<device_buffer>,2> stream_key_buffer_;\r
+ std::shared_ptr<device_buffer> layer_key_buffer_;\r
\r
public:\r
implementation(video_channel_context& video_channel) \r
: channel_(video_channel)\r
{\r
initialize_buffers();\r
- transform_stack_.push(image_transform());\r
+ transform_stack_.push_back(image_transform());\r
mode_stack_.push_back(video_mode::progressive);\r
\r
channel_.ogl().invoke([=]\r
{\r
if(!GLEE_VERSION_3_0)\r
CASPAR_LOG(warning) << "Missing OpenGL 3.0 support.";//BOOST_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 3.0 support."));\r
-\r
- kernel_.reset(new image_kernel());\r
});\r
}\r
\r
draw_buffer_[1] = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 4);\r
stream_key_buffer_[0] = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 1);\r
stream_key_buffer_[1] = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 1);\r
+ channel_.ogl().gc();\r
}\r
\r
void begin(core::basic_frame& frame)\r
{\r
- transform_stack_.push(transform_stack_.top()*frame.get_image_transform());\r
+ transform_stack_.push_back(transform_stack_.back()*frame.get_image_transform());\r
mode_stack_.push_back(frame.get_mode() == video_mode::progressive ? mode_stack_.back() : frame.get_mode());\r
}\r
\r
if(boost::range::find(mode_stack_, video_mode::upper) != mode_stack_.end() && boost::range::find(mode_stack_, video_mode::lower) != mode_stack_.end())\r
return;\r
\r
- core::render_item item = {frame.get_pixel_format_desc(), frame.get_textures(), transform_stack_.top(), mode_stack_.back(), frame.tag()}; \r
+ core::render_item item = {frame.get_pixel_format_desc(), frame.get_textures(), transform_stack_.back(), mode_stack_.back(), frame.tag()}; \r
\r
auto& layer = layers_.back();\r
\r
\r
void end()\r
{\r
- transform_stack_.pop();\r
+ transform_stack_.pop_back();\r
mode_stack_.pop_back();\r
}\r
\r
\r
if(item.transform.get_is_key())\r
{\r
- render_item(stream_key_buffer_, item, nullptr, nullptr);\r
+ render_item(stream_key_buffer_, std::move(item), nullptr, nullptr);\r
local_key = true;\r
}\r
else\r
{\r
- render_item(draw_buffer_, item, local_key ? stream_key_buffer_[0] : nullptr, layer_key ? layer_key_buffer_ : nullptr); \r
+ render_item(draw_buffer_, std::move(item), local_key ? stream_key_buffer_[0] : nullptr, layer_key ? layer_key_buffer_ : nullptr); \r
stream_key_buffer_[0]->clear();\r
local_key = false;\r
}\r
-\r
- channel_.ogl().yield();\r
+ channel_.ogl().yield(); // Return resources to pool as early as possible.\r
}\r
\r
layer_key = local_key;\r
\r
std::swap(draw_buffer_[0], write_buffer_);\r
\r
- // Start transfer from device to host. \r
+ // device -> host. \r
write_buffer_->write(*read_buffer);\r
\r
return read_buffer;\r
}\r
\r
- void render_item(std::array<std::shared_ptr<device_buffer>,2>& targets, render_item& item, const std::shared_ptr<device_buffer>& local_key, const std::shared_ptr<device_buffer>& layer_key)\r
+ void render_item(std::array<std::shared_ptr<device_buffer>,2>& targets, render_item&& item, const std::shared_ptr<device_buffer>& local_key, const std::shared_ptr<device_buffer>& layer_key)\r
{\r
targets[1]->attach();\r
\r
- kernel_->draw(channel_.get_format_desc().width, channel_.get_format_desc().height, item.desc, item.transform, item.mode, item.textures, make_safe(targets[0]), local_key, layer_key);\r
+ kernel_.draw(item, make_safe(targets[0]), local_key, layer_key);\r
\r
targets[0]->bind();\r
\r
- glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, channel_.get_format_desc().width, channel_.get_format_desc().height);\r
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, targets[0]->width(), targets[0]->height());\r
\r
std::swap(targets[0], targets[1]);\r
}\r
write_frame::write_frame(ogl_device& ogl, const void* tag, const core::pixel_format_desc& desc) \r
: impl_(new implementation(ogl, tag, desc)){}\r
write_frame::write_frame(const write_frame& other) : impl_(new implementation(*other.impl_)){}\r
-void write_frame::accept(core::frame_visitor& visitor){impl_->accept(*this, visitor);}\r
+write_frame::write_frame(write_frame&& other) : impl_(std::move(*other.impl_)){}\r
+write_frame& write_frame::operator=(const write_frame& other)\r
+{\r
+ basic_frame temp(other);\r
+ temp.swap(*this);\r
+ return *this;\r
+}\r
+write_frame& write_frame::operator=(write_frame&& other)\r
+{\r
+ write_frame temp(std::move(other));\r
+ temp.swap(*this);\r
+ return *this;\r
+}\r
+void write_frame::swap(write_frame& other){impl_.swap(other.impl_);}\r
\r
boost::iterator_range<uint8_t*> write_frame::image_data(size_t index){return impl_->image_data(index);}\r
std::vector<int16_t>& write_frame::audio_data() { return impl_->audio_data_; }\r
void write_frame::commit(){impl_->commit();}\r
void write_frame::set_type(const video_mode::type& mode){impl_->mode_ = mode;}\r
core::video_mode::type write_frame::get_type() const{return impl_->mode_;}\r
+\r
std::wstring write_frame::print() const{return impl_->print();}\r
+void write_frame::accept(core::frame_visitor& visitor){impl_->accept(*this, visitor);}\r
\r
}}
\ No newline at end of file
\r
namespace caspar { namespace core {\r
\r
-class host_buffer;\r
class device_buffer;\r
struct frame_visitor;\r
struct pixel_format_desc;\r
class write_frame : public core::basic_frame, boost::noncopyable\r
{\r
public: \r
- write_frame(const void* tag);\r
+ explicit write_frame(const void* tag);\r
explicit write_frame(ogl_device& ogl, const void* tag, const core::pixel_format_desc& desc);\r
+\r
write_frame(const write_frame& other);\r
+ write_frame(write_frame&& other);\r
+\r
+ write_frame& operator=(const write_frame& other);\r
+ write_frame& operator=(write_frame&& other);\r
+ \r
+ void swap(write_frame& other);\r
\r
- virtual boost::iterator_range<uint8_t*> image_data(size_t plane_index = 0); \r
- virtual std::vector<int16_t>& audio_data();\r
- \r
- virtual const boost::iterator_range<const uint8_t*> image_data(size_t plane_index = 0) const;\r
- virtual const boost::iterator_range<const int16_t*> audio_data() const;\r
+ boost::iterator_range<uint8_t*> image_data(size_t plane_index = 0); \r
+ const boost::iterator_range<const uint8_t*> image_data(size_t plane_index = 0) const;\r
\r
+ std::vector<int16_t>& audio_data();\r
+ const boost::iterator_range<const int16_t*> audio_data() const;\r
+ \r
void commit(uint32_t plane_index);\r
void commit();\r
\r
void set_type(const core::video_mode::type& mode);\r
core::video_mode::type get_type() const;\r
\r
- virtual void accept(core::frame_visitor& visitor);\r
-\r
- virtual const void* tag() const;\r
+ const void* tag() const;\r
\r
const core::pixel_format_desc& get_pixel_format_desc() const;\r
+ \r
+ // basic_frame\r
+\r
+ virtual void accept(core::frame_visitor& visitor);\r
\r
virtual std::wstring print() const;\r
\r
basic_frame::basic_frame(std::vector<safe_ptr<basic_frame>>&& frames) : impl_(new implementation(frames)){}\r
basic_frame::basic_frame(const safe_ptr<basic_frame>& frame) : impl_(new implementation(frame)){}\r
basic_frame::basic_frame(safe_ptr<basic_frame>&& frame) : impl_(new implementation(std::move(frame))){}\r
-core::video_mode::type basic_frame::get_mode() const{return impl_->mode_;}\r
-void basic_frame::swap(basic_frame& other){impl_.swap(other.impl_);}\r
+basic_frame::basic_frame(basic_frame&& other) : impl_(std::move(other.impl_)){}\r
basic_frame& basic_frame::operator=(const basic_frame& other)\r
{\r
basic_frame temp(other);\r
temp.swap(*this);\r
return *this;\r
}\r
-basic_frame::basic_frame(basic_frame&& other) : impl_(std::move(other.impl_)){}\r
basic_frame& basic_frame::operator=(basic_frame&& other)\r
{\r
basic_frame temp(std::move(other));\r
temp.swap(*this);\r
return *this;\r
}\r
-void basic_frame::accept(frame_visitor& visitor){impl_->accept(*this, visitor);}\r
+void basic_frame::swap(basic_frame& other){impl_.swap(other.impl_);}\r
\r
+core::video_mode::type basic_frame::get_mode() const{return impl_->mode_;}\r
const image_transform& basic_frame::get_image_transform() const { return impl_->image_transform_;}\r
image_transform& basic_frame::get_image_transform() { return impl_->image_transform_;}\r
const audio_transform& basic_frame::get_audio_transform() const { return impl_->audio_transform_;}\r
audio_transform& basic_frame::get_audio_transform() { return impl_->audio_transform_;}\r
\r
std::wstring basic_frame::print() const{return impl_->print();}\r
+void basic_frame::accept(frame_visitor& visitor){impl_->accept(*this, visitor);}\r
\r
safe_ptr<basic_frame> basic_frame::interlace(const safe_ptr<basic_frame>& frame1, const safe_ptr<basic_frame>& frame2, video_mode::type mode)\r
{ \r
basic_frame(const safe_ptr<basic_frame>& frame);\r
basic_frame(safe_ptr<basic_frame>&& frame);\r
basic_frame(const std::vector<safe_ptr<basic_frame>>& frames);\r
-\r
- void swap(basic_frame& other);\r
- \r
basic_frame(const basic_frame& other);\r
basic_frame(basic_frame&& other);\r
\r
basic_frame& operator=(const basic_frame& other);\r
basic_frame& operator=(basic_frame&& other);\r
\r
+ void swap(basic_frame& other);\r
+\r
const image_transform& get_image_transform() const;\r
image_transform& get_image_transform();\r
\r