1 #include "../StdAfx.h"
\r
3 #include "gpu_frame_processor.h"
\r
5 #include "gpu_frame.h"
\r
6 #include "frame_format.h"
\r
8 #include "../../common/exception/exceptions.h"
\r
9 #include "../../common/concurrency/executor.h"
\r
10 #include "../../common/image/image.h"
\r
11 #include "../../common/gl/utility.h"
\r
14 #include <SFML/Window.hpp>
\r
16 #include <tbb/concurrent_queue.h>
\r
17 #include <tbb/concurrent_unordered_map.h>
\r
19 #include <boost/lexical_cast.hpp>
\r
20 #include <boost/thread/once.hpp>
\r
21 #include <boost/thread.hpp>
\r
22 #include <boost/range.hpp>
\r
23 #include <boost/foreach.hpp>
\r
24 #include <boost/range/algorithm_ext/erase.hpp>
\r
25 #include <boost/range/algorithm.hpp>
\r
27 #include <functional>
\r
28 #include <unordered_map>
\r
36 frame_buffer(size_t width, size_t height)
\r
38 CASPAR_GL_CHECK(glGenTextures(1, &texture_));
\r
40 CASPAR_GL_CHECK(glBindTexture(GL_TEXTURE_2D, texture_));
\r
42 CASPAR_GL_CHECK(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
\r
43 CASPAR_GL_CHECK(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
\r
44 CASPAR_GL_CHECK(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
\r
45 CASPAR_GL_CHECK(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
\r
47 CASPAR_GL_CHECK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL));
\r
49 glGenFramebuffersEXT(1, &fbo_);
\r
51 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_);
\r
52 glBindTexture(GL_TEXTURE_2D, texture_);
\r
53 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture_, 0);
\r
58 glDeleteFramebuffersEXT(1, &fbo_);
\r
61 GLuint handle() { return fbo_; }
\r
62 GLenum attachement() { return GL_COLOR_ATTACHMENT0_EXT; }
\r
68 typedef std::shared_ptr<frame_buffer> frame_buffer_ptr;
\r
70 struct gpu_frame_processor::implementation
\r
72 implementation(const frame_format_desc& format_desc) : format_desc_(format_desc)
\r
75 executor_.begin_invoke([=]
\r
77 context_.reset(new sf::Context());
\r
78 context_->SetActive(true);
\r
79 glEnable(GL_TEXTURE_2D);
\r
81 glDisable(GL_DEPTH_TEST);
\r
82 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
\r
83 glClearColor(0.0, 0.0, 0.0, 0.0);
\r
84 glViewport(0, 0, format_desc_.width, format_desc_.height);
\r
89 fbo_ = std::make_shared<frame_buffer>(format_desc_.width, format_desc_.height);
\r
90 output_frame_ = std::make_shared<gpu_frame>(format_desc_.width, format_desc_.height);
\r
94 empty_frame_ = create_frame(format_desc.width, format_desc.height);
\r
95 common::image::clear(empty_frame_->data(), empty_frame_->size());
\r
96 // Fill pipeline length
\r
97 for(int n = 0; n < 3; ++n)
\r
98 finished_frames_.push(empty_frame_);
\r
103 finished_frames_.push(nullptr);
\r
107 void pop(gpu_frame_ptr& frame)
\r
109 finished_frames_.pop(frame);
\r
112 void composite(std::vector<gpu_frame_ptr> frames)
\r
114 boost::range::remove_erase(frames, nullptr);
\r
115 boost::range::remove_erase(frames, gpu_frame::null());
\r
117 executor_.begin_invoke([=]
\r
121 index_ = (index_ + 1) % 2;
\r
122 int next_index = (index_ + 1) % 2;
\r
124 // 2. Start asynchronous DMA transfer to video memory
\r
125 // Lock frames and give pointer ownership to OpenGL
\r
126 boost::range::for_each(input_[index_], std::mem_fn(&gpu_frame::write_lock));
\r
127 writing_[index_] = input_[index_];
\r
128 input_[index_].clear();
\r
130 // 1. Copy to page-locked memory
\r
131 input_[next_index] = frames;
\r
133 // 4. Output to external buffer
\r
134 if(output_frame_->read_unlock())
\r
135 finished_frames_.push(output_frame_);
\r
136 output_frame_ = nullptr;
\r
138 // 3. Draw to framebuffer and start asynchronous DMA transfer to page-locked memory
\r
139 // Clear framebuffer
\r
140 glClear(GL_COLOR_BUFFER_BIT);
\r
142 // Draw all frames to framebuffer
\r
144 boost::range::for_each(writing_[next_index], std::mem_fn(&gpu_frame::draw));
\r
146 // Create an output frame
\r
147 output_frame_ = create_output_frame();
\r
149 // Read from framebuffer into page-locked memory
\r
150 output_frame_->read_lock(GL_COLOR_ATTACHMENT0_EXT);
\r
152 // Unlock frames and give back pointer ownership
\r
153 boost::range::for_each(writing_[next_index], std::mem_fn(&gpu_frame::write_unlock));
\r
155 // Mix audio from composite frames into output frame
\r
156 std::accumulate(writing_[next_index].begin(), writing_[next_index].end(), output_frame_, mix_audio_safe<gpu_frame_ptr>);
\r
158 // Return frames to pool
\r
159 writing_[next_index].clear();
\r
163 CASPAR_LOG_CURRENT_EXCEPTION();
\r
168 gpu_frame_ptr create_output_frame()
\r
170 gpu_frame_ptr frame;
\r
171 if(!out_frame_pool_.try_pop(frame))
\r
172 frame = std::make_shared<gpu_frame>(format_desc_.width, format_desc_.height);
\r
174 return gpu_frame_ptr(frame.get(), [=](gpu_frame*)
\r
177 out_frame_pool_.push(frame);
\r
181 gpu_frame_ptr create_frame(size_t width, size_t height)
\r
183 size_t key = width | (height << 16);
\r
184 auto& pool = input_frame_pools_[key];
\r
186 gpu_frame_ptr frame;
\r
187 if(!pool.try_pop(frame))
\r
189 frame = executor_.invoke([=]() -> gpu_frame_ptr
\r
191 auto frame = std::make_shared<gpu_frame>(width, height);
\r
192 frame->write_unlock();
\r
197 auto destructor = [=]
\r
200 input_frame_pools_[key].push(frame);
\r
203 return gpu_frame_ptr(frame.get(), [=](gpu_frame*)
\r
205 executor_.begin_invoke(destructor);
\r
209 tbb::concurrent_unordered_map<size_t, tbb::concurrent_bounded_queue<gpu_frame_ptr>> input_frame_pools_;
\r
211 tbb::concurrent_bounded_queue<gpu_frame_ptr> out_frame_pool_;
\r
213 frame_buffer_ptr fbo_;
\r
216 std::vector<std::vector<gpu_frame_ptr>> input_;
\r
217 std::vector<std::vector<gpu_frame_ptr>> writing_;
\r
219 gpu_frame_ptr output_frame_;
\r
220 tbb::concurrent_bounded_queue<gpu_frame_ptr> finished_frames_;
\r
222 frame_format_desc format_desc_;
\r
224 std::unique_ptr<sf::Context> context_;
\r
225 common::executor executor_;
\r
227 gpu_frame_ptr empty_frame_;
\r
230 gpu_frame_processor::gpu_frame_processor(const frame_format_desc& format_desc) : impl_(new implementation(format_desc)){}
\r
231 void gpu_frame_processor::push(const std::vector<gpu_frame_ptr>& frames){ impl_->composite(frames);}
\r
232 void gpu_frame_processor::pop(gpu_frame_ptr& frame){ impl_->pop(frame);}
\r
233 gpu_frame_ptr gpu_frame_processor::create_frame(size_t width, size_t height){return impl_->create_frame(width, height);}
\r