<ClInclude Include="config.h" />\r
<ClInclude Include="exception\exceptions.h" />\r
<ClInclude Include="exception\win32_exception.h" />\r
+ <ClInclude Include="gl\frame_buffer_object.h" />\r
<ClInclude Include="gl\gl_check.h" />\r
+ <ClInclude Include="gl\pixel_buffer_object.h" />\r
<ClInclude Include="io\AsyncEventServer.h" />\r
<ClInclude Include="io\ClientInfo.h" />\r
<ClInclude Include="io\ProtocolStrategy.h" />\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
</ClCompile>\r
+ <ClCompile Include="gl\frame_buffer_object.cpp">\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
+ </ClCompile>\r
+ <ClCompile Include="gl\pixel_buffer_object.cpp">\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
+ </ClCompile>\r
<ClCompile Include="io\AsyncEventServer.cpp">\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<ClCompile Include="utility\memory.cpp">\r
<Filter>Source\utility</Filter>\r
</ClCompile>\r
+ <ClCompile Include="gl\frame_buffer_object.cpp">\r
+ <Filter>Source\gl</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="gl\pixel_buffer_object.cpp">\r
+ <Filter>Source\gl</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="stdafx.h">\r
<ClInclude Include="gl\gl_check.h">\r
<Filter>Source\gl</Filter>\r
</ClInclude>\r
+ <ClInclude Include="gl\pixel_buffer_object.h">\r
+ <Filter>Source\gl</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="gl\frame_buffer_object.h">\r
+ <Filter>Source\gl</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
--- /dev/null
+#include "../StdAfx.h"\r
+\r
+#include "frame_buffer_object.h"\r
+\r
+#include "../../common/gl/gl_check.h"\r
+\r
+#include <Glee.h>\r
+\r
+#include <memory>\r
+\r
+namespace caspar { namespace common { namespace gl {\r
+\r
+struct frame_buffer_object::implementation\r
+{\r
+public:\r
+ implementation(size_t width, size_t height, GLenum mode) : mode_(mode)\r
+ {\r
+ GL(glGenTextures(1, &texture_)); \r
+ GL(glBindTexture(GL_TEXTURE_2D, texture_)); \r
+ GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, \r
+ GL_UNSIGNED_BYTE, NULL));\r
+ GL(glGenFramebuffersEXT(1, &fbo_)); \r
+ GL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_));\r
+ GL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, mode_, GL_TEXTURE_2D, \r
+ texture_, 0));\r
+ }\r
+ \r
+ ~implementation()\r
+ {\r
+ glDeleteFramebuffersEXT(1, &fbo_);\r
+ glDeleteTextures(1, &texture_);\r
+ }\r
+\r
+ void bind_pixel_source()\r
+ {\r
+ GL(glReadBuffer(mode_));\r
+ }\r
+\r
+ GLuint texture_;\r
+ GLuint fbo_;\r
+ GLenum mode_;\r
+ size_t width_;\r
+ size_t height_;\r
+};\r
+\r
+frame_buffer_object::frame_buffer_object(){}\r
+frame_buffer_object::frame_buffer_object(size_t width, size_t height, GLenum mode)\r
+ : impl_(new implementation(width, height, mode)){}\r
+void frame_buffer_object::create(size_t width, size_t height, GLenum mode)\r
+{\r
+ impl_.reset(new implementation(width, height, mode));\r
+}\r
+void frame_buffer_object::bind_pixel_source() {impl_->bind_pixel_source();}\r
+}}}
\ No newline at end of file
--- /dev/null
+#pragma once\r
+\r
+#include <Glee.h>\r
+\r
+#include <memory>\r
+\r
+namespace caspar { namespace common { namespace gl {\r
+\r
+class frame_buffer_object\r
+{\r
+public:\r
+ frame_buffer_object();\r
+ frame_buffer_object(size_t width, size_t height, GLenum mode = GL_COLOR_ATTACHMENT0_EXT);\r
+ void create(size_t width, size_t height, GLenum mode = GL_COLOR_ATTACHMENT0_EXT);\r
+ void bind_pixel_source();\r
+private:\r
+ struct implementation;\r
+ std::shared_ptr<implementation> impl_;\r
+};\r
+\r
+}}}
\ No newline at end of file
--- /dev/null
+#include "../StdAfx.h"\r
+\r
+#include "pixel_buffer_object.h"\r
+\r
+#include "../../common/exception/exceptions.h"\r
+#include "../../common/gl/gl_check.h"\r
+#include "../../common/utility/memory.h"\r
+\r
+namespace caspar { namespace common { namespace gl {\r
+ \r
+struct pixel_buffer_object::implementation : boost::noncopyable\r
+{\r
+ implementation(size_t width, size_t height) \r
+ : width_(width), height_(height), size_(width*height*4), pbo_(0),\r
+ texture_(0), writing_(false), reading_(false), mapped_(false){}\r
+\r
+ ~implementation()\r
+ {\r
+ if(pbo_ != 0)\r
+ glDeleteBuffers(1, &pbo_);\r
+ } \r
+\r
+ void bind_pbo(GLenum mode)\r
+ {\r
+ if(pbo_ == 0)\r
+ GL(glGenBuffers(1, &pbo_));\r
+ GL(glBindBuffer(mode, pbo_));\r
+ }\r
+\r
+ void unbind_pbo(GLenum mode)\r
+ {\r
+ GL(glBindBuffer(mode, 0));\r
+ }\r
+ \r
+ void bind_texture()\r
+ {\r
+ if(texture_ == 0)\r
+ {\r
+ GL(glGenTextures(1, &texture_));\r
+\r
+ GL(glBindTexture(GL_TEXTURE_2D, texture_));\r
+\r
+ GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));\r
+ GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));\r
+ GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));\r
+ GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));\r
+\r
+ GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width_, height_, 0, GL_BGRA, \r
+ GL_UNSIGNED_BYTE, NULL));\r
+ }\r
+ GL(glBindTexture(GL_TEXTURE_2D, texture_));\r
+ }\r
+\r
+ void begin_write()\r
+ {\r
+ bind_pbo(GL_PIXEL_UNPACK_BUFFER);\r
+ if(mapped_)\r
+ GL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));\r
+ mapped_ = false;\r
+ bind_texture();\r
+ GL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_BGRA, \r
+ GL_UNSIGNED_BYTE, NULL));\r
+ unbind_pbo(GL_PIXEL_UNPACK_BUFFER);\r
+ writing_ = true;\r
+ }\r
+\r
+ void* end_write()\r
+ {\r
+ if(mapped_)\r
+ BOOST_THROW_EXCEPTION(invalid_operation());\r
+\r
+ bind_pbo(GL_PIXEL_UNPACK_BUFFER);\r
+ GL(glBufferData(GL_PIXEL_UNPACK_BUFFER, size_, NULL, GL_STREAM_DRAW));\r
+ auto data = static_cast<unsigned char*>(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));\r
+ unbind_pbo(GL_PIXEL_UNPACK_BUFFER); \r
+ if(!data)\r
+ BOOST_THROW_EXCEPTION(invalid_operation() \r
+ << msg_info("glMapBuffer failed"));\r
+ writing_ = false;\r
+ mapped_ = true;\r
+ return data;\r
+ }\r
+ \r
+ void begin_read()\r
+ { \r
+ bind_pbo(GL_PIXEL_PACK_BUFFER);\r
+ if(mapped_)\r
+ GL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER)); \r
+ mapped_ = false;\r
+ GL(glBufferData(GL_PIXEL_PACK_BUFFER, size_, NULL, GL_STREAM_READ)); \r
+ GL(glReadPixels(0, 0, width_, height_, GL_BGRA, GL_UNSIGNED_BYTE, NULL));\r
+ unbind_pbo(GL_PIXEL_PACK_BUFFER);\r
+ reading_ = true;\r
+ }\r
+\r
+ void* end_read()\r
+ {\r
+ if(mapped_)\r
+ BOOST_THROW_EXCEPTION(invalid_operation());\r
+\r
+ bind_pbo(GL_PIXEL_PACK_BUFFER);\r
+ auto data = static_cast<unsigned char*>(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY)); \r
+ unbind_pbo(GL_PIXEL_PACK_BUFFER);\r
+ if(!data)\r
+ BOOST_THROW_EXCEPTION(std::bad_alloc());\r
+ reading_ = false;\r
+ mapped_ = true;\r
+ return data;\r
+ }\r
+\r
+ \r
+ GLuint pbo_;\r
+ GLuint texture_;\r
+ size_t width_;\r
+ size_t height_;\r
+ size_t size_;\r
+\r
+ bool mapped_;\r
+ bool writing_;\r
+ bool reading_;\r
+};\r
+\r
+pixel_buffer_object::pixel_buffer_object(){}\r
+pixel_buffer_object::pixel_buffer_object(size_t width, size_t height) \r
+ : impl_(new implementation(width, height)){}\r
+void pixel_buffer_object::create(size_t width, size_t height)\r
+{\r
+ impl_.reset(new implementation(width, height));\r
+}\r
+void pixel_buffer_object::begin_write() { impl_->begin_write();}\r
+void* pixel_buffer_object::end_write() {return impl_->end_write();} \r
+void pixel_buffer_object::begin_read() { impl_->begin_read();}\r
+void* pixel_buffer_object::end_read(){return impl_->end_read();}\r
+void pixel_buffer_object::bind_texture() {impl_->bind_texture();}\r
+size_t pixel_buffer_object::width() const {return impl_->width_;}\r
+size_t pixel_buffer_object::heigth() const {return impl_->height_;}\r
+size_t pixel_buffer_object::size() const {return impl_->size_;}\r
+bool pixel_buffer_object::is_reading() const { return impl_->reading_;}\r
+bool pixel_buffer_object::is_writing() const { return impl_->writing_;}\r
+}}}
\ No newline at end of file
--- /dev/null
+#pragma once\r
+\r
+#include <memory>\r
+\r
+#include <boost/noncopyable.hpp>\r
+\r
+#include <Glee.h>\r
+\r
+#include <boost/tuple/tuple.hpp>\r
+#include <boost/thread/future.hpp>\r
+\r
+namespace caspar { namespace common { namespace gl {\r
+ \r
+class pixel_buffer_object : boost::noncopyable\r
+{\r
+public:\r
+ pixel_buffer_object();\r
+ pixel_buffer_object(size_t width, size_t height);\r
+ void create(size_t width, size_t height);\r
+ ~pixel_buffer_object(){}\r
+\r
+ void begin_write();\r
+ void* end_write();\r
+\r
+ void begin_read();\r
+ void* end_read();\r
+\r
+ void bind_texture();\r
+\r
+ size_t width() const;\r
+ size_t heigth() const;\r
+ size_t size() const;\r
+\r
+ bool is_reading() const;\r
+ bool is_writing() const;\r
+ \r
+private:\r
+ struct implementation;\r
+ std::shared_ptr<implementation> impl_;\r
+};\r
+typedef std::shared_ptr<pixel_buffer_object> pixel_buffer_object_ptr;\r
+ \r
+}}}
\ No newline at end of file
return dest;\r
}\r
\r
-void* copy(void* dest, const void* source, size_t num)\r
+void* aligned_memcpy(void* dest, const void* source, size_t num)\r
{ \r
+ if(num < 128)\r
+ return memcpy(dest, source, num);\r
+\r
tbb::parallel_for(tbb::blocked_range<size_t>(0, num/128), [&](const tbb::blocked_range<size_t>& r)\r
{\r
memcpy_SSE2(reinterpret_cast<char*>(dest) + r.begin()*128, reinterpret_cast<const char*>(source) + r.begin()*128, r.size()*128);\r
\r
namespace caspar { namespace common {\r
\r
-void* copy(void* dest, const void* source, size_t size);\r
+void* aligned_memcpy(void* dest, const void* source, size_t size);\r
void* clear(void* dest, size_t size);\r
\r
}}
\ No newline at end of file
std::shared_ptr<DecklinkVideoFrame> pTempFrame = GetReservedFrame();\r
if(pTempFrame && frame->size() == pTempFrame->size())\r
{\r
- common::copy(pTempFrame->data(), frame->data(), pTempFrame->size());\r
+ common::aligned_memcpy(pTempFrame->data(), frame->data(), pTempFrame->size());\r
DoRender(pTempFrame);\r
}\r
else\r
#include "../../frame/gpu_frame.h"\r
#include "../../../common/utility/memory.h"\r
#include "../../../common/gl/gl_check.h"\r
+#include "../../../common/gl/pixel_buffer_object.h"\r
\r
#include <boost/thread.hpp>\r
\r
#include <Glee.h>\r
#include <SFML/Window.hpp>\r
-#include <SFML/Graphics.hpp>\r
\r
#include <windows.h>\r
\r
struct consumer::implementation : boost::noncopyable\r
{ \r
implementation(const frame_format_desc& format_desc, unsigned int screen_index, stretch stretch, bool windowed) \r
- : format_desc_(format_desc), stretch_(stretch), pbo_index_(0), screen_width_(0), screen_height_(0), windowed_(windowed)\r
- {\r
- pbos_[0] = pbos_[1] = 0;\r
- \r
+ : index_(0), format_desc_(format_desc), stretch_(stretch), screen_width_(0), screen_height_(0), windowed_(windowed)\r
+ { \r
#ifdef _WIN32\r
DISPLAY_DEVICE dDevice; \r
memset(&dDevice,0,sizeof(dDevice));\r
{\r
frame_buffer_.push(nullptr),\r
thread_.join();\r
-\r
- if(pbos_[0] && pbos_[1])\r
- glDeleteBuffers(2, pbos_);\r
}\r
\r
void init() \r
window_.SetPosition(screenX_, screenY_);\r
window_.SetSize(screen_width_, screen_height_);\r
window_.SetActive();\r
- \r
+ GL(glEnable(GL_TEXTURE_2D));\r
+ GL(glDisable(GL_DEPTH_TEST)); \r
+ GL(glClearColor(0.0, 0.0, 0.0, 0.0));\r
+ GL(glViewport(0, 0, format_desc_.width, format_desc_.height));\r
+ glLoadIdentity();\r
+ \r
+ wratio_ = static_cast<float>(format_desc_.width)/static_cast<float>(format_desc_.width);\r
+ hratio_ = static_cast<float>(format_desc_.height)/static_cast<float>(format_desc_.height);\r
+\r
std::pair<float, float> target_ratio = None();\r
if(stretch_ == ogl::fill)\r
target_ratio = Fill();\r
else if(stretch_ == ogl::uniform_to_fill)\r
target_ratio = UniformToFill();\r
\r
- float wSize = target_ratio.first;\r
- float hSize = target_ratio.second;\r
+ wSize_ = target_ratio.first;\r
+ hSize_ = target_ratio.second;\r
\r
- image_.Create(format_desc_.width, format_desc_.height);\r
- sprite_.SetImage(image_);\r
- \r
- GL(glGenBuffersARB(2, pbos_));\r
- GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[0]));\r
- GL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW));\r
- GL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[1]));\r
- GL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW)); \r
-\r
- pbo_index_ = 0;\r
+ pbos_[0].create(format_desc_.width, format_desc_.height);\r
+ pbos_[1].create(format_desc_.width, format_desc_.height);\r
}\r
\r
std::pair<float, float> None()\r
}\r
\r
void render(const gpu_frame_ptr& frame)\r
- { \r
- // Render\r
- window_.Clear();\r
- \r
- image_.Bind();\r
- GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[pbo_index_])); \r
- GL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, format_desc_.width, format_desc_.height, GL_BGRA, GL_UNSIGNED_BYTE, 0));\r
-\r
- window_.Draw(sprite_);\r
-\r
- // Update\r
- int nextPboIndex = pbo_index_ ^ 1;\r
-\r
- GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[nextPboIndex]));\r
- GL(glBufferData(GL_PIXEL_UNPACK_BUFFER, format_desc_.size, NULL, GL_STREAM_DRAW));\r
- GLubyte* ptr = static_cast<GLubyte*>(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));\r
-\r
- if(ptr != NULL) \r
- {\r
- common::copy(ptr, frame->data(), frame->size());\r
- GL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));\r
- }\r
-\r
- // Swap\r
- pbo_index_ = nextPboIndex;\r
+ { \r
+ index_ = (index_ + 1) % 2;\r
+ int next_index = (index_ + 1) % 2;\r
+ \r
+ auto ptr = pbos_[index_].end_write();\r
+ common::aligned_memcpy(ptr, frame->data(), frame->size());\r
+\r
+ GL(glClear(GL_COLOR_BUFFER_BIT)); \r
+ pbos_[next_index].bind_texture(); \r
+ glBegin(GL_QUADS);\r
+ glTexCoord2f(0.0f, hratio_); glVertex2f(-wSize_, -hSize_);\r
+ glTexCoord2f(wratio_, hratio_); glVertex2f( wSize_, -hSize_);\r
+ glTexCoord2f(wratio_, 0.0f); glVertex2f( wSize_, hSize_);\r
+ glTexCoord2f(0.0f, 0.0f); glVertex2f(-wSize_, hSize_);\r
+ glEnd();\r
+\r
+ pbos_[next_index].begin_write();\r
}\r
\r
void display(const gpu_frame_ptr& frame)\r
}\r
} \r
while(frame != nullptr);\r
- }\r
- \r
+ } \r
\r
- GLuint dlist_;\r
+ float wratio_;\r
+ float hratio_;\r
+ \r
+ float wSize_;\r
+ float hSize_;\r
+\r
+ int index_;\r
+ common::gl::pixel_buffer_object pbos_[2];\r
\r
bool windowed_;\r
unsigned int screen_width_;\r
unsigned int screenX_;\r
unsigned int screenY_;\r
\r
- GLuint pbos_[2];\r
- int pbo_index_;\r
-\r
stretch stretch_;\r
frame_format_desc format_desc_;\r
\r
boost::thread thread_;\r
tbb::concurrent_bounded_queue<gpu_frame_ptr> frame_buffer_;\r
\r
- sf::Image image_;\r
- sf::Sprite sprite_;\r
- sf::RenderWindow window_;\r
+ sf::Window window_;\r
};\r
\r
consumer::consumer(const frame_format_desc& format_desc, unsigned int screen_index, stretch stretch, bool windowed)\r
{\r
implementation(gpu_composite_frame* self) : self_(self){}\r
\r
- void write_lock()\r
+ void begin_write()\r
{\r
- boost::range::for_each(frames_, std::mem_fn(&gpu_frame::write_lock)); \r
+ boost::range::for_each(frames_, std::mem_fn(&gpu_frame::begin_write)); \r
}\r
\r
- bool write_unlock()\r
+ void end_write()\r
{\r
- return std::all_of(frames_.begin(), frames_.end(), \r
- std::mem_fn(&gpu_frame::write_unlock)); \r
+ boost::range::for_each(frames_, std::mem_fn(&gpu_frame::end_write)); \r
}\r
\r
- void read_lock(GLenum mode)\r
+ void begin_read()\r
{ \r
- boost::range::for_each(frames_, std::bind(&gpu_frame::read_lock, \r
- std::placeholders::_1, mode)); \r
+ boost::range::for_each(frames_, std::mem_fn(&gpu_frame::begin_read)); \r
}\r
\r
- bool read_unlock()\r
+ void end_read()\r
{\r
- return std::all_of(frames_.begin(), frames_.end(), \r
- std::mem_fn(&gpu_frame::read_unlock)); \r
+ boost::range::for_each(frames_, std::mem_fn(&gpu_frame::end_read)); \r
}\r
\r
void draw()\r
\r
gpu_composite_frame::gpu_composite_frame() \r
: gpu_frame(0, 0), impl_(new implementation(this)){}\r
-void gpu_composite_frame::write_lock(){impl_->write_lock();}\r
-bool gpu_composite_frame::write_unlock(){return impl_->write_unlock();} \r
-void gpu_composite_frame::read_lock(GLenum mode){impl_->read_lock(mode);}\r
-bool gpu_composite_frame::read_unlock(){return impl_->read_unlock();}\r
+void gpu_composite_frame::begin_write(){impl_->begin_write();}\r
+void gpu_composite_frame::end_write(){impl_->end_write();} \r
+void gpu_composite_frame::begin_read(){impl_->begin_read();}\r
+void gpu_composite_frame::end_read(){impl_->end_read();}\r
void gpu_composite_frame::draw(){impl_->draw();}\r
unsigned char* gpu_composite_frame::data(){return impl_->data();}\r
void gpu_composite_frame::add(const gpu_frame_ptr& frame){impl_->add(frame);}\r
\r
private:\r
virtual unsigned char* data();\r
- virtual void write_lock();\r
- virtual bool write_unlock();\r
- virtual void read_lock(GLenum mode);\r
- virtual bool read_unlock();\r
+ virtual void begin_write();\r
+ virtual void end_write();\r
+ virtual void begin_read();\r
+ virtual void end_read();\r
virtual void draw();\r
\r
struct implementation;\r
#include "gpu_frame.h"\r
#include "../../common/utility/memory.h"\r
#include "../../common/gl/gl_check.h"\r
+#include "../../common/gl/pixel_buffer_object.h"\r
\r
namespace caspar { namespace core {\r
\r
struct gpu_frame::implementation : boost::noncopyable\r
{\r
implementation(size_t width, size_t height) \r
- : pbo_(0), data_(nullptr), width_(width), height_(height), \r
- size_(width*height*4), reading_(false), texture_(0), alpha_(1.0f), \r
+ : pbo_(width, height), data_(nullptr), width_(width), height_(height), \r
+ size_(width*height*4), reading_(false), alpha_(1.0f), \r
x_(0.0f), y_(0.0f), mode_(video_mode::progressive), \r
- texcoords_(0.0, 1.0, 1.0, 0.0)\r
+ texcoords_(0.0, 1.0, 1.0, 0.0), writing_(false), mapped_(false)\r
{ \r
+ if(width > 0 && height > 0)\r
+ end_write();\r
}\r
-\r
- ~implementation()\r
+ \r
+ void begin_write()\r
{\r
- if(pbo_ != 0)\r
- glDeleteBuffers(1, &pbo_);\r
- if(texture_ != 0)\r
- glDeleteTextures(1, &texture_);\r
- }\r
-\r
- GLuint pbo()\r
- { \r
- if(pbo_ == 0)\r
- GL(glGenBuffers(1, &pbo_));\r
- return pbo_;\r
+ data_ = nullptr;\r
+ pbo_.begin_write(); \r
}\r
\r
- void write_lock()\r
+ void end_write()\r
{\r
- if(texture_ == 0)\r
- {\r
- GL(glGenTextures(1, &texture_));\r
-\r
- GL(glBindTexture(GL_TEXTURE_2D, texture_));\r
-\r
- GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));\r
- GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));\r
- GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));\r
- GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));\r
-\r
- GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width_, height_, 0, GL_BGRA, \r
- GL_UNSIGNED_BYTE, NULL));\r
- }\r
-\r
- GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo()));\r
- if(data_ != nullptr)\r
- {\r
- GL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));\r
- data_ = nullptr;\r
- }\r
- GL(glBindTexture(GL_TEXTURE_2D, texture_));\r
- GL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_BGRA, \r
- GL_UNSIGNED_BYTE, NULL));\r
- GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));\r
- }\r
-\r
- bool write_unlock()\r
- {\r
- if(data_ != nullptr)\r
- return false;\r
- GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo()));\r
- GL(glBufferData(GL_PIXEL_UNPACK_BUFFER, size_, NULL, GL_STREAM_DRAW));\r
- void* ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);\r
- GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));\r
- data_ = reinterpret_cast<unsigned char*>(ptr);\r
- if(!data_)\r
- BOOST_THROW_EXCEPTION(invalid_operation() \r
- << msg_info("glMapBuffer failed"));\r
- return true;\r
+ data_ = static_cast<unsigned char*>(pbo_.end_write());\r
}\r
\r
- void read_lock(GLenum mode)\r
+ void begin_read()\r
{ \r
- GL(glReadBuffer(mode));\r
- GL(glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo()));\r
- if(data_ != nullptr) \r
- { \r
- GL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER)); \r
- data_ = nullptr;\r
- }\r
- GL(glBufferData(GL_PIXEL_PACK_BUFFER, size_, NULL, GL_STREAM_READ)); \r
- GL(glReadPixels(0, 0, width_, height_, GL_BGRA, GL_UNSIGNED_BYTE, NULL));\r
- GL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));\r
- reading_ = true;\r
+ data_ = nullptr;\r
+ pbo_.begin_read();\r
}\r
\r
- bool read_unlock()\r
+ void end_read()\r
{\r
- if(data_ != nullptr || !reading_)\r
- return false;\r
- GL(glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo()));\r
- void* ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); \r
- GL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));\r
- data_ = reinterpret_cast<unsigned char*>(ptr);\r
- if(!data_)\r
- BOOST_THROW_EXCEPTION(std::bad_alloc());\r
- reading_ = false;\r
- return true;\r
+ data_ = static_cast<unsigned char*>(pbo_.end_read());\r
}\r
\r
void draw()\r
else if(mode_ == video_mode::lower)\r
glPolygonStipple(lower_pattern);\r
\r
- GL(glBindTexture(GL_TEXTURE_2D, texture_));\r
+ pbo_.bind_texture();\r
glBegin(GL_QUADS);\r
glTexCoord2d(texcoords_.left, texcoords_.bottom); glVertex2d(-1.0, -1.0);\r
glTexCoord2d(texcoords_.right, texcoords_.bottom); glVertex2d( 1.0, -1.0);\r
mode_ = video_mode::progressive;\r
}\r
\r
+ common::gl::pixel_buffer_object pbo_;\r
gpu_frame* self_;\r
- GLuint pbo_;\r
- GLuint texture_;\r
unsigned char* data_;\r
size_t width_;\r
size_t height_;\r
size_t size_;\r
+\r
bool reading_;\r
+ bool writing_;\r
+ bool mapped_;\r
+\r
std::vector<short> audio_data_;\r
\r
double alpha_;\r
\r
gpu_frame::gpu_frame(size_t width, size_t height) \r
: impl_(new implementation(width, height)){}\r
-void gpu_frame::write_lock(){impl_->write_lock();}\r
-bool gpu_frame::write_unlock(){return impl_->write_unlock();} \r
-void gpu_frame::read_lock(GLenum mode){impl_->read_lock(mode);}\r
-bool gpu_frame::read_unlock(){return impl_->read_unlock();}\r
+void gpu_frame::begin_write(){impl_->begin_write();}\r
+void gpu_frame::end_write(){impl_->end_write();} \r
+void gpu_frame::begin_read(){impl_->begin_read();}\r
+void gpu_frame::end_read(){impl_->end_read();}\r
void gpu_frame::draw(){impl_->draw();}\r
-bool gpu_frame::valid() const { return impl_->data_ != nullptr;}\r
unsigned char* gpu_frame::data(){return impl_->data();}\r
size_t gpu_frame::size() const { return impl_->size_; }\r
size_t gpu_frame::width() const { return impl_->width_;}\r
class gpu_frame : boost::noncopyable\r
{\r
public:\r
- gpu_frame(size_t width, size_t height);\r
virtual ~gpu_frame(){}\r
- virtual void write_lock();\r
- virtual bool write_unlock();\r
- virtual void read_lock(GLenum mode);\r
- virtual bool read_unlock();\r
- virtual void draw();\r
-\r
- virtual bool valid() const;\r
- \r
+ \r
virtual unsigned char* data();\r
virtual size_t size() const;\r
virtual size_t width() const;\r
virtual size_t height() const;\r
- \r
- virtual void reset();\r
- \r
+ \r
virtual const std::vector<short>& audio_data() const; \r
virtual std::vector<short>& audio_data();\r
\r
\r
static std::shared_ptr<gpu_frame> null()\r
{\r
- static auto my_null_frame = std::make_shared<gpu_frame>(0,0);\r
+ static auto my_null_frame = std::shared_ptr<gpu_frame>(new gpu_frame(0,0));\r
return my_null_frame;\r
}\r
\r
+protected:\r
+ gpu_frame(size_t width, size_t height);\r
+\r
+ friend class gpu_frame_processor;\r
+ \r
+ virtual void begin_write();\r
+ virtual void end_write();\r
+ virtual void begin_read();\r
+ virtual void end_read();\r
+ virtual void draw();\r
+ virtual void reset();\r
+\r
private:\r
struct implementation;\r
std::shared_ptr<implementation> impl_;\r
#include "../../common/concurrency/executor.h"\r
#include "../../common/utility/memory.h"\r
#include "../../common/gl/gl_check.h"\r
+#include "../../common/gl/frame_buffer_object.h"\r
\r
#include <Glee.h>\r
#include <SFML/Window.hpp>\r
GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); \r
GL(glClearColor(0.0, 0.0, 0.0, 0.0));\r
GL(glViewport(0, 0, format_desc_.width, format_desc_.height));\r
- glLoadIdentity(); \r
- \r
- // Create and bind a framebuffer\r
- GL(glGenTextures(1, &render_texture_)); \r
- GL(glBindTexture(GL_TEXTURE_2D, render_texture_)); \r
- GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, format_desc_.width, \r
- format_desc_.height, 0, GL_BGRA, \r
- GL_UNSIGNED_BYTE, NULL));\r
- GL(glGenFramebuffersEXT(1, &fbo_)); \r
- GL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_));\r
- GL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, \r
- GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, \r
- render_texture_, 0));\r
- \r
+ glLoadIdentity(); \r
+\r
+ fbo_.create(format_desc_.width, format_desc_.height);\r
+ fbo_.bind_pixel_source();\r
+\r
writing_.resize(2, std::make_shared<gpu_composite_frame>());\r
- output_frame_ = std::make_shared<gpu_frame>(format_desc_.width, \r
- format_desc_.height);\r
+\r
+ // Fill pipeline\r
+ for(int n = 0; n < 2; ++n)\r
+ composite(std::vector<gpu_frame_ptr>());\r
});\r
- // Fill pipeline\r
- composite(std::vector<gpu_frame_ptr>());\r
- composite(std::vector<gpu_frame_ptr>());\r
- composite(std::vector<gpu_frame_ptr>());\r
}\r
\r
~implementation()\r
{\r
- glDeleteFramebuffersEXT(1, &fbo_);\r
executor_.stop();\r
}\r
\r
// 1. Start asynchronous DMA transfer to video memory.\r
writing_[index_] = std::move(frame); \r
// Lock frame and give pointer ownership to OpenGL.\r
- writing_[index_]->write_lock();\r
+ writing_[index_]->begin_write();\r
\r
// 3. Output to external buffer.\r
- if(output_frame_->read_unlock())\r
+ if(output_frame_)\r
+ { \r
+ output_frame_->end_read();\r
output_.push(output_frame_);\r
- \r
+ }\r
+\r
// Clear framebuffer.\r
- glClear(GL_COLOR_BUFFER_BIT); \r
+ GL(glClear(GL_COLOR_BUFFER_BIT)); \r
\r
// 2. Draw to framebuffer and start asynchronous DMA transfer \r
// to page-locked memory.\r
writing_[next_index]->draw();\r
\r
// Create an output frame\r
- output_frame_ = create_output_frame();\r
+ auto temp_frame = create_output_frame();\r
\r
// Read from framebuffer into page-locked memory.\r
- output_frame_->read_lock(GL_COLOR_ATTACHMENT0_EXT);\r
- output_frame_->audio_data() = std::move(writing_[next_index]->audio_data());\r
+ temp_frame->begin_read();\r
+ temp_frame->audio_data() = std::move(writing_[next_index]->audio_data());\r
+\r
+ output_frame_ = temp_frame;\r
\r
// Return frames to pool.\r
+ writing_[next_index]->end_write();\r
writing_[next_index] = nullptr;\r
}\r
catch(...)\r
{\r
gpu_frame_ptr frame;\r
if(!reading_pool_.try_pop(frame))\r
- frame = std::make_shared<gpu_frame>(format_desc_.width, \r
- format_desc_.height);\r
+ frame.reset(new gpu_frame(format_desc_.width, format_desc_.height));\r
\r
return gpu_frame_ptr(frame.get(), [=](gpu_frame*)\r
{\r
gpu_frame_ptr frame;\r
if(!pool.try_pop(frame))\r
{\r
- frame = executor_.invoke([=]() -> gpu_frame_ptr\r
+ frame = executor_.invoke([&]\r
{\r
- auto frame = std::make_shared<gpu_frame>(width, height);\r
- frame->write_unlock();\r
- return frame;\r
+ return std::shared_ptr<gpu_frame>(new gpu_frame(width, height));\r
});\r
}\r
\r
auto destructor = [=]\r
{\r
- frame->write_unlock();\r
frame->reset();\r
writing_pools_[key].push(frame);\r
};\r
\r
common::executor executor_;\r
\r
- GLuint render_texture_;\r
- GLuint fbo_;\r
+ common::gl::frame_buffer_object fbo_;\r
};\r
\r
gpu_frame_processor::gpu_frame_processor(const frame_format_desc& format_desc) : impl_(new implementation(format_desc)){}\r
\r
gpu_frame_ptr get_frame()\r
{ \r
- frame_ = factory_->create_frame(format_desc_);\r
- __stosd(reinterpret_cast<unsigned long*>(frame_->data()), color_value_, frame_->size() / sizeof(unsigned long));\r
return frame_;\r
}\r
const frame_format_desc& get_frame_format_desc() const { return format_desc_; }\r
\r
void initialize(const frame_factory_ptr& factory)\r
{\r
- factory_ = factory;\r
+ frame_ = factory->create_frame(format_desc_);\r
+ __stosd(reinterpret_cast<unsigned long*>(frame_->data()), color_value_, frame_->size() / sizeof(unsigned long));\r
}\r
\r
frame_format_desc format_desc_;\r
gpu_frame_ptr frame_;\r
unsigned int color_value_;\r
- frame_factory_ptr factory_;\r
};\r
\r
union Color \r
} \r
\r
auto frame = factory_->create_frame(format_desc_);\r
- common::copy(frame->data(), current_frame_->data(), current_frame_->size()); \r
+ common::aligned_memcpy(frame->data(), current_frame_->data(), current_frame_->size()); \r
\r
return frame;\r
}\r
\r
FreeImage_FlipVertical(bitmap.get());\r
frame_ = factory->create_frame(format_desc_);\r
- common::copy(frame_->data(), FreeImage_GetBits(bitmap.get()), frame_->size());\r
+ common::aligned_memcpy(frame_->data(), FreeImage_GetBits(bitmap.get()), frame_->size());\r
}\r
\r
const frame_format_desc& get_frame_format_desc() const { return format_desc_; } \r
unsigned char* pBits = FreeImage_GetBits(pBitmap.get());\r
\r
for (size_t i = 0; i < height; ++i)\r
- common::copy(&image_.get()[i * image_width_ * 4], &pBits[i* width * 4], width * 4);\r
+ common::aligned_memcpy(&image_.get()[i * image_width_ * 4], &pBits[i* width * 4], width * 4);\r
}\r
\r
gpu_frame_ptr render_frame()\r
\r
gpu_frame_ptr get_frame()\r
{\r
- if(++current_frame_ >= info_.duration)\r
+ if(current_frame_++ >= info_.duration)\r
return nullptr;\r
\r
gpu_frame_ptr source;\r
auto composite = std::make_shared<gpu_composite_frame>();\r
if(src_frame)\r
composite->add(src_frame);\r
- else\r
- src_frame = std::make_shared<gpu_frame>(0, 0);\r
composite->add(dest_frame);\r
\r
- if(info_.type == transition_type::mix)\r
- dest_frame->alpha(alpha); \r
- else if(info_.type == transition_type::slide)\r
+ switch(info_.type)\r
{\r
+ case transition_type::mix: \r
+ dest_frame->alpha(alpha); \r
+ break;\r
+ case transition_type::slide: \r
if(info_.direction == transition_direction::from_left) \r
dest_frame->translate(-1.0+alpha, 0.0); \r
else if(info_.direction == transition_direction::from_right)\r
- dest_frame->translate(1.0-alpha, 0.0); \r
- }\r
- else if(info_.type == transition_type::push)\r
- {\r
+ dest_frame->translate(1.0-alpha, 0.0); \r
+ break;\r
+ case transition_type::push:\r
if(info_.direction == transition_direction::from_left) \r
{\r
dest_frame->translate(-1.0+alpha, 0.0);\r
- src_frame->translate(0.0+alpha, 0.0);\r
+ if(src_frame)\r
+ src_frame->translate(0.0+alpha, 0.0);\r
}\r
else if(info_.direction == transition_direction::from_right)\r
{\r
dest_frame->translate(1.0-alpha, 0.0);\r
- src_frame->translate(0.0-alpha, 0.0);\r
+ if(src_frame)\r
+ src_frame->translate(0.0-alpha, 0.0);\r
}\r
+ break;\r
+ }\r
+\r
+ if(info_.type == transition_type::mix)\r
+ dest_frame->alpha(alpha); \r
+ else if(info_.type == transition_type::slide)\r
+ { \r
+ }\r
+ else if(info_.type == transition_type::push)\r
+ {\r
}\r
else if(info_.type == transition_type::wipe)\r
{\r
</paths>\r
<channels>\r
<channel>\r
- <videomode>PAL</videomode>\r
+ <videomode>1080p2500</videomode>\r
<consumers>\r
<ogl>\r
<device>1</device>\r
--- /dev/null
+#pragma once\r
+\r
+#include <core/frame/gpu_frame.h>\r
+\r
+struct mock_frame : public caspar::core::gpu_frame\r
+{\r
+ mock_frame(void* tag, short volume = 100) : caspar::core::gpu_frame(0,0), tag(tag)\r
+ {\r
+ audio_data().resize(100, volume);\r
+ }\r
+ void* tag;\r
+};
\ No newline at end of file
--- /dev/null
+#pragma once\r
+\r
+#include <core/frame/frame_factory.h>\r
+#include <core/frame/gpu_frame.h>\r
+\r
+struct mock_frame_factory : public caspar::core::frame_factory\r
+{\r
+ virtual gpu_frame_ptr create_frame(size_t width, size_t height)\r
+ {\r
+ return std::make_shared<mock_frame>();\r
+ }\r
+};
\ No newline at end of file
#pragma once\r
\r
-#include <core/frame/gpu_frame.h>\r
+#include "mock_frame.h"\r
+\r
#include <core/producer/frame_producer.h>\r
#include <common/exception/exceptions.h>\r
\r
{\r
public:\r
mock_frame_producer(bool null = false, bool throws = false) \r
- : null_(null), throws_(throws){}\r
+ : null_(null), throws_(throws), initialized_(false), volume_(100){}\r
+ void set_volume(short volume) { volume_ = volume;}\r
gpu_frame_ptr get_frame()\r
{ \r
if(throws_)\r
if(leading_)\r
return leading_->get_frame();\r
if(!null_)\r
- return std::make_shared<gpu_frame>(0, 0);\r
+ return std::make_shared<mock_frame>(this, volume_);\r
return nullptr;\r
}\r
std::shared_ptr<frame_producer> get_following_producer() const \r
return format;\r
}\r
void initialize(const frame_factory_ptr& factory)\r
- {}\r
+ {initialized_ = true;}\r
void set_following_producer(const std::shared_ptr<frame_producer>& following)\r
{following_ = following;}\r
+\r
+ bool is_initialized() const { return initialized_;}\r
private:\r
std::shared_ptr<frame_producer> following_;\r
std::shared_ptr<frame_producer> leading_;\r
bool null_;\r
bool throws_;\r
+ bool initialized_;\r
+ short volume_;\r
};
\ No newline at end of file
--- /dev/null
+#include <gtest/gtest.h>\r
+\r
+#include "../../mock/mock_frame_producer.h"\r
+#include "../../mock/mock_frame.h"\r
+\r
+#include <core/frame/frame_format.h>\r
+#include <core/frame/gpu_frame.h>\r
+#include <core/producer/transition/transition_producer.h>\r
+#include <common/exception/exceptions.h>\r
+\r
+using namespace caspar;\r
+using namespace caspar::core;\r
+\r
+frame_format_desc test_format = frame_format_desc::format_descs[frame_format::pal];\r
+\r
+TEST(transition_producer, constructor_dest_nullptr) \r
+{\r
+ ASSERT_THROW(transition_producer(nullptr, transition_info(), test_format);\r
+ , null_argument); \r
+}\r
+\r
+TEST(transition_producer, get_following_producer) \r
+{\r
+ auto dest = std::make_shared<mock_frame_producer>();\r
+ \r
+ transition_producer producer(dest, transition_info(), test_format);\r
+\r
+ ASSERT_TRUE(producer.get_following_producer() == dest);\r
+}\r
+\r
+TEST(transition_producer, get_frame_format_desc) \r
+{\r
+ auto dest = std::make_shared<mock_frame_producer>();\r
+ \r
+ transition_producer producer(dest, transition_info(), test_format);\r
+\r
+ ASSERT_TRUE(producer.get_frame_format_desc() == test_format);\r
+}\r
+\r
+TEST(transition_producer, null_dest_get_frame) \r
+{\r
+ auto source = std::make_shared<mock_frame_producer>();\r
+ \r
+ transition_producer producer(nullptr, transition_info(), test_format);\r
+ producer.set_leading_producer(source);\r
+\r
+ ASSERT_TRUE(producer.get_frame() == nullptr);\r
+}\r
+\r
+TEST(transition_producer, null_source_get_frame_cut) \r
+{\r
+ auto dest = std::make_shared<mock_frame_producer>();\r
+ \r
+ transition_info info;\r
+ info.type = transition_type::cut;\r
+\r
+ transition_producer producer(dest, info, test_format);\r
+\r
+ ASSERT_TRUE(producer.get_frame() == nullptr);\r
+}\r
+\r
+TEST(transition_producer, initialize) \r
+{\r
+ auto dest = std::make_shared<mock_frame_producer>();\r
+ \r
+ transition_producer producer(dest, transition_info(), test_format);\r
+ producer.initialize(nullptr);\r
+ \r
+ ASSERT_TRUE(dest->is_initialized());\r
+}\r
+\r
+TEST(transition_producer, duration) \r
+{\r
+ auto dest = std::make_shared<mock_frame_producer>();\r
+ auto source = std::make_shared<mock_frame_producer>();\r
+ \r
+ transition_info info;\r
+ info.type = transition_type::cut;\r
+ info.duration = 3;\r
+\r
+ transition_producer producer(dest, info, test_format);\r
+ producer.set_leading_producer(source);\r
+\r
+ for(int n = 0; n < info.duration; ++n)\r
+ {\r
+ auto frame = producer.get_frame();\r
+ ASSERT_TRUE(std::static_pointer_cast<mock_frame>(frame)->tag == source.get());\r
+ }\r
+ ASSERT_TRUE(producer.get_frame() == nullptr);\r
+}
\ No newline at end of file
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>\r
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>\r
</ClCompile>\r
+ <ClCompile Include="producer\transition\transition_producer_test.cpp" />\r
<ClCompile Include="renderer\layer_test.cpp" />\r
</ItemGroup>\r
<ItemGroup>\r
</ProjectReference>\r
</ItemGroup>\r
<ItemGroup>\r
+ <ClInclude Include="mock\mock_frame.h" />\r
+ <ClInclude Include="mock\mock_frame_factory.h" />\r
<ClInclude Include="mock\mock_frame_producer.h" />\r
</ItemGroup>\r
<PropertyGroup Label="Globals">\r
<Filter Include="Source\mock">\r
<UniqueIdentifier>{420ab80e-66c8-477b-b8c6-d8f4ceb08102}</UniqueIdentifier>\r
</Filter>\r
+ <Filter Include="Source\producer">\r
+ <UniqueIdentifier>{2d766f33-578d-40a7-a018-8a0a8b031e08}</UniqueIdentifier>\r
+ </Filter>\r
+ <Filter Include="Source\producer\transition">\r
+ <UniqueIdentifier>{cb38fc85-f618-4461-94bf-d7eff61b49fe}</UniqueIdentifier>\r
+ </Filter>\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="main.cpp">\r
<ClCompile Include="renderer\layer_test.cpp">\r
<Filter>Source\renderer</Filter>\r
</ClCompile>\r
+ <ClCompile Include="producer\transition\transition_producer_test.cpp">\r
+ <Filter>Source\producer\transition</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="mock\mock_frame_producer.h">\r
<Filter>Source\mock</Filter>\r
</ClInclude>\r
+ <ClInclude Include="mock\mock_frame.h">\r
+ <Filter>Source\mock</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="mock\mock_frame_factory.h">\r
+ <Filter>Source\mock</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
</Project>
\ No newline at end of file