class image_renderer\r
{\r
spl::shared_ptr<context> ogl_;\r
- image_kernel kernel_; \r
+ image_kernel kernel_;\r
+ bool warm_;\r
public:\r
image_renderer(const spl::shared_ptr<context>& ogl)\r
: ogl_(ogl)\r
, kernel_(ogl_)\r
+ , warm_(false)\r
{\r
}\r
\r
boost::unique_future<boost::iterator_range<const uint8_t*>> operator()(std::vector<layer> layers, const core::video_format_desc& format_desc)\r
{ \r
- if(layers.empty())\r
+ // Remove empty layers.\r
+ boost::range::remove_erase_if(layers, [](const layer& layer)\r
{\r
- // Bypass GPU since no work needs to be done.\r
- auto buffer = ogl_->create_host_buffer(format_desc.size, host_buffer::usage::write_only);\r
- A_memset(buffer->data(), 0, buffer->size());\r
+ return layer.second.empty();\r
+ });\r
\r
- return async(launch_policy::deferred, [=]() mutable -> boost::iterator_range<const uint8_t*>\r
- {\r
- auto ptr = reinterpret_cast<const uint8_t*>(buffer->data());\r
- return boost::iterator_range<const uint8_t*>(ptr, ptr + buffer.get()->size());\r
- });\r
- }\r
- else if(layers.size() == 1 && \r
- layers.at(0).first == core::blend_mode::normal &&\r
- layers.at(0).second.at(0).pix_desc.format == core::pixel_format::bgra)\r
- { \r
- // Bypass GPU since no work needs to be done.\r
- auto buffer = layers.at(0).second.at(0).buffers.at(0);\r
- return async(launch_policy::deferred, [=]() mutable -> boost::iterator_range<const uint8_t*>\r
- {\r
- auto ptr = reinterpret_cast<const uint8_t*>(buffer->data());\r
- return boost::iterator_range<const uint8_t*>(ptr, ptr + buffer.get()->size());\r
- });\r
- }\r
- else\r
+ // Start host->device transfers.\r
+ std::map<host_buffer*, boost::shared_future<spl::shared_ptr<device_buffer>>> buffer_map;\r
+ BOOST_FOREACH(auto& layer, layers)\r
{\r
- // Start host->device transfers.\r
- BOOST_FOREACH(auto& layer, layers)\r
- {\r
- BOOST_FOREACH(auto& item, layer.second)\r
- {\r
- for(size_t n = 0; n < item.pix_desc.planes.size(); ++n) \r
- item.textures.push_back(ogl_->copy_async(item.buffers.at(n), item.pix_desc.planes[n].width, item.pix_desc.planes[n].height, item.pix_desc.planes[n].channels));\r
- }\r
- } \r
-\r
- // Draw\r
- boost::shared_future<spl::shared_ptr<host_buffer>> buffer = ogl_->begin_invoke([=]() mutable -> spl::shared_ptr<host_buffer>\r
+ BOOST_FOREACH(auto& item, layer.second)\r
{\r
- auto draw_buffer = create_mixer_buffer(4, format_desc);\r
-\r
- if(format_desc.field_mode != core::field_mode::progressive)\r
+ for(size_t n = 0; n < item.pix_desc.planes.size(); ++n) \r
{\r
- auto upper = layers;\r
- auto lower = std::move(layers);\r
-\r
- BOOST_FOREACH(auto& layer, upper)\r
+ auto host_buffer = item.buffers.at(n);\r
+ auto it = buffer_map.find(host_buffer.get());\r
+ if(it == buffer_map.end())\r
{\r
- BOOST_FOREACH(auto& item, layer.second)\r
- item.transform.field_mode = static_cast<core::field_mode>(item.transform.field_mode & core::field_mode::upper);\r
+ auto plane = item.pix_desc.planes[n];\r
+ auto future_device_buffer = ogl_->copy_async(host_buffer, plane.width, plane.height, plane.channels);\r
+ it = buffer_map.insert(std::make_pair(host_buffer.get(), std::move(future_device_buffer))).first;\r
}\r
+ item.textures.push_back(it->second);\r
+ } \r
+ item.buffers.clear();\r
+ }\r
+ } \r
+\r
+ // Draw\r
+ boost::shared_future<spl::shared_ptr<host_buffer>> buffer = ogl_->begin_invoke([=]() mutable -> spl::shared_ptr<host_buffer>\r
+ {\r
+ auto draw_buffer = create_mixer_buffer(4, format_desc);\r
\r
- BOOST_FOREACH(auto& layer, lower)\r
- {\r
- BOOST_FOREACH(auto& item, layer.second)\r
- item.transform.field_mode = static_cast<core::field_mode>(item.transform.field_mode & core::field_mode::lower);\r
- }\r
+ if(format_desc.field_mode != core::field_mode::progressive)\r
+ {\r
+ auto upper = layers;\r
+ auto lower = std::move(layers);\r
\r
- draw(std::move(upper), draw_buffer, format_desc);\r
- draw(std::move(lower), draw_buffer, format_desc);\r
+ BOOST_FOREACH(auto& layer, upper)\r
+ {\r
+ BOOST_FOREACH(auto& item, layer.second)\r
+ item.transform.field_mode = static_cast<core::field_mode>(item.transform.field_mode & core::field_mode::upper);\r
}\r
- else\r
+\r
+ BOOST_FOREACH(auto& layer, lower)\r
{\r
- draw(std::move(layers), draw_buffer, format_desc);\r
+ BOOST_FOREACH(auto& item, layer.second)\r
+ item.transform.field_mode = static_cast<core::field_mode>(item.transform.field_mode & core::field_mode::lower);\r
}\r
- \r
- auto result = ogl_->create_host_buffer(static_cast<int>(format_desc.size), host_buffer::usage::read_only); \r
- draw_buffer->copy_to(result); \r
- return result;\r
- });\r
\r
- // Defer memory mapping.\r
- return async(launch_policy::deferred, [=]() mutable -> boost::iterator_range<const uint8_t*>\r
+ draw(std::move(upper), draw_buffer, format_desc);\r
+ draw(std::move(lower), draw_buffer, format_desc);\r
+ }\r
+ else\r
{\r
- auto ptr = reinterpret_cast<const uint8_t*>(buffer.get()->data()); // .get() and ->data() can block calling thread, ->data() can also block OpenGL thread, defer it as long as possible.\r
- return boost::iterator_range<const uint8_t*>(ptr, ptr + buffer.get()->size());\r
- });\r
- }\r
+ draw(std::move(layers), draw_buffer, format_desc);\r
+ }\r
+ \r
+ auto result = ogl_->create_host_buffer(static_cast<int>(format_desc.size), host_buffer::usage::read_only); \r
+ draw_buffer->copy_to(result); \r
+ return result;\r
+ });\r
+\r
+ warm_ = true;\r
+\r
+ // Defer memory mapping.\r
+ return async(launch_policy::deferred, [=]() mutable -> boost::iterator_range<const uint8_t*>\r
+ {\r
+ auto ptr = reinterpret_cast<const uint8_t*>(buffer.get()->data()); // .get() and ->data() can block calling thread, ->data() can also block OpenGL thread, defer it as long as possible.\r
+ return boost::iterator_range<const uint8_t*>(ptr, ptr + buffer.get()->size());\r
+ });\r
}\r
\r
private:\r
\r
struct image_mixer::impl : boost::noncopyable\r
{ \r
- spl::shared_ptr<context> ogl_;\r
+ spl::shared_ptr<context> ogl_;\r
image_renderer renderer_;\r
std::vector<core::frame_transform> transform_stack_;\r
std::vector<layer> layers_; // layer/stream/items\r
if(frame->get_buffers().empty())\r
return;\r
\r
+ if(transform_stack_.back().field_mode == core::field_mode::empty)\r
+ return;\r
+\r
item item;\r
item.pix_desc = frame->get_pixel_format_desc();\r
item.buffers = frame->get_buffers(); \r
\r
struct flash_producer : public core::frame_producer\r
{ \r
- const std::wstring filename_; \r
+ const std::wstring filename_; \r
const spl::shared_ptr<core::frame_factory> frame_factory_;\r
- const int width_;\r
- const int height_;\r
- const int buffer_size_;\r
+ const int width_;\r
+ const int height_;\r
+ const int buffer_size_;\r
\r
- tbb::atomic<int> fps_;\r
- tbb::atomic<bool> sync_;\r
+ tbb::atomic<int> fps_;\r
+ tbb::atomic<bool> sync_;\r
\r
- spl::shared_ptr<diagnostics::graph> graph_;\r
+ spl::shared_ptr<diagnostics::graph> graph_;\r
\r
std::queue<spl::shared_ptr<core::draw_frame>> frame_buffer_;\r
tbb::concurrent_bounded_queue<spl::shared_ptr<core::draw_frame>> output_buffer_;\r
\r
- mutable tbb::spin_mutex last_frame_mutex_;\r
+ mutable tbb::spin_mutex last_frame_mutex_;\r
spl::shared_ptr<core::draw_frame> last_frame_;\r
\r
- std::unique_ptr<flash_renderer> renderer_;\r
+ std::unique_ptr<flash_renderer> renderer_;\r
\r
- executor executor_; \r
+ executor executor_; \r
public:\r
flash_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, int width, int height) \r
: filename_(filename) \r
, last_frame_(core::draw_frame::empty())\r
, width_(width > 0 ? width : frame_factory->get_video_format_desc().width)\r
, height_(height > 0 ? height : frame_factory->get_video_format_desc().height)\r
- , buffer_size_(env::properties().get(L"configuration.flash.buffer-depth", frame_factory_->get_video_format_desc().fps > 30.0 ? 3 : 2))\r
+ , buffer_size_(env::properties().get(L"configuration.flash.buffer-depth", frame_factory_->get_video_format_desc().fps > 30.0 ? 4 : 2))\r
, executor_(L"flash_producer")\r
{ \r
sync_ = true;\r