#include <boost/property_tree/ptree.hpp>\r
\r
namespace caspar { namespace core {\r
+\r
+const long SEND_TIMEOUT_MILLIS = 10000L;\r
\r
struct output::implementation\r
{ \r
\r
try\r
{\r
- if (!result_future.timed_wait(boost::posix_time::seconds(2)))\r
+ if (!result_future.timed_wait(boost::posix_time::seconds(SEND_TIMEOUT_MILLIS)))\r
{\r
BOOST_THROW_EXCEPTION(timed_out() << msg_info(narrow(print()) + " " + narrow(consumer->print()) + " Timed out during send"));\r
}\r
consumer->initialize(format_desc_, audio_channel_layout_, channel_index_);\r
auto retry_future = consumer->send(frame);\r
\r
- if (!retry_future.timed_wait(boost::posix_time::seconds(2)))\r
+ if (!retry_future.timed_wait(boost::posix_time::seconds(SEND_TIMEOUT_MILLIS)))\r
{\r
BOOST_THROW_EXCEPTION(timed_out() << msg_info(narrow(print()) + " " + narrow(consumer->print()) + " Timed out during retry"));\r
}\r
\r
#include <tbb/atomic.h>\r
\r
+#include <boost/property_tree/ptree.hpp>\r
+\r
namespace caspar { namespace core {\r
\r
static GLenum FORMAT[] = {0, GL_RED, GL_RG, GL_BGR, GL_BGRA};\r
return FORMAT[stride];\r
}\r
\r
+static tbb::atomic<int> g_instance_id;\r
static tbb::atomic<int> g_total_count;\r
+static tbb::atomic<int> g_total_size;\r
\r
struct device_buffer::implementation : boost::noncopyable\r
{\r
GLuint id_;\r
+ int instance_id_;\r
\r
const size_t width_;\r
const size_t height_;\r
const size_t stride_;\r
+ const size_t size_;\r
const bool mipmapped_;\r
\r
fence fence_;\r
\r
public:\r
implementation(size_t width, size_t height, size_t stride, bool mipmapped) \r
- : width_(width)\r
+ : instance_id_(++instance_id_)\r
+ , width_(width)\r
, height_(height)\r
, stride_(stride)\r
+ , size_(static_cast<size_t>(width * height * stride * (mipmapped ? 1.33 : 1.0)))\r
, mipmapped_(mipmapped)\r
{ \r
GL(glGenTextures(1, &id_));\r
}\r
\r
GL(glBindTexture(GL_TEXTURE_2D, 0));\r
- CASPAR_LOG(trace) << "[device_buffer] [" << ++g_total_count << L"] allocated size:" << width*height*stride; \r
+ g_total_size += size_;\r
+ ++g_total_count;\r
+ CASPAR_LOG(trace) << "[device_buffer] [" << instance_id_ << L"] allocated size:" << size_ << " for a total of: " << g_total_size;\r
}\r
\r
void enable_anosotropic_filtering_if_available()\r
try\r
{\r
GL(glDeleteTextures(1, &id_));\r
- //CASPAR_LOG(trace) << "[device_buffer] [" << --g_total_count << L"] deallocated size:" << width_*height_*stride_;\r
+ g_total_size -= size_;\r
+ --g_total_count;\r
+ CASPAR_LOG(trace) << "[device_buffer] [" << instance_id_ << L"] deallocated size:" << size_ << " for a remaining total of: " << g_total_size;\r
}\r
catch(...)\r
{\r
size_t device_buffer::stride() const { return impl_->stride_; }\r
size_t device_buffer::width() const { return impl_->width_; }\r
size_t device_buffer::height() const { return impl_->height_; }\r
+size_t device_buffer::size() const { return impl_->size_; }\r
void device_buffer::bind(int index){impl_->bind(index);}\r
void device_buffer::unbind(){impl_->unbind();}\r
void device_buffer::begin_read(){impl_->begin_read();}\r
bool device_buffer::ready() const{return impl_->ready();}\r
int device_buffer::id() const{ return impl_->id_;}\r
\r
+boost::property_tree::wptree device_buffer::info()\r
+{\r
+ boost::property_tree::wptree info;\r
+\r
+ info.add(L"total_count", g_total_count);\r
+ info.add(L"total_size", g_total_size);\r
+\r
+ return info;\r
+}\r
\r
-}}
\ No newline at end of file
+}}\r
#include <common/memory/safe_ptr.h>\r
\r
#include <boost/noncopyable.hpp>\r
+#include <boost/property_tree/ptree_fwd.hpp>\r
\r
#include <memory>\r
\r
size_t stride() const; \r
size_t width() const;\r
size_t height() const;\r
+ size_t size() const;\r
bool mipmapped() const;\r
\r
void bind(int index);\r
\r
void begin_read();\r
bool ready() const;\r
+\r
+ static boost::property_tree::wptree info();\r
private:\r
friend class ogl_device;\r
device_buffer(size_t width, size_t height, size_t stride, bool mipmapped);\r
\r
#include <tbb/atomic.h>\r
\r
+#include <boost/property_tree/ptree.hpp>\r
+\r
namespace caspar { namespace core {\r
\r
+static tbb::atomic<int> g_w_instance_id;\r
static tbb::atomic<int> g_w_total_count;\r
+static tbb::atomic<int> g_w_total_size;\r
+static tbb::atomic<int> g_r_instance_id;\r
static tbb::atomic<int> g_r_total_count;\r
+static tbb::atomic<int> g_r_total_size;\r
\r
struct host_buffer::implementation : boost::noncopyable\r
-{ \r
+{\r
+ int instance_id_;\r
GLuint pbo_;\r
const size_t size_;\r
void* data_;\r
- GLenum usage_;\r
+ usage_t usage_;\r
GLenum target_;\r
fence fence_;\r
\r
public:\r
implementation(size_t size, usage_t usage) \r
- : size_(size)\r
+ : instance_id_(++(usage == write_only ? g_w_instance_id : g_r_instance_id))\r
+ , size_(size)\r
, data_(nullptr)\r
, pbo_(0)\r
, target_(usage == write_only ? GL_PIXEL_UNPACK_BUFFER : GL_PIXEL_PACK_BUFFER)\r
- , usage_(usage == write_only ? GL_STREAM_DRAW : GL_STREAM_READ)\r
+ , usage_(usage)\r
{\r
GL(glGenBuffers(1, &pbo_));\r
GL(glBindBuffer(target_, pbo_));\r
- if(usage_ != write_only) \r
- GL(glBufferData(target_, size_, NULL, usage_)); \r
+ GL(glBufferData(target_, size_, NULL, usage_ == write_only ? GL_STREAM_DRAW : GL_STREAM_READ));\r
GL(glBindBuffer(target_, 0));\r
\r
if(!pbo_)\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Failed to allocate buffer."));\r
\r
- CASPAR_LOG(trace) << "[host_buffer] [" << ++(usage_ == write_only ? g_w_total_count : g_r_total_count) << L"] allocated size:" << size_ << " usage: " << (usage == write_only ? "write_only" : "read_only");\r
+ ++(usage_ == write_only ? g_w_total_count : g_r_total_count);\r
+ auto total_size = (usage_ == write_only ? g_w_total_size : g_r_total_size) += size_;\r
+ CASPAR_LOG(trace) << "[host_buffer] [" << instance_id_ << L"] allocated size:" << size_ << " (total: " << total_size << ") usage: " << (usage_ == write_only ? "write_only" : "read_only");\r
} \r
\r
~implementation()\r
try\r
{\r
GL(glDeleteBuffers(1, &pbo_));\r
- //CASPAR_LOG(trace) << "[host_buffer] [" << --(usage_ == write_only ? g_w_total_count : g_r_total_count) << L"] deallocated size:" << size_ << " usage: " << (usage_ == write_only ? "write_only" : "read_only");\r
+ --(usage_ == write_only ? g_w_total_count : g_r_total_count);\r
+ auto total_size = (usage_ == write_only ? g_w_total_size : g_r_total_size) -= size_;\r
+ CASPAR_LOG(trace) << "[host_buffer] [" << instance_id_ << L"] deallocated size:" << size_ << "(remaining total: " << total_size << ") usage: " << (usage_ == write_only ? "write_only" : "read_only");\r
}\r
catch(...)\r
{\r
if(data_)\r
return;\r
\r
+ GL(glBindBuffer(target_, pbo_));\r
+\r
if(usage_ == write_only) \r
- GL(glBufferData(target_, size_, NULL, usage_)); // Notify OpenGL that we don't care about previous data.\r
+ GL(glBufferData(target_, size_, NULL, GL_STREAM_DRAW)); // Notify OpenGL that we don't care about previous data.\r
\r
- GL(glBindBuffer(target_, pbo_));\r
- data_ = GL2(glMapBuffer(target_, usage_ == GL_STREAM_DRAW ? GL_WRITE_ONLY : GL_READ_ONLY)); \r
+ data_ = GL2(glMapBuffer(target_, usage_ == host_buffer::write_only ? GL_WRITE_ONLY : GL_READ_ONLY));\r
GL(glBindBuffer(target_, 0)); \r
if(!data_)\r
BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("Failed to map target_ OpenGL Pixel Buffer Object."));\r
bool host_buffer::ready() const{return impl_->ready();}\r
void host_buffer::wait(ogl_device& ogl){impl_->wait(ogl);}\r
\r
+boost::property_tree::wptree host_buffer::info()\r
+{\r
+ boost::property_tree::wptree info;\r
+\r
+ info.add(L"total_read_count", g_r_total_count);\r
+ info.add(L"total_write_count", g_w_total_count);\r
+ info.add(L"total_read_size", g_r_total_size);\r
+ info.add(L"total_write_size", g_w_total_size);\r
+\r
+ return info;\r
+}\r
+\r
}}
\ No newline at end of file
#pragma once\r
\r
#include <boost/noncopyable.hpp>\r
+#include <boost/property_tree/ptree_fwd.hpp>\r
\r
#include <common/memory/safe_ptr.h>\r
\r
void begin_read(size_t width, size_t height, unsigned int format);\r
bool ready() const;\r
void wait(ogl_device& ogl);\r
+\r
+ static boost::property_tree::wptree info();\r
private:\r
friend class ogl_device;\r
host_buffer(size_t size, usage_t usage);\r
#include <common/gl/gl_check.h>\r
\r
#include <boost/foreach.hpp>\r
+#include <boost/property_tree/ptree.hpp>\r
\r
#include <gl/glew.h>\r
\r
executor_.yield();\r
}\r
\r
+boost::property_tree::wptree ogl_device::info() const\r
+{\r
+ boost::property_tree::wptree info;\r
+\r
+ boost::property_tree::wptree pooled_device_buffers;\r
+ size_t total_pooled_device_buffer_size = 0;\r
+ size_t total_pooled_device_buffer_count = 0;\r
+\r
+ for (size_t i = 0; i < device_pools_.size(); ++i)\r
+ {\r
+ auto& pools = device_pools_.at(i);\r
+ bool mipmapping = i > 3;\r
+ int stride = mipmapping ? i - 3 : i + 1;\r
+\r
+ BOOST_FOREACH(auto& pool, pools)\r
+ {\r
+ auto width = pool.first >> 16;\r
+ auto height = pool.first & 0x0000FFFF;\r
+ auto size = width * height * stride;\r
+ auto count = pool.second->items.size();\r
+\r
+ if (count == 0)\r
+ continue;\r
+\r
+ boost::property_tree::wptree pool_info;\r
+\r
+ pool_info.add(L"stride", stride);\r
+ pool_info.add(L"mipmapping", mipmapping);\r
+ pool_info.add(L"width", width);\r
+ pool_info.add(L"height", height);\r
+ pool_info.add(L"size", size);\r
+ pool_info.add(L"count", count);\r
+\r
+ total_pooled_device_buffer_size += size * count;\r
+ total_pooled_device_buffer_count += count;\r
+\r
+ pooled_device_buffers.add_child(L"device_buffer_pool", pool_info);\r
+ }\r
+ }\r
+\r
+ info.add_child(L"gl.details.pooled_device_buffers", pooled_device_buffers);\r
+\r
+ boost::property_tree::wptree pooled_host_buffers;\r
+ size_t total_read_size = 0;\r
+ size_t total_write_size = 0;\r
+ size_t total_read_count = 0;\r
+ size_t total_write_count = 0;\r
+\r
+ for (size_t i = 0; i < host_pools_.size(); ++i)\r
+ {\r
+ auto& pools = host_pools_.at(i);\r
+ host_buffer::usage_t usage = static_cast<host_buffer::usage_t>(i);\r
+\r
+ BOOST_FOREACH(auto& pool, pools)\r
+ {\r
+ auto size = pool.first;\r
+ auto count = pool.second->items.size();\r
+\r
+ if (count == 0)\r
+ continue;\r
+\r
+ boost::property_tree::wptree pool_info;\r
+\r
+ pool_info.add(L"usage", usage == host_buffer::read_only\r
+ ? L"read_only" : L"write_only");\r
+ pool_info.add(L"size", size);\r
+ pool_info.add(L"count", count);\r
+\r
+ pooled_host_buffers.add_child(L"host_buffer_pool", pool_info);\r
+\r
+ (usage == host_buffer::read_only\r
+ ? total_read_count : total_write_count) += count;\r
+ (usage == host_buffer::read_only\r
+ ? total_read_size : total_write_size) += size * count;\r
+ }\r
+ }\r
+\r
+ info.add_child(L"gl.details.pooled_host_buffers", pooled_host_buffers);\r
+\r
+ info.add(L"gl.summary.pooled_device_buffers.total_count", total_pooled_device_buffer_count);\r
+ info.add(L"gl.summary.pooled_device_buffers.total_size", total_pooled_device_buffer_size);\r
+ info.add_child(L"gl.summary.all_device_buffers", device_buffer::info());\r
+ info.add(L"gl.summary.pooled_host_buffers.total_read_count", total_read_count);\r
+ info.add(L"gl.summary.pooled_host_buffers.total_write_count", total_write_count);\r
+ info.add(L"gl.summary.pooled_host_buffers.total_read_size", total_read_size);\r
+ info.add(L"gl.summary.pooled_host_buffers.total_write_size", total_write_size);\r
+ info.add_child(L"gl.summary.all_host_buffers", host_buffer::info());\r
+\r
+ return info;\r
+}\r
+\r
boost::unique_future<void> ogl_device::gc()\r
{ \r
return begin_invoke([=]\r
\r
#include <boost/noncopyable.hpp>\r
#include <boost/thread/future.hpp>\r
+#include <boost/property_tree/ptree_fwd.hpp>\r
\r
#include <array>\r
#include <unordered_map>\r
safe_ptr<host_buffer> create_host_buffer(size_t size, host_buffer::usage_t usage);\r
\r
void yield();\r
+ boost::property_tree::wptree info() const;\r
boost::unique_future<void> gc();\r
\r
std::wstring version();\r
#include <core/consumer/frame_consumer.h>\r
#include <core/parameters/parameters.h>\r
#include <core/video_channel.h>\r
+#include <core/mixer/gpu/ogl_device.h>\r
#include <core/thumbnail_generator.h>\r
\r
#include <boost/algorithm/string.hpp>\r
void SetLayerIntex(int layerIndex){layerIndex_ = layerIndex;}\r
int GetLayerIndex(int defaultValue = 0) const{return layerIndex_ != -1 ? layerIndex_ : defaultValue;}\r
\r
+ void SetOglDevice(const safe_ptr<core::ogl_device>& device){ogl_ = device;}\r
+ std::shared_ptr<core::ogl_device> GetOglDevice() const { return ogl_; }\r
+\r
virtual void Clear();\r
\r
AMCPCommandScheduling GetScheduling()\r
unsigned int channelIndex_;\r
int layerIndex_;\r
IO::ClientInfoPtr pClientInfo_;\r
+ std::shared_ptr<core::ogl_device> ogl_;\r
std::shared_ptr<core::video_channel> pChannel_;\r
std::vector<safe_ptr<core::video_channel>> channels_;\r
std::shared_ptr<core::thumbnail_generator> thumb_gen_;\r
return true;\r
}\r
\r
+bool GlCommand::DoExecute()\r
+{\r
+ try\r
+ {\r
+ std::wstring command = _parameters.at(0);\r
+\r
+ if (command == TEXT("GC"))\r
+ return DoExecuteGc();\r
+ else if (command == TEXT("INFO"))\r
+ return DoExecuteInfo();\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ }\r
+\r
+ SetReplyString(TEXT("403 GL ERROR\r\n"));\r
+ return false;\r
+}\r
+\r
+bool GlCommand::DoExecuteGc()\r
+{\r
+ if (!GetOglDevice()->gc().timed_wait(boost::posix_time::seconds(2)))\r
+ BOOST_THROW_EXCEPTION(timed_out());\r
+\r
+ SetReplyString(TEXT("202 GL GC OK\r\n"));\r
+ return true;\r
+}\r
+\r
+bool GlCommand::DoExecuteInfo()\r
+{\r
+ std::wstringstream reply_string;\r
+ boost::property_tree::xml_writer_settings<wchar_t> w(' ', 3);\r
+\r
+ auto info = GetOglDevice()->info();\r
+\r
+ reply_string << L"201 GL INFO OK\r\n";\r
+ boost::property_tree::write_xml(reply_string, info, w);\r
+ reply_string << L"\r\n";\r
+\r
+ SetReplyString(reply_string.str());\r
+\r
+ return true;\r
+}\r
+\r
bool KillCommand::DoExecute()\r
{\r
GetShutdownServerNow().set_value(false); // False for not attempting to restart.\r
bool DoExecute();\r
};\r
\r
+class GlCommand : public AMCPCommandBase<false, AddToQueue, 0>\r
+{\r
+ std::wstring print() const { return L"GlCommand";}\r
+ bool DoExecute();\r
+ bool DoExecuteInfo();\r
+ bool DoExecuteGc();\r
+};\r
+\r
class RestartCommand : public AMCPCommandBase<false, AddToQueue, 0>\r
{\r
std::wstring print() const { return L"RestartCommand";}\r
const std::vector<safe_ptr<core::video_channel>>& channels,\r
const std::shared_ptr<core::thumbnail_generator>& thumb_gen,\r
const safe_ptr<core::media_info_repository>& media_info_repo,\r
+ const safe_ptr<core::ogl_device>& ogl_device,\r
boost::promise<bool>& shutdown_server_now)\r
: channels_(channels)\r
, thumb_gen_(thumb_gen)\r
, media_info_repo_(media_info_repo)\r
+ , ogl_(ogl_device)\r
, shutdown_server_now_(shutdown_server_now)\r
{\r
AMCPCommandQueuePtr pGeneralCommandQueue(new AMCPCommandQueue());\r
pCommand->SetChannels(channels_);\r
pCommand->SetThumbGenerator(thumb_gen_);\r
pCommand->SetMediaInfoRepo(media_info_repo_);\r
+ pCommand->SetOglDevice(ogl_);\r
pCommand->SetShutdownServerNow(shutdown_server_now_);\r
//Set scheduling\r
if(commandSwitch.size() > 0) {\r
else if(s == TEXT("VERSION")) return std::make_shared<VersionCommand>();\r
else if(s == TEXT("BYE")) return std::make_shared<ByeCommand>();\r
else if(s == TEXT("SET")) return std::make_shared<SetCommand>();\r
+ else if(s == TEXT("GL")) return std::make_shared<GlCommand>();\r
else if(s == TEXT("THUMBNAIL")) return std::make_shared<ThumbnailCommand>();\r
//else if(s == TEXT("MONITOR"))\r
//{\r
const std::vector<safe_ptr<core::video_channel>>& channels,\r
const std::shared_ptr<core::thumbnail_generator>& thumb_gen,\r
const safe_ptr<core::media_info_repository>& media_info_repo,\r
+ const safe_ptr<core::ogl_device>& ogl_device,\r
boost::promise<bool>& shutdown_server_now);\r
virtual ~AMCPProtocolStrategy();\r
\r
std::vector<safe_ptr<core::video_channel>> channels_;\r
std::shared_ptr<core::thumbnail_generator> thumb_gen_;\r
safe_ptr<core::media_info_repository> media_info_repo_;\r
+ safe_ptr<core::ogl_device> ogl_;\r
boost::promise<bool>& shutdown_server_now_;\r
std::vector<AMCPCommandQueuePtr> commandQueues_;\r
static const std::wstring MessageDelimiter;\r
caspar_server.get_channels(),\r
caspar_server.get_thumbnail_generator(),\r
caspar_server.get_media_info_repo(),\r
+ caspar_server.get_ogl_device(),\r
shutdown_server_now);\r
\r
// Create a dummy client which prints amcp responses to console.\r
safe_ptr<IO::IProtocolStrategy> create_protocol(const std::wstring& name) const\r
{\r
if(boost::iequals(name, L"AMCP"))\r
- return make_safe<amcp::AMCPProtocolStrategy>(channels_, thumbnail_generator_, media_info_repo_, shutdown_server_now_);\r
+ return make_safe<amcp::AMCPProtocolStrategy>(channels_, thumbnail_generator_, media_info_repo_, ogl_, shutdown_server_now_);\r
else if(boost::iequals(name, L"CII"))\r
return make_safe<cii::CIIProtocolStrategy>(channels_);\r
else if(boost::iequals(name, L"CLOCK"))\r
return impl_->media_info_repo_;\r
}\r
\r
+safe_ptr<ogl_device> server::get_ogl_device() const\r
+{\r
+ return impl_->ogl_;\r
+}\r
+\r
core::monitor::subject& server::monitor_output()\r
{\r
return *impl_->monitor_subject_;\r
class video_channel;\r
class thumbnail_generator;\r
struct media_info_repository;\r
+ class ogl_device;\r
}\r
\r
class server : boost::noncopyable\r
const std::vector<safe_ptr<core::video_channel>> get_channels() const;\r
std::shared_ptr<core::thumbnail_generator> get_thumbnail_generator() const;\r
safe_ptr<core::media_info_repository> get_media_info_repo() const;\r
+ safe_ptr<core::ogl_device> get_ogl_device() const;\r
\r
core::monitor::subject& monitor_output();\r
\r