X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fmixer%2Fimage%2Fimage_mixer.cpp;h=326128672bfa664cc876f580c6b4f5f001a48b45;hb=636d2b36ba4a5b2a7c963adbebc8535cdb943208;hp=8cfc85a951282ffd7394746c6a3f8da1e3c12fdc;hpb=ce2b97c07b781d108d65530af5e18fcd6766c99d;p=casparcg diff --git a/core/mixer/image/image_mixer.cpp b/core/mixer/image/image_mixer.cpp index 8cfc85a95..326128672 100644 --- a/core/mixer/image/image_mixer.cpp +++ b/core/mixer/image/image_mixer.cpp @@ -37,223 +37,196 @@ #include #include +#include + #include +#include +#include +#include #include +#include #include namespace caspar { namespace core { struct image_mixer::implementation : boost::noncopyable -{ - static const size_t LOCAL_KEY_INDEX = 3; - static const size_t LAYER_KEY_INDEX = 4; +{ + typedef std::deque layer; - struct render_item - { - pixel_format_desc desc; - std::vector> textures; - int tag; - core::image_transform transform; - }; + video_channel_context& channel_; - video_channel_context& channel_; - - std::stack transform_stack_; - std::queue> render_queue_; + std::vector transform_stack_; + std::vector mode_stack_; + + std::deque> layers_; // layer/stream/items - image_kernel kernel_; + image_kernel kernel_; - safe_ptr read_buffer_; - safe_ptr draw_buffer_; - safe_ptr write_buffer_; + std::array,2> draw_buffer_; + std::shared_ptr write_buffer_; - safe_ptr local_key_buffer_; - safe_ptr layer_key_buffer_; - - bool local_key_; - bool layer_key_; + std::array,2> local_key_buffer_; + std::shared_ptr layer_key_buffer_; public: implementation(video_channel_context& video_channel) : channel_(video_channel) - , read_buffer_(video_channel.ogl().create_host_buffer(video_channel.get_format_desc().size, host_buffer::read_only)) - , draw_buffer_(video_channel.ogl().create_device_buffer(video_channel.get_format_desc().width, channel_.get_format_desc().height, 4)) - , write_buffer_ (video_channel.ogl().create_device_buffer(video_channel.get_format_desc().width, channel_.get_format_desc().height, 4)) - , local_key_buffer_(video_channel.ogl().create_device_buffer(video_channel.get_format_desc().width, channel_.get_format_desc().height, 1)) - , layer_key_buffer_(video_channel.ogl().create_device_buffer(video_channel.get_format_desc().width, channel_.get_format_desc().height, 1)) - , local_key_(false) - , layer_key_(false) + , transform_stack_(1) + , mode_stack_(1, video_mode::progressive) { - transform_stack_.push(core::image_transform()); + initialize_buffers(); + } - channel_.ogl().invoke([=] - { - if(!GLEE_VERSION_3_0) - CASPAR_LOG(warning) << "Missing OpenGL 3.0 support.";//BOOST_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 3.0 support.")); - }); + ~implementation() + { + channel_.ogl().gc(); } - void begin(const core::basic_frame& frame) + void initialize_buffers() { - transform_stack_.push(transform_stack_.top()*frame.get_image_transform()); + write_buffer_ = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 4); + layer_key_buffer_ = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 1); + draw_buffer_[0] = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 4); + //draw_buffer_[1] = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 4); + local_key_buffer_[0] = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 1); + //local_key_buffer_[1] = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 1); + channel_.ogl().gc(); + } + + void begin(core::basic_frame& frame) + { + transform_stack_.push_back(transform_stack_.back()*frame.get_image_transform()); + mode_stack_.push_back(frame.get_mode() == video_mode::progressive ? mode_stack_.back() : frame.get_mode()); } void visit(core::write_frame& frame) - { - render_item item = {frame.get_pixel_format_desc(), frame.get_textures(), frame.tag(), transform_stack_.top()*frame.get_image_transform()}; - render_queue_.back().push(item); + { + CASPAR_ASSERT(!layers_.empty()); + + // Check if frame has been discarded by interlacing + if(boost::range::find(mode_stack_, video_mode::upper) != mode_stack_.end() && boost::range::find(mode_stack_, video_mode::lower) != mode_stack_.end()) + return; + + core::render_item item(frame.get_pixel_format_desc(), frame.get_textures(), transform_stack_.back(), mode_stack_.back(), frame.tag()); + + auto& layer = layers_.back(); + + auto it = boost::range::find(layer, item); + if(it == layer.end()) + layer.push_back(item); } void end() { - transform_stack_.pop(); + transform_stack_.pop_back(); + mode_stack_.pop_back(); } void begin_layer() { - render_queue_.push(std::queue()); + layers_.push_back(layer()); } void end_layer() { } - - void reinitialize_buffers() - { - read_buffer_ = channel_.ogl().create_host_buffer(channel_.get_format_desc().size, host_buffer::read_only); - draw_buffer_ = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 4); - write_buffer_ = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 4); - local_key_buffer_ = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 1); - layer_key_buffer_ = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 1); - channel_.ogl().gc(); - } - - safe_ptr render() + + boost::unique_future> render() { - auto read_buffer = read_buffer_; - auto result = channel_.ogl().begin_invoke([=]() -> safe_ptr - { - read_buffer->map(); - return read_buffer; - }); - - auto render_queue = std::move(render_queue_); - - channel_.ogl().begin_invoke([=]() mutable - { - if(draw_buffer_->width() != channel_.get_format_desc().width || draw_buffer_->height() != channel_.get_format_desc().height) - reinitialize_buffers(); - - local_key_ = false; - layer_key_ = false; - - // Clear buffers. - local_key_buffer_->clear(); - layer_key_buffer_->clear(); - draw_buffer_->clear(); + auto layers = std::move(layers_); + return channel_.ogl().begin_invoke([=]()mutable{return render(std::move(layers));}); + } + + safe_ptr render(std::deque&& layers) + { + if(channel_.get_format_desc().width != write_buffer_->width() || channel_.get_format_desc().height != write_buffer_->height()) + initialize_buffers(); + + layer_key_buffer_->clear(); + draw_buffer_[0]->clear(); + //draw_buffer_[1]->clear(); + local_key_buffer_[0]->clear(); + //local_key_buffer_[1]->clear(); - // Draw items in device. + bool layer_key = false; - while(!render_queue.empty()) - { - auto layer = render_queue.front(); - render_queue.pop(); - - draw_buffer_->attach(); - - while(!layer.empty()) - { - // Split layer into streams - std::vector stream; - do - { - stream.push_back(layer.front()); - layer.pop(); - } - while(!layer.empty() && layer.front().tag == stream.front().tag); + BOOST_FOREACH(auto& layer, layers) + draw(std::move(layer), layer_key); - draw(stream); + std::swap(draw_buffer_[0], write_buffer_); + + auto host_buffer = channel_.ogl().create_host_buffer(channel_.get_format_desc().size, host_buffer::read_only); + host_buffer->begin_read(*write_buffer_); + + GL(glFlush()); - channel_.ogl().yield(); // Allow quick buffer allocation to execute. - } + return host_buffer; + } - layer_key_ = local_key_; // If there was only key in last layer then use it as key for the entire next layer. - local_key_ = false; + void draw(layer&& layer, bool& layer_key) + { + bool local_key = false; - std::swap(local_key_buffer_, layer_key_buffer_); - } + local_key_buffer_[0]->clear(); - std::swap(draw_buffer_, write_buffer_); + BOOST_FOREACH(auto& item, layer) + draw(std::move(item), local_key, layer_key); + + layer_key = local_key; - // Start transfer from device to host. - read_buffer_ = channel_.ogl().create_host_buffer(channel_.get_format_desc().size, host_buffer::read_only); - write_buffer_->write(*read_buffer_); - }); + std::swap(local_key_buffer_[0], layer_key_buffer_); + } - return std::move(result.get()); + void draw(render_item&& item, bool& local_key, bool& layer_key) + { + if(item.transform.get_is_key()) + { + draw(local_key_buffer_, std::move(item), nullptr, nullptr); + local_key = true; + } + else + { + draw(draw_buffer_, std::move(item), local_key ? local_key_buffer_[0] : nullptr, layer_key ? layer_key_buffer_ : nullptr); + local_key_buffer_[0]->clear(); + local_key = false; + } + channel_.ogl().yield(); // Return resources to pool as early as possible. } - void draw(const std::vector& stream) - { - if(stream.empty()) - return; - - BOOST_FOREACH(auto item, stream) + void draw(std::array,2>& targets, render_item&& item, const std::shared_ptr& local_key, const std::shared_ptr& layer_key) + { + if(!std::all_of(item.textures.begin(), item.textures.end(), std::mem_fn(&device_buffer::ready))) { - bool local_key = false; - bool layer_key = false; - - // Setup key and kernel - - if(item.transform.get_is_key()) // This is a key frame, render it to the local_key buffer for later use. - { - if(!local_key_) // Initialize local-key if it is not active. - { - local_key_buffer_->clear(); - local_key_buffer_->attach(); - local_key_ = true; - } - } - else // This is a normal frame. Use key buffers if they are active. - { - local_key = local_key_; - layer_key = layer_key_; - - if(local_key_) // Use local key if we have it. - { - local_key_buffer_->bind(LOCAL_KEY_INDEX); - draw_buffer_->attach(); - } - - if(layer_key_) // Use layer key if we have it. - layer_key_buffer_->bind(LAYER_KEY_INDEX); - } - - // Bind textures - - for(size_t n = 0; n < item.textures.size(); ++n) - item.textures[n]->bind(n); - - // Draw + CASPAR_LOG(warning) << L"[image_mixer] Performance warning. Host to device transfer not complete, GPU will be stalled"; + channel_.ogl().yield(); // Try to give it some more time. + } - kernel_.draw(channel_.get_format_desc().width, channel_.get_format_desc().height, item.desc, item.transform, local_key, layer_key); - } + targets[0]->attach(); + kernel_.draw(item, make_safe(targets[0]), local_key, layer_key); - local_key_ = stream.back().transform.get_is_key(); // Keep for next layer if last frame is key - } + //targets[1]->attach(); + //kernel_.draw(item, make_safe(targets[0]), local_key, layer_key); + + //targets[0]->bind(); + + //glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, targets[0]->width(), targets[0]->height()); + + //std::swap(targets[0], targets[1]); + } + safe_ptr create_frame(const void* tag, const core::pixel_format_desc& desc) { - return make_safe(channel_.ogl(), reinterpret_cast(tag), desc); + return make_safe(channel_.ogl(), tag, desc); } }; image_mixer::image_mixer(video_channel_context& video_channel) : impl_(new implementation(video_channel)){} -void image_mixer::begin(const core::basic_frame& frame){impl_->begin(frame);} +void image_mixer::begin(core::basic_frame& frame){impl_->begin(frame);} void image_mixer::visit(core::write_frame& frame){impl_->visit(frame);} void image_mixer::end(){impl_->end();} -safe_ptr image_mixer::render(){return impl_->render();} +boost::unique_future> image_mixer::render(){return impl_->render();} safe_ptr image_mixer::create_frame(const void* tag, const core::pixel_format_desc& desc){return impl_->create_frame(tag, desc);} void image_mixer::begin_layer(){impl_->begin_layer();} void image_mixer::end_layer(){impl_->end_layer();}