]> git.sesse.net Git - casparcg/blob - accelerator/ogl/util/device.cpp
set svn:eol-style native on .h and .cpp files
[casparcg] / accelerator / ogl / util / device.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Robert Nagy, ronag89@gmail.com
20 */
21
22 // TODO: Smart GC
23
24 #include "../../stdafx.h"
25
26 #include "device.h"
27
28 #include "buffer.h"
29 #include "texture.h"
30 #include "shader.h"
31
32 #include <common/assert.h>
33 #include <common/except.h>
34 #include <common/future.h>
35 #include <common/array.h>
36 #include <common/gl/gl_check.h>
37 #include <common/os/windows/windows.h>
38
39 #include <boost/foreach.hpp>
40
41 #include <gl/glew.h>
42
43 #include <SFML/Window/Context.hpp>
44
45 #include <tbb/concurrent_unordered_map.h>
46 #include <tbb/concurrent_hash_map.h>
47 #include <tbb/concurrent_queue.h>
48
49 #include <boost/utility/declval.hpp>
50
51 #include <array>
52 #include <unordered_map>
53
54 #include <asmlib.h>
55 #include <tbb/parallel_for.h>
56
57 namespace caspar { namespace accelerator { namespace ogl {
58                 
59 struct device::impl : public std::enable_shared_from_this<impl>
60 {       
61         static_assert(std::is_same<decltype(boost::declval<device>().impl_), spl::shared_ptr<impl>>::value, "impl_ must be shared_ptr");
62
63         tbb::concurrent_hash_map<buffer*, std::shared_ptr<texture>> texture_cache_;
64
65         std::unique_ptr<sf::Context> device_;
66         
67         std::array<tbb::concurrent_unordered_map<std::size_t, tbb::concurrent_bounded_queue<std::shared_ptr<texture>>>, 4>      device_pools_;
68         std::array<tbb::concurrent_unordered_map<std::size_t, tbb::concurrent_bounded_queue<std::shared_ptr<buffer>>>, 2>       host_pools_;
69         
70         GLuint fbo_;
71
72         executor& executor_;
73                                 
74         impl(executor& executor) 
75                 : executor_(executor)
76         {
77                 executor_.set_capacity(256);
78
79                 CASPAR_LOG(info) << L"Initializing OpenGL Device.";
80                 
81                 executor_.invoke([=]
82                 {
83                         device_.reset(new sf::Context());
84                         device_->SetActive(true);               
85                                                 
86                         if (glewInit() != GLEW_OK)
87                                 CASPAR_THROW_EXCEPTION(gl::ogl_exception() << msg_info("Failed to initialize GLEW."));
88                 
89                         if(!GLEW_VERSION_3_0)
90                                 CASPAR_THROW_EXCEPTION(not_supported() << msg_info("Your graphics card does not meet the minimum hardware requirements since it does not support OpenGL 3.0 or higher."));
91         
92                         glGenFramebuffers(1, &fbo_);                            
93                         glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
94                 });
95                                 
96                 CASPAR_LOG(info) << L"Successfully initialized OpenGL " << version();
97         }
98
99         ~impl()
100         {
101                 executor_.invoke([=]
102                 {
103                         BOOST_FOREACH(auto& pool, device_pools_)
104                                 pool.clear();
105                         glDeleteFramebuffers(1, &fbo_);
106
107                         device_.reset();
108                 });
109         }
110                 
111         std::wstring version()
112         {       
113                 try
114                 {
115                         return executor_.invoke([]
116                         {
117                                 return u16(reinterpret_cast<const char*>(GL2(glGetString(GL_VERSION)))) + L" " + u16(reinterpret_cast<const char*>(GL2(glGetString(GL_VENDOR))));
118                         });     
119                 }
120                 catch(...)
121                 {
122                         return L"Not found";;
123                 }
124         }
125                                                         
126         spl::shared_ptr<texture> create_texture(int width, int height, int stride, bool clear = false)
127         {
128                 CASPAR_VERIFY(stride > 0 && stride < 5);
129                 CASPAR_VERIFY(width > 0 && height > 0);
130
131                 if(!executor_.is_current())
132                         CASPAR_THROW_EXCEPTION(invalid_operation() << msg_info("Operation only valid in an OpenGL Context."));
133                                         
134                 auto pool = &device_pools_[stride-1][((width << 16) & 0xFFFF0000) | (height & 0x0000FFFF)];
135                 
136                 std::shared_ptr<texture> tex;
137                 if(!pool->try_pop(tex))         
138                         tex = spl::make_shared<texture>(width, height, stride);
139         
140                 if(clear)
141                         tex->clear();
142
143                 return spl::shared_ptr<texture>(tex.get(), [tex, pool](texture*) mutable
144                 {               
145                         pool->push(tex);        
146                 });
147         }
148                 
149         spl::shared_ptr<buffer> create_buffer(std::size_t size, buffer::usage usage)
150         {
151                 CASPAR_VERIFY(size > 0);
152                 
153                 auto pool = &host_pools_[usage.value()][size];
154                 
155                 std::shared_ptr<buffer> buf;
156                 if(!pool->try_pop(buf)) 
157                 {
158                         boost::timer timer;
159
160                         buf = executor_.invoke([&]
161                         {
162                                 return spl::make_shared<buffer>(size, usage);
163                         }, task_priority::high_priority);
164                         
165                         if(timer.elapsed() > 0.02)
166                                 CASPAR_LOG(debug) << L"[ogl-device] Performance warning. Buffer allocation blocked: " << timer.elapsed();
167                 }
168                 
169                 auto self = shared_from_this(); // buffers can leave the device context, take a hold on life-time.
170                 return spl::shared_ptr<buffer>(buf.get(), [=](buffer*) mutable
171                 {       
172                         texture_cache_.erase(buf.get());
173                         pool->push(buf);
174                 });
175         }
176
177         array<std::uint8_t> create_array(std::size_t size)
178         {               
179                 auto buf = create_buffer(size, buffer::usage::write_only);
180                 return array<std::uint8_t>(buf->data(), buf->size(), false, buf);
181         }
182
183         template<typename T>
184         std::shared_ptr<buffer> copy_to_buf(const T& source)
185         {
186                 std::shared_ptr<buffer> buf;
187
188                 auto tmp = source.storage<spl::shared_ptr<buffer>>();
189                 if(tmp)
190                         buf = *tmp;
191                 else
192                 {                       
193                         buf = create_buffer(source.size(), buffer::usage::write_only);
194                         tbb::parallel_for(tbb::blocked_range<std::size_t>(0, source.size()), [&](const tbb::blocked_range<std::size_t>& r)
195                         {
196                                 A_memcpy(buf->data() + r.begin(), source.data() + r.begin(), r.size());
197                         });
198                 }
199
200                 return buf;
201         }
202
203         // TODO: Since the returned texture is cached it SHOULD NOT be modified.
204         boost::unique_future<spl::shared_ptr<texture>> copy_async(const array<const std::uint8_t>& source, int width, int height, int stride)
205         {
206                 std::shared_ptr<buffer> buf = copy_to_buf(source);
207                                 
208                 return executor_.begin_invoke([=]() -> spl::shared_ptr<texture>
209                 {
210                         tbb::concurrent_hash_map<buffer*, std::shared_ptr<texture>>::const_accessor a;
211                         if(texture_cache_.find(a, buf.get()))
212                                 return spl::make_shared_ptr(a->second);
213
214                         auto texture = create_texture(width, height, stride);
215                         texture->copy_from(*buf);       
216
217                         texture_cache_.insert(std::make_pair(buf.get(), texture));
218                         
219                         return texture;
220                 }, task_priority::high_priority);
221         }
222         
223         boost::unique_future<spl::shared_ptr<texture>> copy_async(const array<std::uint8_t>& source, int width, int height, int stride)
224         {
225                 std::shared_ptr<buffer> buf = copy_to_buf(source);
226
227                 return executor_.begin_invoke([=]() -> spl::shared_ptr<texture>
228                 {
229                         auto texture = create_texture(width, height, stride, false);
230                         texture->copy_from(*buf);       
231                         
232                         return texture;
233                 }, task_priority::high_priority);
234         }
235
236         boost::unique_future<array<const std::uint8_t>> copy_async(const spl::shared_ptr<texture>& source)
237         {
238                 if(!executor_.is_current())
239                         CASPAR_THROW_EXCEPTION(invalid_operation() << msg_info("Operation only valid in an OpenGL Context."));
240
241                 auto buffer = create_buffer(source->size(), buffer::usage::read_only); 
242                 source->copy_to(*buffer);       
243
244                 auto self = shared_from_this();
245                 return async(launch::deferred, [self, buffer]() mutable -> array<const std::uint8_t>
246                 {
247                         self->executor_.invoke(std::bind(&buffer::map, std::ref(buffer))); // Defer blocking "map" call until data is needed.
248                         return array<const std::uint8_t>(buffer->data(), buffer->size(), true, buffer);
249                 });
250         }
251 };
252
253 device::device() 
254         : executor_(L"OpenGL Rendering Context")
255         , impl_(new impl(executor_)){}
256 device::~device(){}
257 spl::shared_ptr<texture>                                                        device::create_texture(int width, int height, int stride){return impl_->create_texture(width, height, stride, true);}
258 array<std::uint8_t>                                                                     device::create_array(int size){return impl_->create_array(size);}
259 boost::unique_future<spl::shared_ptr<texture>>          device::copy_async(const array<const std::uint8_t>& source, int width, int height, int stride){return impl_->copy_async(source, width, height, stride);}
260 boost::unique_future<spl::shared_ptr<texture>>          device::copy_async(const array<std::uint8_t>& source, int width, int height, int stride){return impl_->copy_async(source, width, height, stride);}
261 boost::unique_future<array<const std::uint8_t>>         device::copy_async(const spl::shared_ptr<texture>& source){return impl_->copy_async(source);}
262 std::wstring                                                                            device::version() const{return impl_->version();}
263
264
265 }}}
266
267