]> 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 #include "../../common/gl/frame_buffer_object.h"\r
14 \r
15 #include <Glee.h>\r
16 #include <SFML/Window.hpp>\r
17 \r
18 #include <tbb/concurrent_queue.h>\r
19 #include <tbb/concurrent_unordered_map.h>\r
20 \r
21 #include <boost/lexical_cast.hpp>\r
22 #include <boost/thread/once.hpp>\r
23 #include <boost/thread.hpp>\r
24 #include <boost/range.hpp>\r
25 #include <boost/foreach.hpp>\r
26 #include <boost/range/algorithm_ext/erase.hpp>\r
27 #include <boost/range/algorithm.hpp>\r
28 \r
29 #include <functional>\r
30 #include <unordered_map>\r
31 #include <numeric>\r
32 \r
33 namespace caspar { namespace core {\r
34         \r
35 struct gpu_frame_processor::implementation : boost::noncopyable\r
36 {       \r
37         implementation(const frame_format_desc& format_desc) \r
38                 : format_desc_(format_desc), index_(0)\r
39         {               \r
40                 input_.set_capacity(2);\r
41                 executor_.start();\r
42                 executor_.begin_invoke([=]\r
43                 {\r
44                         ogl_context_.reset(new sf::Context());\r
45                         ogl_context_->SetActive(true);\r
46                         GL(glEnable(GL_POLYGON_STIPPLE));\r
47                         GL(glEnable(GL_TEXTURE_2D));\r
48                         GL(glEnable(GL_BLEND));\r
49                         GL(glDisable(GL_DEPTH_TEST));\r
50                         GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));                  \r
51                         GL(glClearColor(0.0, 0.0, 0.0, 0.0));\r
52                         GL(glViewport(0, 0, format_desc_.width, format_desc_.height));\r
53                         glLoadIdentity();   \r
54 \r
55                         fbo_.create(format_desc_.width, format_desc_.height);\r
56                         fbo_.bind_pixel_source();\r
57 \r
58                         writing_.resize(2, std::make_shared<gpu_composite_frame>());\r
59 \r
60                         // Fill pipeline\r
61                         for(int n = 0; n < 2; ++n)\r
62                                 composite(std::vector<gpu_frame_ptr>());\r
63                 });\r
64         }\r
65 \r
66         ~implementation()\r
67         {\r
68                 executor_.stop();\r
69         }\r
70                         \r
71         void composite(std::vector<gpu_frame_ptr> frames)\r
72         {\r
73                 boost::range::remove_erase(frames, nullptr);\r
74                 boost::range::remove_erase(frames, gpu_frame::null());\r
75                 auto composite_frame = std::make_shared<gpu_composite_frame>();\r
76                 boost::range::for_each(frames, std::bind(&gpu_composite_frame::add, \r
77                                                                                                         composite_frame, \r
78                                                                                                         std::placeholders::_1));\r
79 \r
80                 input_.push(composite_frame);\r
81                 executor_.begin_invoke([=]\r
82                 {\r
83                         try\r
84                         {\r
85                                 gpu_frame_ptr frame;\r
86                                 input_.pop(frame);\r
87 \r
88                                 index_ = (index_ + 1) % 2;\r
89                                 int next_index = (index_ + 1) % 2;\r
90 \r
91                                 // 1. Start asynchronous DMA transfer to video memory.\r
92                                 writing_[index_] = std::move(frame);            \r
93                                 // Lock frame and give pointer ownership to OpenGL.\r
94                                 writing_[index_]->begin_write();\r
95                                 \r
96                                 // 3. Output to external buffer.\r
97                                 if(output_frame_)\r
98                                 {       \r
99                                         output_frame_->end_read();\r
100                                         output_.push(output_frame_);\r
101                                 }\r
102 \r
103                                 // Clear framebuffer.\r
104                                 GL(glClear(GL_COLOR_BUFFER_BIT));       \r
105 \r
106                                 // 2. Draw to framebuffer and start asynchronous DMA transfer \r
107                                 // to page-locked memory.\r
108                                 writing_[next_index]->draw();\r
109                                 \r
110                                 // Create an output frame\r
111                                 auto temp_frame = create_output_frame();\r
112                         \r
113                                 // Read from framebuffer into page-locked memory.\r
114                                 temp_frame->begin_read();\r
115                                 temp_frame->audio_data() = std::move(writing_[next_index]->audio_data());\r
116 \r
117                                 output_frame_ = temp_frame;\r
118 \r
119                                 // Return frames to pool.\r
120                                 writing_[next_index]->end_write();\r
121                                 writing_[next_index] = nullptr;\r
122                         }\r
123                         catch(...)\r
124                         {\r
125                                 CASPAR_LOG_CURRENT_EXCEPTION();\r
126                         }\r
127                 });     \r
128         }\r
129 \r
130         gpu_frame_ptr create_output_frame()\r
131         {\r
132                 gpu_frame_ptr frame;\r
133                 if(!reading_pool_.try_pop(frame))\r
134                         frame.reset(new gpu_frame(format_desc_.width, format_desc_.height));\r
135 \r
136                 return gpu_frame_ptr(frame.get(), [=](gpu_frame*)\r
137                 {\r
138                         frame->reset();\r
139                         reading_pool_.push(frame);\r
140                 });\r
141         }\r
142                         \r
143         gpu_frame_ptr create_frame(size_t width, size_t height)\r
144         {\r
145                 size_t key = width | (height << 16);\r
146                 auto& pool = writing_pools_[key];\r
147                 \r
148                 gpu_frame_ptr frame;\r
149                 if(!pool.try_pop(frame))\r
150                 {\r
151                         frame = executor_.invoke([&]\r
152                         {\r
153                                 return std::shared_ptr<gpu_frame>(new gpu_frame(width, height));\r
154                         });\r
155                 }\r
156                 \r
157                 auto destructor = [=]\r
158                 {\r
159                         frame->reset();\r
160                         writing_pools_[key].push(frame);\r
161                 };\r
162 \r
163                 return gpu_frame_ptr(frame.get(), [=](gpu_frame*)                                                       \r
164                 {\r
165                         executor_.begin_invoke(destructor);\r
166                 });\r
167         }\r
168         \r
169         void pop(gpu_frame_ptr& frame)\r
170         {\r
171                 output_.pop(frame);\r
172         }\r
173                         \r
174         typedef tbb::concurrent_bounded_queue<gpu_frame_ptr> gpu_frame_queue;\r
175         tbb::concurrent_unordered_map<size_t, gpu_frame_queue> writing_pools_;\r
176         gpu_frame_queue reading_pool_;  \r
177 \r
178         gpu_frame_queue input_;\r
179         std::vector<gpu_frame_ptr> writing_;\r
180         gpu_frame_queue output_;        \r
181 \r
182         size_t index_;\r
183 \r
184         gpu_frame_ptr output_frame_;                    \r
185         frame_format_desc format_desc_;\r
186         \r
187         std::unique_ptr<sf::Context> ogl_context_;\r
188         \r
189         common::executor executor_;\r
190 \r
191         common::gl::frame_buffer_object fbo_;\r
192 };\r
193         \r
194 gpu_frame_processor::gpu_frame_processor(const frame_format_desc& format_desc) : impl_(new implementation(format_desc)){}\r
195 void gpu_frame_processor::push(const std::vector<gpu_frame_ptr>& frames){ impl_->composite(frames);}\r
196 void gpu_frame_processor::pop(gpu_frame_ptr& frame){impl_->pop(frame);}\r
197 gpu_frame_ptr gpu_frame_processor::create_frame(size_t width, size_t height){return impl_->create_frame(width, height);}\r
198 \r
199 }}