]> git.sesse.net Git - casparcg/blob - core/frame/gpu_frame_processor.cpp
2.0.0.2:
[casparcg] / core / frame / gpu_frame_processor.cpp
1 #include "../StdAfx.h"\r
2 \r
3 #include "gpu_frame_processor.h"\r
4 \r
5 #include "gpu_frame.h"\r
6 #include "gpu_composite_frame.h"\r
7 #include "frame_format.h"\r
8 \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
13 \r
14 #include <Glee.h>\r
15 #include <SFML/Window.hpp>\r
16 \r
17 #include <tbb/concurrent_queue.h>\r
18 #include <tbb/concurrent_unordered_map.h>\r
19 \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
27 \r
28 #include <functional>\r
29 #include <unordered_map>\r
30 #include <numeric>\r
31 \r
32 namespace caspar { namespace core {\r
33         \r
34 struct gpu_frame_processor::implementation : boost::noncopyable\r
35 {       \r
36         implementation(const frame_format_desc& format_desc) \r
37                 : format_desc_(format_desc), index_(0)\r
38         {               \r
39                 input_.set_capacity(2);\r
40                 executor_.start();\r
41                 executor_.begin_invoke([=]\r
42                 {\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
52                         glLoadIdentity();       \r
53                                                 \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
65                         \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
69                 });\r
70                 // Fill pipeline\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
74         }\r
75 \r
76         ~implementation()\r
77         {\r
78                 glDeleteFramebuffersEXT(1, &fbo_);\r
79                 executor_.stop();\r
80         }\r
81                         \r
82         void composite(std::vector<gpu_frame_ptr> frames)\r
83         {\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
88                                                                                                         composite_frame, \r
89                                                                                                         std::placeholders::_1));\r
90 \r
91                 input_.push(composite_frame);\r
92                 executor_.begin_invoke([=]\r
93                 {\r
94                         try\r
95                         {\r
96                                 gpu_frame_ptr frame;\r
97                                 input_.pop(frame);\r
98 \r
99                                 index_ = (index_ + 1) % 2;\r
100                                 int next_index = (index_ + 1) % 2;\r
101 \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
106                                 \r
107                                 // 3. Output to external buffer.\r
108                                 if(output_frame_->read_unlock())\r
109                                         output_.push(output_frame_);\r
110                 \r
111                                 // Clear framebuffer.\r
112                                 glClear(GL_COLOR_BUFFER_BIT);   \r
113 \r
114                                 // 2. Draw to framebuffer and start asynchronous DMA transfer \r
115                                 // to page-locked memory.\r
116                                 writing_[next_index]->draw();\r
117                                 \r
118                                 // Create an output frame\r
119                                 output_frame_ = create_output_frame();\r
120                         \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
124 \r
125                                 // Return frames to pool.\r
126                                 writing_[next_index] = nullptr;\r
127                         }\r
128                         catch(...)\r
129                         {\r
130                                 CASPAR_LOG_CURRENT_EXCEPTION();\r
131                         }\r
132                 });     \r
133         }\r
134 \r
135         gpu_frame_ptr create_output_frame()\r
136         {\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
141 \r
142                 return gpu_frame_ptr(frame.get(), [=](gpu_frame*)\r
143                 {\r
144                         frame->reset();\r
145                         reading_pool_.push(frame);\r
146                 });\r
147         }\r
148                         \r
149         gpu_frame_ptr create_frame(size_t width, size_t height)\r
150         {\r
151                 size_t key = width | (height << 16);\r
152                 auto& pool = writing_pools_[key];\r
153                 \r
154                 gpu_frame_ptr frame;\r
155                 if(!pool.try_pop(frame))\r
156                 {\r
157                         frame = executor_.invoke([=]() -> gpu_frame_ptr\r
158                         {\r
159                                 auto frame = std::make_shared<gpu_frame>(width, height);\r
160                                 frame->write_unlock();\r
161                                 return frame;\r
162                         });\r
163                 }\r
164                 \r
165                 auto destructor = [=]\r
166                 {\r
167                         frame->write_unlock();\r
168                         frame->reset();\r
169                         writing_pools_[key].push(frame);\r
170                 };\r
171 \r
172                 return gpu_frame_ptr(frame.get(), [=](gpu_frame*)                                                       \r
173                 {\r
174                         executor_.begin_invoke(destructor);\r
175                 });\r
176         }\r
177         \r
178         void pop(gpu_frame_ptr& frame)\r
179         {\r
180                 output_.pop(frame);\r
181         }\r
182                         \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
186 \r
187         gpu_frame_queue input_;\r
188         std::vector<gpu_frame_ptr> writing_;\r
189         gpu_frame_queue output_;        \r
190 \r
191         size_t index_;\r
192 \r
193         gpu_frame_ptr output_frame_;                    \r
194         frame_format_desc format_desc_;\r
195         \r
196         std::unique_ptr<sf::Context> ogl_context_;\r
197         \r
198         common::executor executor_;\r
199 \r
200         GLuint render_texture_;\r
201         GLuint fbo_;\r
202 };\r
203         \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
208 \r
209 }}