<MultiProcessorCompilation>true</MultiProcessorCompilation>\r
<FloatingPointModel>Fast</FloatingPointModel>\r
<ForcedIncludeFiles>common/compiler/vs/disable_silly_warnings.h</ForcedIncludeFiles>\r
+ <ObjectFileName>$(IntDir)/%(RelativeDir)/</ObjectFileName>\r
</ClCompile>\r
<PostBuildEvent>\r
<Command>\r
</Lib>\r
</ItemDefinitionGroup>\r
<ItemGroup>\r
+ <ClInclude Include="cpu\factory.h" />\r
<ClInclude Include="cpu\image\image_mixer.h" />\r
<ClInclude Include="cpu\util\blend.h" />\r
<ClInclude Include="cpu\util\simd.h" />\r
+ <ClInclude Include="cpu\util\write_frame.h" />\r
+ <ClInclude Include="factory.h" />\r
+ <ClInclude Include="ogl\factory.h" />\r
<ClInclude Include="ogl\image\blending_glsl.h" />\r
<ClInclude Include="ogl\image\image_kernel.h" />\r
<ClInclude Include="ogl\image\image_mixer.h" />\r
</ProjectReference>\r
</ItemGroup>\r
<ItemGroup>\r
+ <ClCompile Include="cpu\factory.cpp">\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
+ </ClCompile>\r
<ClCompile Include="cpu\image\image_mixer.cpp">\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
</ClCompile>\r
+ <ClCompile Include="cpu\util\write_frame.cpp">\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ </ClCompile>\r
+ <ClCompile Include="ogl\factory.cpp">\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
+ </ClCompile>\r
<ClCompile Include="ogl\image\image_kernel.cpp">\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
<Filter Include="source\ogl">\r
<UniqueIdentifier>{dbb5f22a-c188-4ab8-af04-3ec2b12d2e27}</UniqueIdentifier>\r
</Filter>\r
- <Filter Include="source\image">\r
- <UniqueIdentifier>{1e686392-8f55-4919-8eee-026f3e8d2e9a}</UniqueIdentifier>\r
- </Filter>\r
<Filter Include="source\cpu">\r
<UniqueIdentifier>{51a1672a-ca14-4c53-819d-ba8fbedc01da}</UniqueIdentifier>\r
</Filter>\r
<ClInclude Include="cpu\image\image_mixer.h">\r
<Filter>source\cpu\image</Filter>\r
</ClInclude>\r
+ <ClInclude Include="cpu\util\write_frame.h">\r
+ <Filter>source\cpu\util</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="factory.h">\r
+ <Filter>source</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="cpu\factory.h">\r
+ <Filter>source\cpu</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="ogl\factory.h">\r
+ <Filter>source\ogl</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="StdAfx.cpp" />\r
<ClCompile Include="cpu\image\image_mixer.cpp">\r
<Filter>source\cpu\image</Filter>\r
</ClCompile>\r
+ <ClCompile Include="cpu\util\write_frame.cpp">\r
+ <Filter>source\cpu\util</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="cpu\factory.cpp">\r
+ <Filter>source\cpu</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="ogl\factory.cpp">\r
+ <Filter>source\ogl</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
--- /dev/null
+#include "../StdAfx.h"\r
+\r
+#include "factory.h"\r
+\r
+#include "image/image_mixer.h"\r
+\r
+namespace caspar { namespace accelerator { namespace cpu {\r
+ \r
+spl::shared_ptr<core::image_mixer> factory::create_image_mixer()\r
+{\r
+ return spl::shared_ptr<cpu::image_mixer>();\r
+}\r
+\r
+}}}
\ No newline at end of file
--- /dev/null
+#pragma once\r
+\r
+#include "../factory.h"\r
+\r
+namespace caspar { namespace accelerator { namespace cpu {\r
+ \r
+class factory : public accelerator::factory\r
+{\r
+public:\r
+ virtual spl::shared_ptr<core::image_mixer> create_image_mixer() override;\r
+};\r
+\r
+}}}
\ No newline at end of file
-#include "../../StdAfx.h"\r
+/*\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
+*\r
+* This file is part of CasparCG (www.casparcg.com).\r
+*\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
+*\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+*\r
+* Author: Robert Nagy, ronag89@gmail.com\r
+*/\r
+\r
+#include "../../stdafx.h"\r
\r
#include "image_mixer.h"\r
\r
+#include "../util/write_frame.h"\r
+\r
+#include <common/assert.h>\r
+#include <common/gl/gl_check.h>\r
+#include <common/concurrency/async.h>\r
+#include <common/memory/memcpy.h>\r
+\r
+#include <core/frame/write_frame.h>\r
+#include <core/frame/frame_transform.h>\r
+#include <core/frame/pixel_format.h>\r
+#include <core/video_format.h>\r
+\r
+#include <asmlib.h>\r
+\r
+#include <gl/glew.h>\r
+\r
+#include <tbb/cache_aligned_allocator.h>\r
+\r
+#include <boost/foreach.hpp>\r
+#include <boost/range.hpp>\r
+#include <boost/range/algorithm_ext/erase.hpp>\r
+#include <boost/thread/future.hpp>\r
+\r
+#include <algorithm>\r
+#include <vector>\r
+\r
namespace caspar { namespace accelerator { namespace cpu {\r
+ \r
+struct item\r
+{\r
+ core::pixel_format_desc pix_desc;\r
+ std::vector<spl::shared_ptr<host_buffer>> buffers;\r
+ core::frame_transform transform;\r
+\r
+ item()\r
+ : pix_desc(core::pixel_format::invalid)\r
+ {\r
+ }\r
+};\r
+\r
+bool operator==(const item& lhs, const item& rhs)\r
+{\r
+ return lhs.buffers == rhs.buffers && lhs.transform == rhs.transform;\r
+}\r
+\r
+bool operator!=(const item& lhs, const item& rhs)\r
+{\r
+ return !(lhs == rhs);\r
+}\r
+\r
+struct layer\r
+{\r
+ std::vector<item> items;\r
+\r
+ layer()\r
+ {\r
+ }\r
+\r
+ layer(std::vector<item> items)\r
+ : items(std::move(items))\r
+ {\r
+ }\r
+};\r
+\r
+bool operator==(const layer& lhs, const layer& rhs)\r
+{\r
+ return lhs.items == rhs.items;\r
+}\r
+\r
+bool operator!=(const layer& lhs, const layer& rhs)\r
+{\r
+ return !(lhs == rhs);\r
+}\r
+\r
+class image_renderer\r
+{\r
+ std::pair<std::vector<layer>, boost::shared_future<boost::iterator_range<const uint8_t*>>> last_image_;\r
+public:\r
+ \r
+ boost::shared_future<boost::iterator_range<const uint8_t*>> operator()(std::vector<layer> layers, const core::video_format_desc& format_desc)\r
+ { \r
+ if(last_image_.first == layers && last_image_.second.has_value())\r
+ return last_image_.second;\r
+\r
+ auto image = render(layers, format_desc);\r
+ last_image_ = std::make_pair(std::move(layers), image);\r
+ return image;\r
+ }\r
+\r
+private: \r
+ boost::shared_future<boost::iterator_range<const uint8_t*>> render(std::vector<layer> layers, const core::video_format_desc& format_desc)\r
+ { \r
+ static const auto empty = spl::make_shared<const std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>>>(2048*2048*4, 0);\r
+ CASPAR_VERIFY(empty->size() >= format_desc.size);\r
+ \r
+ if(layers.empty())\r
+ {\r
+ return async(launch_policy::deferred, [=]\r
+ {\r
+ return boost::iterator_range<const uint8_t*>(empty->data(), empty->data() + format_desc.size);\r
+ });\r
+ }\r
+ else if(layers.size() == 1 &&\r
+ layers.at(0).items.size() == 1 &&\r
+ layers.at(0).items.at(0).pix_desc.format == core::pixel_format::bgra &&\r
+ layers.at(0).items.at(0).buffers.at(0)->size() == format_desc.size &&\r
+ layers.at(0).items.at(0).transform == core::frame_transform())\r
+ {\r
+ auto buffer = layers.at(0).items.at(0).buffers.at(0);\r
+ return async(launch_policy::deferred, [=]\r
+ {\r
+ return boost::iterator_range<const uint8_t*>(buffer->data(), buffer->data() + format_desc.size);\r
+ });\r
+ }\r
+ else\r
+ {\r
+ return async(launch_policy::deferred, [=]\r
+ {\r
+ return boost::iterator_range<const uint8_t*>(empty->data(), empty->data() + format_desc.size);\r
+ });\r
+ }\r
+ //else\r
+ //{ \r
+ //auto draw_buffer = create_mixer_buffer(4, format_desc);\r
+\r
+ //if(format_desc.field_mode != core::field_mode::progressive)\r
+ //{\r
+ // auto upper = layers;\r
+ // auto lower = std::move(layers);\r
+\r
+ // BOOST_FOREACH(auto& layer, upper)\r
+ // {\r
+ // BOOST_FOREACH(auto& item, layer.items)\r
+ // item.transform.field_mode &= core::field_mode::upper;\r
+ // }\r
+\r
+ // BOOST_FOREACH(auto& layer, lower)\r
+ // {\r
+ // BOOST_FOREACH(auto& item, layer.items)\r
+ // item.transform.field_mode &= core::field_mode::lower;\r
+ // }\r
+\r
+ // draw(std::move(upper), draw_buffer, format_desc);\r
+ // draw(std::move(lower), draw_buffer, format_desc);\r
+ //}\r
+ //else\r
+ //{\r
+ // draw(std::move(layers), draw_buffer, format_desc);\r
+ //}\r
+ // \r
+ //return draw_buffer;\r
+ //}\r
+ }\r
+ \r
+ //void draw(std::vector<layer>&& layers, \r
+ // spl::shared_ptr<device_buffer>& draw_buffer, \r
+ // const core::video_format_desc& format_desc)\r
+ //{\r
+ // std::shared_ptr<device_buffer> layer_key_buffer;\r
+\r
+ // BOOST_FOREACH(auto& layer, layers)\r
+ // draw_layer(std::move(layer), draw_buffer, layer_key_buffer, format_desc);\r
+ //}\r
+\r
+ //void draw_layer(layer&& layer, \r
+ // spl::shared_ptr<device_buffer>& draw_buffer,\r
+ // std::shared_ptr<device_buffer>& layer_key_buffer,\r
+ // const core::video_format_desc& format_desc)\r
+ //{ \r
+ // // Remove empty items.\r
+ // boost::range::remove_erase_if(layer.items, [](const item& item)\r
+ // {\r
+ // return item.transform.field_mode == core::field_mode::empty;\r
+ // });\r
+\r
+ // if(layer.items.empty())\r
+ // return;\r
+\r
+ // std::shared_ptr<device_buffer> local_key_buffer;\r
+ // std::shared_ptr<device_buffer> local_mix_buffer;\r
+ // \r
+ // if(layer.blend_mode != core::blend_mode::normal)\r
+ // {\r
+ // auto layer_draw_buffer = create_mixer_buffer(4, format_desc);\r
+\r
+ // BOOST_FOREACH(auto& item, layer.items)\r
+ // draw_item(std::move(item), layer_draw_buffer, layer_key_buffer, local_key_buffer, local_mix_buffer, format_desc); \r
+ // \r
+ // draw_mixer_buffer(layer_draw_buffer, std::move(local_mix_buffer), core::blend_mode::normal); \r
+ // draw_mixer_buffer(draw_buffer, std::move(layer_draw_buffer), layer.blend_mode);\r
+ // }\r
+ // else // fast path\r
+ // {\r
+ // BOOST_FOREACH(auto& item, layer.items) \r
+ // draw_item(std::move(item), draw_buffer, layer_key_buffer, local_key_buffer, local_mix_buffer, format_desc); \r
+ // \r
+ // draw_mixer_buffer(draw_buffer, std::move(local_mix_buffer), core::blend_mode::normal);\r
+ // } \r
+\r
+ // layer_key_buffer = std::move(local_key_buffer);\r
+ //}\r
+\r
+ //void draw_item(item&& item, \r
+ // spl::shared_ptr<device_buffer>& draw_buffer, \r
+ // std::shared_ptr<device_buffer>& layer_key_buffer, \r
+ // std::shared_ptr<device_buffer>& local_key_buffer, \r
+ // std::shared_ptr<device_buffer>& local_mix_buffer,\r
+ // const core::video_format_desc& format_desc)\r
+ //{ \r
+ // draw_params draw_params;\r
+ // draw_params.pix_desc = std::move(item.pix_desc);\r
+ // draw_params.transform = std::move(item.transform);\r
+ // BOOST_FOREACH(auto& future_texture, item.textures)\r
+ // draw_params.textures.push_back(future_texture.get());\r
+\r
+ // if(item.transform.is_key)\r
+ // {\r
+ // local_key_buffer = local_key_buffer ? local_key_buffer : create_mixer_buffer(1, format_desc);\r
+\r
+ // draw_params.background = local_key_buffer;\r
+ // draw_params.local_key = nullptr;\r
+ // draw_params.layer_key = nullptr;\r
+\r
+ // kernel_.draw(std::move(draw_params));\r
+ // }\r
+ // else if(item.transform.is_mix)\r
+ // {\r
+ // local_mix_buffer = local_mix_buffer ? local_mix_buffer : create_mixer_buffer(4, format_desc);\r
+\r
+ // draw_params.background = local_mix_buffer;\r
+ // draw_params.local_key = std::move(local_key_buffer);\r
+ // draw_params.layer_key = layer_key_buffer;\r
+\r
+ // draw_params.keyer = keyer::additive;\r
+\r
+ // kernel_.draw(std::move(draw_params));\r
+ // }\r
+ // else\r
+ // {\r
+ // draw_mixer_buffer(draw_buffer, std::move(local_mix_buffer), core::blend_mode::normal);\r
+ // \r
+ // draw_params.background = draw_buffer;\r
+ // draw_params.local_key = std::move(local_key_buffer);\r
+ // draw_params.layer_key = layer_key_buffer;\r
+\r
+ // kernel_.draw(std::move(draw_params));\r
+ // } \r
+ //}\r
+\r
+ //void draw_mixer_buffer(spl::shared_ptr<device_buffer>& draw_buffer, \r
+ // std::shared_ptr<device_buffer>&& source_buffer, \r
+ // core::blend_mode blend_mode = core::blend_mode::normal)\r
+ //{\r
+ // if(!source_buffer)\r
+ // return;\r
+\r
+ // draw_params draw_params;\r
+ // draw_params.pix_desc.format = core::pixel_format::bgra;\r
+ // draw_params.pix_desc.planes = list_of(core::pixel_format_desc::plane(source_buffer->width(), source_buffer->height(), 4));\r
+ // draw_params.textures = list_of(source_buffer);\r
+ // draw_params.transform = core::frame_transform();\r
+ // draw_params.blend_mode = blend_mode;\r
+ // draw_params.background = draw_buffer;\r
+\r
+ // kernel_.draw(std::move(draw_params));\r
+ //}\r
+ // \r
+ //spl::shared_ptr<device_buffer> create_mixer_buffer(int stride, const core::video_format_desc& format_desc)\r
+ //{\r
+ // auto buffer = ogl_->create_device_buffer(format_desc.width, format_desc.height, stride);\r
+ // buffer->clear();\r
+ // return buffer;\r
+ //}\r
+};\r
+ \r
+struct image_mixer::impl : boost::noncopyable\r
+{ \r
+ image_renderer renderer_;\r
+ std::vector<core::frame_transform> transform_stack_;\r
+ std::vector<layer> layers_; // layer/stream/items\r
+public:\r
+ impl() \r
+ : transform_stack_(1) \r
+ {\r
+ CASPAR_LOG(info) << L"Initialized CPU Accelerated Image Mixer";\r
+ }\r
+\r
+ void begin_layer(core::blend_mode blend_mode)\r
+ {\r
+ layers_.push_back(layer(std::vector<item>()));\r
+ }\r
+ \r
+ void push(core::frame_transform& transform)\r
+ {\r
+ transform_stack_.push_back(transform_stack_.back()*transform);\r
+ }\r
+ \r
+ void visit(core::data_frame& frame2)\r
+ { \r
+ write_frame* frame = dynamic_cast<write_frame*>(&frame2);\r
+ if(frame == nullptr)\r
+ return;\r
+\r
+ if(frame->get_pixel_format_desc().format == core::pixel_format::invalid)\r
+ return;\r
+\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
+ item.transform = transform_stack_.back();\r
+ item.transform.volume = core::frame_transform().volume; // Set volume to default since we don't care about it here.\r
+\r
+ layers_.back().items.push_back(item);\r
+ }\r
+\r
+ void pop()\r
+ {\r
+ transform_stack_.pop_back();\r
+ }\r
+\r
+ void end_layer()\r
+ { \r
+ }\r
+ \r
+ boost::shared_future<boost::iterator_range<const uint8_t*>> render(const core::video_format_desc& format_desc)\r
+ {\r
+ // Remove empty layers.\r
+ boost::range::remove_erase_if(layers_, [](const layer& layer)\r
+ {\r
+ return layer.items.empty();\r
+ });\r
+\r
+ return renderer_(std::move(layers_), format_desc);\r
+ }\r
+ \r
+ virtual spl::shared_ptr<cpu::write_frame> create_frame(const void* tag, const core::pixel_format_desc& desc)\r
+ {\r
+ return spl::make_shared<cpu::write_frame>(tag, desc);\r
+ }\r
+};\r
+\r
+image_mixer::image_mixer() : impl_(new impl()){}\r
+void image_mixer::push(core::frame_transform& transform){impl_->push(transform);}\r
+void image_mixer::visit(core::data_frame& frame){impl_->visit(frame);}\r
+void image_mixer::pop(){impl_->pop();}\r
+boost::shared_future<boost::iterator_range<const uint8_t*>> image_mixer::operator()(const core::video_format_desc& format_desc){return impl_->render(format_desc);}\r
+void image_mixer::begin_layer(core::blend_mode blend_mode){impl_->begin_layer(blend_mode);}\r
+void image_mixer::end_layer(){impl_->end_layer();}\r
+spl::shared_ptr<core::write_frame> image_mixer::create_frame(const void* tag, const core::pixel_format_desc& desc) {return impl_->create_frame(tag, desc);}\r
\r
}}}
\ No newline at end of file
#pragma once\r
\r
+#include <common/forward.h>\r
+#include <common/spl/memory.h>\r
+\r
+#include <core/mixer/image/blend_modes.h>\r
+#include <core/mixer/image/image_mixer.h>\r
+\r
+#include <core/frame/frame_visitor.h>\r
+\r
+FORWARD1(boost, template<typename> class shared_future);\r
+FORWARD1(boost, template<typename> class iterator_range);\r
+FORWARD2(caspar, core, struct write_frame);\r
+FORWARD2(caspar, core, struct pixel_format_desc);\r
+FORWARD2(caspar, core, struct video_format_desc);\r
+FORWARD2(caspar, core, struct data_Frame);\r
+FORWARD2(caspar, core, struct frame_transform);\r
+\r
namespace caspar { namespace accelerator { namespace cpu {\r
+ \r
+class image_mixer sealed : public core::image_mixer\r
+{\r
+public:\r
+ image_mixer();\r
+ \r
+ virtual void push(core::frame_transform& frame);\r
+ virtual void visit(core::data_frame& frame);\r
+ virtual void pop();\r
+\r
+ void begin_layer(core::blend_mode blend_mode);\r
+ void end_layer();\r
+ \r
+ // NOTE: Content of return future is only valid while future is valid.\r
+ virtual ::boost::shared_future<::boost::iterator_range<const uint8_t*>> operator()(const core::video_format_desc& format_desc) override;\r
+ \r
+ virtual spl::shared_ptr<core::write_frame> create_frame(const void* tag, const core::pixel_format_desc& desc) override;\r
+private:\r
+ struct impl;\r
+ spl::shared_ptr<impl> impl_;\r
+};\r
\r
}}}
\ No newline at end of file
--- /dev/null
+/*\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
+*\r
+* This file is part of CasparCG (www.casparcg.com).\r
+*\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
+*\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+*\r
+* Author: Robert Nagy, ronag89@gmail.com\r
+*/\r
+\r
+#include "../../stdafx.h"\r
+\r
+#include "write_frame.h"\r
+\r
+#include <common/except.h>\r
+#include <core/frame/frame_visitor.h>\r
+#include <core/frame/pixel_format.h>\r
+\r
+#include <boost/lexical_cast.hpp>\r
+\r
+namespace caspar { namespace accelerator { namespace cpu {\r
+ \r
+struct write_frame::impl : boost::noncopyable\r
+{ \r
+ std::vector<spl::shared_ptr<host_buffer>> buffers_;\r
+ core::audio_buffer audio_data_;\r
+ const core::pixel_format_desc desc_;\r
+ const void* tag_;\r
+\r
+ impl(const void* tag)\r
+ : desc_(core::pixel_format::invalid)\r
+ , tag_(tag) \r
+ {\r
+ }\r
+\r
+ impl(const void* tag, const core::pixel_format_desc& desc) \r
+ : desc_(desc)\r
+ , tag_(tag)\r
+ {\r
+ std::transform(desc.planes.begin(), desc.planes.end(), std::back_inserter(buffers_), [&](const core::pixel_format_desc::plane& plane)\r
+ {\r
+ return spl::make_shared<host_buffer>(plane.size);\r
+ });\r
+ }\r
+ \r
+ void accept(write_frame& self, core::frame_visitor& visitor)\r
+ {\r
+ visitor.push(self.get_frame_transform());\r
+ visitor.visit(self);\r
+ visitor.pop();\r
+ }\r
+\r
+ boost::iterator_range<uint8_t*> image_data(int index)\r
+ {\r
+ if(index >= buffers_.size() || !buffers_[index]->data())\r
+ BOOST_THROW_EXCEPTION(out_of_range());\r
+ auto ptr = buffers_[index]->data();\r
+ return boost::iterator_range<uint8_t*>(ptr, ptr+buffers_[index]->size());\r
+ }\r
+};\r
+ \r
+write_frame::write_frame(const void* tag) : impl_(new impl(tag)){}\r
+write_frame::write_frame(const void* tag, const core::pixel_format_desc& desc) \r
+ : impl_(new impl(tag, desc)){}\r
+write_frame::write_frame(write_frame&& other) : impl_(std::move(other.impl_)){}\r
+write_frame& write_frame::operator=(write_frame&& other)\r
+{\r
+ impl_ = std::move(other.impl_);\r
+ return *this;\r
+}\r
+void write_frame::swap(write_frame& other){impl_.swap(other.impl_);}\r
+void write_frame::accept(core::frame_visitor& visitor){impl_->accept(*this, visitor);}\r
+const core::pixel_format_desc& write_frame::get_pixel_format_desc() const{return impl_->desc_;}\r
+const boost::iterator_range<const uint8_t*> write_frame::image_data(int index) const{return impl_->image_data(index);}\r
+const core::audio_buffer& write_frame::audio_data() const{return impl_->audio_data_;}\r
+const boost::iterator_range<uint8_t*> write_frame::image_data(int index){return impl_->image_data(index);}\r
+core::audio_buffer& write_frame::audio_data(){return impl_->audio_data_;}\r
+double write_frame::get_frame_rate() const{return 0.0;} // TODO: what's this?\r
+int write_frame::width() const{return impl_->desc_.planes.at(0).width;}\r
+int write_frame::height() const{return impl_->desc_.planes.at(0).height;} \r
+const void* write_frame::tag() const{return impl_->tag_;} \r
+std::vector<spl::shared_ptr<host_buffer>> write_frame::get_buffers(){return impl_->buffers_;}\r
+\r
+}}}
\ No newline at end of file
--- /dev/null
+/*\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
+*\r
+* This file is part of CasparCG (www.casparcg.com).\r
+*\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
+*\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+*\r
+* Author: Robert Nagy, ronag89@gmail.com\r
+*/\r
+\r
+#pragma once\r
+\r
+#include <common/spl/memory.h>\r
+#include <common/forward.h>\r
+\r
+#include <core/frame/write_frame.h>\r
+#include <core/video_format.h>\r
+#include <core/mixer/audio/audio_mixer.h>\r
+\r
+#include <boost/range/iterator_range.hpp>\r
+\r
+#include <stdint.h>\r
+#include <vector>\r
+\r
+FORWARD2(caspar, core, struct frame_visitor);\r
+FORWARD2(caspar, core, struct pixel_format_desc);\r
+\r
+typedef std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>> host_buffer;\r
+\r
+namespace caspar { namespace accelerator { namespace cpu {\r
+ \r
+class write_frame sealed : public core::write_frame\r
+{\r
+ write_frame(const write_frame&);\r
+ write_frame& operator=(const write_frame);\r
+public: \r
+ explicit write_frame(const void* tag);\r
+ explicit write_frame(const void* tag, const core::pixel_format_desc& desc);\r
+\r
+ write_frame(write_frame&& other);\r
+ write_frame& operator=(write_frame&& other);\r
+\r
+ void swap(write_frame& other);\r
+ \r
+ // draw_frame\r
+\r
+ virtual void accept(core::frame_visitor& visitor) override;\r
+\r
+ // data_frame\r
+ \r
+ virtual const core::pixel_format_desc& get_pixel_format_desc() const override;\r
+\r
+ virtual const boost::iterator_range<const uint8_t*> image_data(int index) const override;\r
+ virtual const core::audio_buffer& audio_data() const override;\r
+\r
+ virtual const boost::iterator_range<uint8_t*> image_data(int index) override;\r
+ virtual core::audio_buffer& audio_data() override;\r
+ \r
+ virtual double get_frame_rate() const override;\r
+\r
+ virtual int width() const override;\r
+ virtual int height() const override;\r
+ \r
+ virtual const void* tag() const override; \r
+\r
+ // write_frames\r
+\r
+ std::vector<spl::shared_ptr<host_buffer>> get_buffers(); \r
+private:\r
+ struct impl;\r
+ spl::shared_ptr<impl> impl_;\r
+};\r
+\r
+}}}
\ No newline at end of file
--- /dev/null
+#pragma once\r
+\r
+#include <common/forward.h>\r
+#include <common/spl/memory.h>\r
+\r
+#include <boost/noncopyable.hpp>\r
+\r
+FORWARD2(caspar, core, struct image_mixer);\r
+\r
+namespace caspar { namespace accelerator {\r
+ \r
+struct factory : boost::noncopyable\r
+{\r
+ virtual spl::shared_ptr<core::image_mixer> create_image_mixer() = 0;\r
+};\r
+\r
+}}
\ No newline at end of file
--- /dev/null
+#include "../StdAfx.h"\r
+\r
+#include "factory.h"\r
+\r
+#include "image/image_mixer.h"\r
+#include "util/context.h"\r
+\r
+namespace caspar { namespace accelerator { namespace ogl {\r
+ \r
+struct factory::impl\r
+{\r
+ spl::shared_ptr<context> ogl_;\r
+ \r
+ spl::shared_ptr<core::image_mixer> create_image_mixer()\r
+ {\r
+ return spl::make_shared<ogl::image_mixer>(ogl_);\r
+ }\r
+};\r
+\r
+factory::factory(){}\r
+spl::shared_ptr<core::image_mixer> factory::create_image_mixer(){return impl_->create_image_mixer();}\r
+\r
+}}}
\ No newline at end of file
--- /dev/null
+#pragma once\r
+\r
+#include "../factory.h"\r
+\r
+#include <common/spl/memory.h>\r
+\r
+namespace caspar { namespace accelerator { namespace ogl {\r
+ \r
+class factory : public accelerator::factory\r
+{\r
+public:\r
+ factory();\r
+\r
+ virtual spl::shared_ptr<core::image_mixer> create_image_mixer() override;\r
+private:\r
+ struct impl;\r
+ spl::shared_ptr<impl> impl_;\r
+};\r
+\r
+}}}
\ No newline at end of file
: ogl_(ogl)\r
, kernel_(ogl_)\r
{\r
+ CASPAR_LOG(info) << L"Initialized OpenGL GPU Accelerated Image Mixer";\r
}\r
\r
boost::shared_future<boost::iterator_range<const uint8_t*>> operator()(std::vector<layer> layers, const core::video_format_desc& format_desc)\r
\r
#include <tbb/concurrent_unordered_map.h>\r
#include <tbb/concurrent_queue.h>\r
+#include <tbb/atomic.h>\r
+\r
+tbb::atomic<int> g_count = tbb::atomic<int>();\r
\r
namespace caspar { namespace accelerator { namespace ogl {\r
\r
: executor_(executor)\r
, host_alloc_executor_(L"OpenGL allocation context")\r
{\r
+ if(g_count++ > 1)\r
+ CASPAR_LOG(warning) << L"Multiple OGL contexts.";\r
+\r
CASPAR_LOG(info) << L"Initializing OpenGL Device.";\r
\r
auto ctx1 = executor_.invoke([=]() -> HGLRC \r
\r
#pragma once\r
\r
-#include "context.h"\r
-\r
#include <common/spl/memory.h>\r
#include <common/forward.h>\r
\r
#include <core/video_format.h>\r
#include <core/mixer/audio/audio_mixer.h>\r
\r
-#include <boost/range/iterator_range.hpp>\r
-\r
#include <stdint.h>\r
#include <vector>\r
\r
+FORWARD1(boost, template <typename> class iterator_range);\r
FORWARD2(caspar, core, struct frame_visitor);\r
FORWARD2(caspar, core, struct pixel_format_desc);\r
\r
typedef D deleter_type;\r
\r
unique_ptr()\r
- : p_(new t())\r
+ : p_(new T())\r
{\r
}\r
+ \r
+ template<typename T2, typename D2> \r
+ unique_ptr(unique_ptr<T2, D2>&& p, typename std::enable_if<std::is_convertible<T2*, T*>::value, void*>::type = 0) \r
+ : p_(std::move(p))\r
+ {\r
+ }\r
\r
template<typename T2, typename D2> \r
explicit unique_ptr(std::unique_ptr<T2, D2>&& p, typename std::enable_if<std::is_convertible<T2*, T*>::value, void*>::type = 0) \r
throw std::invalid_argument("p");\r
}\r
\r
- unique_ptr<T> operator=(unique_ptr&& other)\r
+ unique_ptr<T>& operator=(unique_ptr&& other)\r
{\r
other.swap(*this);\r
return *this;\r
}\r
- \r
+ \r
T& operator*() const \r
{ \r
return *p_.get();\r
{ \r
p_.swap(other.p_); \r
} \r
-\r
- template<typename T2>\r
- operator std::unique_ptr<T2>() const \r
- { \r
- return p_;\r
- }\r
-\r
+ \r
template<class D, class T2> \r
D* get_deleter(shared_ptr<T2> const& ptr) \r
{ \r
#include <common/spl/memory.h>\r
#include <common/tweener.h>\r
\r
+#include <boost/optional.hpp>\r
#include <boost/property_tree/ptree_fwd.hpp>\r
\r
#include <functional>\r
\r
void init()\r
{\r
- if(!GLEW_VERSION_2_1)\r
- BOOST_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 2.1 support."));\r
-\r
window_.Create(sf::VideoMode(screen_width_, screen_height_, 32), u8(print()), config_.windowed ? sf::Style::Resize | sf::Style::Close : sf::Style::Fullscreen);\r
window_.ShowMouseCursor(false);\r
window_.SetPosition(screen_x_, screen_y_);\r
window_.SetSize(screen_width_, screen_height_);\r
window_.SetActive();\r
+ \r
+ if(!GLEW_VERSION_2_1 && glewInit() != GLEW_OK)\r
+ BOOST_THROW_EXCEPTION(gl::ogl_exception() << msg_info("Failed to initialize GLEW."));\r
+\r
+ if(!GLEW_VERSION_2_1)\r
+ BOOST_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 2.1 support."));\r
\r
GL(glEnable(GL_TEXTURE_2D));\r
GL(glDisable(GL_DEPTH_TEST)); \r
<data-path>D:\casparcg\_data\</data-path>\r
<template-path>D:\casparcg\_templates\</template-path>\r
</paths>\r
+ <accelerator>cpu</accelerator>\r
<blend-modes>true</blend-modes>\r
<log-level>trace</log-level>\r
<channels>\r
<channel>\r
- <video-mode>1080i5000</video-mode>\r
+ <video-mode>720p5000</video-mode>\r
<consumers>\r
<screen>\r
</screen>\r
<blend-modes>false [true|false]</blend-modes>\r
<auto-transcode>true [true|false]</auto-transcode>\r
<pipeline-tokens>2 [1..]</pipeline-tokens>\r
+<accelerator>auto [cpu|gpu|auto]</accelerator>\r
<template-hosts>\r
<template-host>\r
<video-mode/>\r
\r
#include "server.h"\r
\r
-#include <accelerator/ogl/util/context.h>\r
-#include <accelerator/ogl/image/image_mixer.h>\r
+#include <accelerator/factory.h>\r
+#include <accelerator/ogl/factory.h>\r
+#include <accelerator/cpu/factory.h>\r
\r
#include <common/env.h>\r
#include <common/except.h>\r
\r
struct server::impl : boost::noncopyable\r
{\r
- spl::shared_ptr<accelerator::ogl::context> ogl_;\r
+ std::unique_ptr<accelerator::factory> accel_factory_;\r
std::vector<spl::shared_ptr<IO::AsyncEventServer>> async_servers_; \r
std::vector<spl::shared_ptr<video_channel>> channels_;\r
\r
impl() \r
- { \r
+ { \r
+ auto accel_str = env::properties().get(L"configuration.accelerator", L"auto");\r
+\r
+ if(accel_str == L"cpu")\r
+ accel_factory_.reset(new accelerator::cpu::factory());\r
+ else\r
+ {\r
+ try\r
+ {\r
+ accel_factory_.reset(new accelerator::ogl::factory());\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ accel_factory_.reset(new accelerator::cpu::factory());\r
+ CASPAR_LOG(warning) << L"Using fallback CPU mixer.";\r
+ }\r
+ }\r
+\r
ffmpeg::init();\r
CASPAR_LOG(info) << L"Initialized ffmpeg module.";\r
\r
if(format_desc.format == video_format::invalid)\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Invalid video-mode."));\r
\r
- channels_.push_back(spl::make_shared<video_channel>(static_cast<int>(channels_.size()+1), format_desc, spl::make_shared<accelerator::ogl::image_mixer>(ogl_)));\r
+ channels_.push_back(spl::make_shared<video_channel>(static_cast<int>(channels_.size()+1), format_desc, accel_factory_->create_image_mixer()));\r
\r
BOOST_FOREACH(auto& xml_consumer, xml_channel.second.get_child(L"consumers"))\r
{\r
\r
// Dummy diagnostics channel\r
if(env::properties().get(L"configuration.channel-grid", false))\r
- channels_.push_back(spl::make_shared<video_channel>(static_cast<int>(channels_.size()+1), core::video_format_desc(core::video_format::x576p2500), spl::make_shared<accelerator::ogl::image_mixer>(ogl_)));\r
+ channels_.push_back(spl::make_shared<video_channel>(static_cast<int>(channels_.size()+1), core::video_format_desc(core::video_format::x576p2500), accel_factory_->create_image_mixer()));\r
}\r
\r
void setup_controllers(const boost::property_tree::wptree& pt)\r