X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fmixer%2Fimage%2Fimage_mixer.cpp;h=35e95f0a34e1e75ad42b7a44709fab40b92e84e1;hb=580d5b82595893c83f7bf715c94de20b70745ae4;hp=88c2e177d0ba7221cbef8c11942073cbae4ad401;hpb=0fde039e59025b2370d0e48b2518025f44b8f2cc;p=casparcg diff --git a/core/mixer/image/image_mixer.cpp b/core/mixer/image/image_mixer.cpp index 88c2e177d..35e95f0a3 100644 --- a/core/mixer/image/image_mixer.cpp +++ b/core/mixer/image/image_mixer.cpp @@ -20,18 +20,17 @@ #include "../../stdafx.h" #include "image_mixer.h" -#include "image_kernel.h" +#include "image_kernel.h" +#include "../write_frame.h" #include "../gpu/ogl_device.h" #include "../gpu/host_buffer.h" #include "../gpu/device_buffer.h" -#include "../write_frame.h" - #include "../../video_channel_context.h" -#include #include #include +#include #include #include @@ -40,19 +39,31 @@ #include #include -#include -#include +#include #include -#include #include -#include using namespace boost::assign; namespace caspar { namespace core { -typedef std::deque layer; +struct item +{ + pixel_format_desc pix_desc; + std::vector> textures; + frame_transform transform; +}; + +struct layer +{ + std::vector items; + blend_mode::type blend_mode; + + layer(blend_mode::type blend_mode) : blend_mode(blend_mode) + { + } +}; class image_renderer { @@ -65,25 +76,47 @@ public: { } - boost::unique_future> render(std::deque&& layers) + boost::unique_future> render(std::vector&& layers) { - auto layers2 = std::move(layers); - return channel_.ogl().begin_invoke([=]() mutable + auto layers2 = make_move_on_copy(std::move(layers)); + return channel_.ogl().begin_invoke([=] { - return do_render(std::move(layers2)); + return do_render(std::move(layers2.value)); }); } private: - safe_ptr do_render(std::deque&& layers) + safe_ptr do_render(std::vector&& layers) { - std::shared_ptr layer_key_buffer; - auto draw_buffer = create_device_buffer(4); - - BOOST_FOREACH(auto& layer, layers) - draw(std::move(layer), draw_buffer, layer_key_buffer); - + + if(channel_.get_format_desc().field_mode != field_mode::progressive) + { + auto upper = layers; + auto lower = std::move(layers); + + BOOST_FOREACH(auto& layer, upper) + { + boost::remove_erase_if(layer.items, [](const item& item){return !(item.transform.field_mode & field_mode::upper);}); + BOOST_FOREACH(auto& item, layer.items) + item.transform.field_mode = field_mode::upper; + } + + BOOST_FOREACH(auto& layer, lower) + { + boost::remove_erase_if(layer.items, [](const item& item){return !(item.transform.field_mode & field_mode::lower);}); + BOOST_FOREACH(auto& item, layer.items) + item.transform.field_mode = field_mode::lower; + } + + draw(std::move(upper), draw_buffer); + draw(std::move(lower), draw_buffer); + } + else + { + draw(std::move(layers), draw_buffer); + } + auto host_buffer = channel_.ogl().create_host_buffer(channel_.get_format_desc().size, host_buffer::read_only); channel_.ogl().attach(*draw_buffer); host_buffer->begin_read(draw_buffer->width(), draw_buffer->height(), format(draw_buffer->stride())); @@ -95,88 +128,107 @@ private: return host_buffer; } - void draw(layer&& layer, const safe_ptr& draw_buffer, std::shared_ptr& layer_key_buffer) + void draw(std::vector&& layers, + safe_ptr& draw_buffer) + { + std::shared_ptr layer_key_buffer; + + BOOST_FOREACH(auto& layer, layers) + draw_layer(std::move(layer), draw_buffer, layer_key_buffer); + } + + void draw_layer(layer&& layer, + safe_ptr& draw_buffer, + std::shared_ptr& layer_key_buffer) { - if(layer.empty()) + if(layer.items.empty()) return; - std::pair> local_key_buffer; // int is fields flag + std::shared_ptr local_key_buffer; + std::shared_ptr local_mix_buffer; - if(layer.front().blend_mode != blend_mode::normal && has_overlapping_items(layer)) + if(layer.blend_mode != blend_mode::normal && layer.items.size() > 1) { - auto layer_draw_buffer = create_device_buffer(4); // int is fields flag - auto layer_blend_mode = layer.front().blend_mode; + auto layer_draw_buffer = create_device_buffer(4); - BOOST_FOREACH(auto& item, layer) - { - item.blend_mode = blend_mode::normal; // Disable blending and just merge. - draw_item(std::move(item), *layer_draw_buffer, local_key_buffer, layer_key_buffer); - } - - render_item item; - item.pix_desc.pix_fmt = pixel_format::bgra; - item.pix_desc.planes = list_of(pixel_format_desc::plane(channel_.get_format_desc().width, channel_.get_format_desc().height, 4)); - item.textures = list_of(layer_draw_buffer); - item.transform = frame_transform(); - item.blend_mode = layer_blend_mode; - - kernel_.draw(channel_.ogl(), std::move(item), *draw_buffer, nullptr, nullptr); + BOOST_FOREACH(auto& item, layer.items) + draw_item(std::move(item), layer_draw_buffer, layer_key_buffer, local_key_buffer, local_mix_buffer); + + draw_device_buffer(layer_draw_buffer, std::move(local_mix_buffer), blend_mode::normal); + draw_device_buffer(draw_buffer, std::move(layer_draw_buffer), layer.blend_mode); } else // fast path { - BOOST_FOREACH(auto& item, layer) - draw_item(std::move(item), *draw_buffer, local_key_buffer, layer_key_buffer); + BOOST_FOREACH(auto& item, layer.items) + draw_item(std::move(item), draw_buffer, layer_key_buffer, local_key_buffer, local_mix_buffer); + + draw_device_buffer(draw_buffer, std::move(local_mix_buffer), blend_mode::normal); } - CASPAR_ASSERT(local_key_buffer.first == 0 || local_key_buffer.first == core::field_mode::progressive); - - std::swap(local_key_buffer.second, layer_key_buffer); + std::swap(local_key_buffer, layer_key_buffer); } - void draw_item(render_item&& item, - device_buffer& draw_buffer, - std::pair>& local_key_buffer, - std::shared_ptr& layer_key_buffer) - { + void draw_item(item&& item, + safe_ptr& draw_buffer, + std::shared_ptr& layer_key_buffer, + std::shared_ptr& local_key_buffer, + std::shared_ptr& local_mix_buffer) + { + draw_params draw_params; + draw_params.pix_desc = std::move(item.pix_desc); + draw_params.textures = std::move(item.textures); + draw_params.transform = std::move(item.transform); + if(item.transform.is_key) { - if(!local_key_buffer.second) - { - local_key_buffer.first = 0; - local_key_buffer.second = create_device_buffer(1); - } - - local_key_buffer.first |= item.transform.field_mode; // Add field to flag. - kernel_.draw(channel_.ogl(), std::move(item), *local_key_buffer.second, nullptr, nullptr); + local_key_buffer = local_key_buffer ? local_key_buffer : create_device_buffer(1); + + draw_params.background = local_key_buffer; + draw_params.local_key = nullptr; + draw_params.layer_key = nullptr; + + kernel_.draw(channel_.ogl(), std::move(draw_params)); + } + else if(item.transform.is_mix) + { + local_mix_buffer = local_mix_buffer ? local_mix_buffer : create_device_buffer(4); + + draw_params.background = local_mix_buffer; + draw_params.local_key = std::move(local_key_buffer); + draw_params.layer_key = layer_key_buffer; + + draw_params.keyer = keyer::additive; + + kernel_.draw(channel_.ogl(), std::move(draw_params)); } else { - kernel_.draw(channel_.ogl(), std::move(item), draw_buffer, local_key_buffer.second, layer_key_buffer); - local_key_buffer.first ^= item.transform.field_mode; // Remove field from flag. + draw_device_buffer(draw_buffer, std::move(local_mix_buffer), blend_mode::normal); - if(local_key_buffer.first == 0) // If all fields from key has been used, reset it - { - local_key_buffer.first = 0; - local_key_buffer.second.reset(); - } - } + draw_params.background = draw_buffer; + draw_params.local_key = std::move(local_key_buffer); + draw_params.layer_key = layer_key_buffer; + + kernel_.draw(channel_.ogl(), std::move(draw_params)); + } } - bool has_overlapping_items(const layer& layer) - { - auto upper_count = boost::range::count_if(layer, [&](const render_item& item) - { - return item.transform.field_mode | field_mode::upper; - }); + void draw_device_buffer(safe_ptr& draw_buffer, std::shared_ptr&& source_buffer, blend_mode::type blend_mode = blend_mode::normal) + { + if(!source_buffer) + return; - auto lower_count = boost::range::count_if(layer, [&](const render_item& item) - { - return item.transform.field_mode | field_mode::lower; - }); + draw_params draw_params; + draw_params.pix_desc.pix_fmt = pixel_format::bgra; + draw_params.pix_desc.planes = list_of(pixel_format_desc::plane(source_buffer->width(), source_buffer->height(), 4)); + draw_params.textures = list_of(source_buffer); + draw_params.transform = frame_transform(); + draw_params.blend_mode = blend_mode; + draw_params.background = draw_buffer; - return upper_count > 1 || lower_count > 1; - } - + kernel_.draw(channel_.ogl(), std::move(draw_params)); + } + safe_ptr create_device_buffer(size_t stride) { auto buffer = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, stride); @@ -184,28 +236,24 @@ private: return buffer; } }; - struct image_mixer::implementation : boost::noncopyable { - ogl_device& ogl_; - image_renderer renderer_; - std::vector transform_stack_; - blend_mode::type active_blend_mode_; - std::deque> layers_; // layer/stream/items + ogl_device& ogl_; + image_renderer renderer_; + std::vector transform_stack_; + std::vector layers_; // layer/stream/items public: implementation(video_channel_context& video_channel) : ogl_(video_channel.ogl()) , renderer_(video_channel) - , transform_stack_(1) - , active_blend_mode_(blend_mode::normal) + , transform_stack_(1) { } void begin_layer(blend_mode::type blend_mode) { - active_blend_mode_ = blend_mode; - layers_ += layer(); + layers_.push_back(layer(blend_mode)); } void begin(core::basic_frame& frame) @@ -214,17 +262,16 @@ public: } void visit(core::write_frame& frame) - { - if(transform_stack_.back().field_mode == field_mode::empty) + { + if(frame.get_frame_transform().field_mode == field_mode::empty) return; - - core::render_item item; + + item item; item.pix_desc = frame.get_pixel_format_desc(); item.textures = frame.get_textures(); item.transform = transform_stack_.back(); - item.blend_mode = active_blend_mode_; - layers_.back() += item; + layers_.back().items.push_back(item); } void end()