1 #include "../StdAfx.h"
\r
3 #include "gpu_frame_processor.h"
\r
5 #include "gpu_frame.h"
\r
6 #include "gpu_composite_frame.h"
\r
7 #include "frame_format.h"
\r
9 #include "../../common/exception/exceptions.h"
\r
10 #include "../../common/concurrency/executor.h"
\r
11 #include "../../common/utility/memory.h"
\r
12 #include "../../common/gl/gl_check.h"
\r
15 #include <SFML/Window.hpp>
\r
17 #include <tbb/concurrent_queue.h>
\r
18 #include <tbb/concurrent_unordered_map.h>
\r
20 #include <boost/lexical_cast.hpp>
\r
21 #include <boost/thread/once.hpp>
\r
22 #include <boost/thread.hpp>
\r
23 #include <boost/range.hpp>
\r
24 #include <boost/foreach.hpp>
\r
25 #include <boost/range/algorithm_ext/erase.hpp>
\r
26 #include <boost/range/algorithm.hpp>
\r
28 #include <functional>
\r
29 #include <unordered_map>
\r
32 namespace caspar { namespace core {
\r
34 struct gpu_frame_processor::implementation : boost::noncopyable
\r
36 implementation(const frame_format_desc& format_desc)
\r
37 : format_desc_(format_desc), index_(0)
\r
39 input_.set_capacity(2);
\r
41 executor_.begin_invoke([=]
\r
43 ogl_context_.reset(new sf::Context());
\r
44 ogl_context_->SetActive(true);
\r
45 GL(glEnable(GL_POLYGON_STIPPLE));
\r
46 GL(glEnable(GL_TEXTURE_2D));
\r
47 GL(glEnable(GL_BLEND));
\r
48 GL(glDisable(GL_DEPTH_TEST));
\r
49 GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
\r
50 GL(glClearColor(0.0, 0.0, 0.0, 0.0));
\r
51 GL(glViewport(0, 0, format_desc_.width, format_desc_.height));
\r
54 // Create and bind a framebuffer
\r
55 GL(glGenTextures(1, &render_texture_));
\r
56 GL(glBindTexture(GL_TEXTURE_2D, render_texture_));
\r
57 GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, format_desc_.width,
\r
58 format_desc_.height, 0, GL_BGRA,
\r
59 GL_UNSIGNED_BYTE, NULL));
\r
60 GL(glGenFramebuffersEXT(1, &fbo_));
\r
61 GL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_));
\r
62 GL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
\r
63 GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
\r
64 render_texture_, 0));
\r
66 writing_.resize(2, std::make_shared<gpu_composite_frame>());
\r
67 output_frame_ = std::make_shared<gpu_frame>(format_desc_.width,
\r
68 format_desc_.height);
\r
71 composite(std::vector<gpu_frame_ptr>());
\r
72 composite(std::vector<gpu_frame_ptr>());
\r
73 composite(std::vector<gpu_frame_ptr>());
\r
78 glDeleteFramebuffersEXT(1, &fbo_);
\r
82 void composite(std::vector<gpu_frame_ptr> frames)
\r
84 boost::range::remove_erase(frames, nullptr);
\r
85 boost::range::remove_erase(frames, gpu_frame::null());
\r
86 auto composite_frame = std::make_shared<gpu_composite_frame>();
\r
87 boost::range::for_each(frames, std::bind(&gpu_composite_frame::add,
\r
89 std::placeholders::_1));
\r
91 input_.push(composite_frame);
\r
92 executor_.begin_invoke([=]
\r
96 gpu_frame_ptr frame;
\r
99 index_ = (index_ + 1) % 2;
\r
100 int next_index = (index_ + 1) % 2;
\r
102 // 1. Start asynchronous DMA transfer to video memory.
\r
103 writing_[index_] = std::move(frame);
\r
104 // Lock frame and give pointer ownership to OpenGL.
\r
105 writing_[index_]->write_lock();
\r
107 // 3. Output to external buffer.
\r
108 if(output_frame_->read_unlock())
\r
109 output_.push(output_frame_);
\r
111 // Clear framebuffer.
\r
112 glClear(GL_COLOR_BUFFER_BIT);
\r
114 // 2. Draw to framebuffer and start asynchronous DMA transfer
\r
115 // to page-locked memory.
\r
116 writing_[next_index]->draw();
\r
118 // Create an output frame
\r
119 output_frame_ = create_output_frame();
\r
121 // Read from framebuffer into page-locked memory.
\r
122 output_frame_->read_lock(GL_COLOR_ATTACHMENT0_EXT);
\r
123 output_frame_->audio_data() = std::move(writing_[next_index]->audio_data());
\r
125 // Return frames to pool.
\r
126 writing_[next_index] = nullptr;
\r
130 CASPAR_LOG_CURRENT_EXCEPTION();
\r
135 gpu_frame_ptr create_output_frame()
\r
137 gpu_frame_ptr frame;
\r
138 if(!reading_pool_.try_pop(frame))
\r
139 frame = std::make_shared<gpu_frame>(format_desc_.width,
\r
140 format_desc_.height);
\r
142 return gpu_frame_ptr(frame.get(), [=](gpu_frame*)
\r
145 reading_pool_.push(frame);
\r
149 gpu_frame_ptr create_frame(size_t width, size_t height)
\r
151 size_t key = width | (height << 16);
\r
152 auto& pool = writing_pools_[key];
\r
154 gpu_frame_ptr frame;
\r
155 if(!pool.try_pop(frame))
\r
157 frame = executor_.invoke([=]() -> gpu_frame_ptr
\r
159 auto frame = std::make_shared<gpu_frame>(width, height);
\r
160 frame->write_unlock();
\r
165 auto destructor = [=]
\r
167 frame->write_unlock();
\r
169 writing_pools_[key].push(frame);
\r
172 return gpu_frame_ptr(frame.get(), [=](gpu_frame*)
\r
174 executor_.begin_invoke(destructor);
\r
178 void pop(gpu_frame_ptr& frame)
\r
180 output_.pop(frame);
\r
183 typedef tbb::concurrent_bounded_queue<gpu_frame_ptr> gpu_frame_queue;
\r
184 tbb::concurrent_unordered_map<size_t, gpu_frame_queue> writing_pools_;
\r
185 gpu_frame_queue reading_pool_;
\r
187 gpu_frame_queue input_;
\r
188 std::vector<gpu_frame_ptr> writing_;
\r
189 gpu_frame_queue output_;
\r
193 gpu_frame_ptr output_frame_;
\r
194 frame_format_desc format_desc_;
\r
196 std::unique_ptr<sf::Context> ogl_context_;
\r
198 common::executor executor_;
\r
200 GLuint render_texture_;
\r
204 gpu_frame_processor::gpu_frame_processor(const frame_format_desc& format_desc) : impl_(new implementation(format_desc)){}
\r
205 void gpu_frame_processor::push(const std::vector<gpu_frame_ptr>& frames){ impl_->composite(frames);}
\r
206 void gpu_frame_processor::pop(gpu_frame_ptr& frame){impl_->pop(frame);}
\r
207 gpu_frame_ptr gpu_frame_processor::create_frame(size_t width, size_t height){return impl_->create_frame(width, height);}
\r