]> git.sesse.net Git - casparcg/blob - common/gl/pixel_buffer_object.cpp
e4b46cba1f504f54106ecc3b3c4d154ee6817949
[casparcg] / common / gl / pixel_buffer_object.cpp
1 #include "../StdAfx.h"\r
2 \r
3 #include "pixel_buffer_object.h"\r
4 \r
5 #include "../../common/exception/exceptions.h"\r
6 #include "../../common/gl/utility.h"\r
7 #include "../../common/utility/memory.h"\r
8 \r
9 namespace caspar { namespace common { namespace gl {\r
10                                                                                                                                                                                                                                                                                                                         \r
11 struct pixel_buffer_object::implementation : boost::noncopyable\r
12 {\r
13         implementation(size_t width, size_t height, GLenum format) \r
14                 : width_(width), height_(height), pbo_(0), format_(format), data_(nullptr),\r
15                         texture_(0), writing_(false), reading_(false), mapped_(false)\r
16         {\r
17                 switch(format)\r
18                 {\r
19                 case GL_RGBA:\r
20                 case GL_BGRA:\r
21                         internal_ = GL_RGBA8;\r
22                         size_ = width*height*4;\r
23                         break;\r
24                 case GL_BGR:\r
25                         internal_ = GL_RGB8;\r
26                         size_ = width*height*3;\r
27                         break;\r
28                 case GL_LUMINANCE_ALPHA:\r
29                         internal_ = GL_LUMINANCE_ALPHA;\r
30                         size_ = width*height*2;\r
31                         break;\r
32                 case GL_LUMINANCE:\r
33                 case GL_ALPHA:\r
34                         internal_ = GL_LUMINANCE;\r
35                         size_ = width*height*1;\r
36                         break;\r
37                 default:\r
38                         BOOST_THROW_EXCEPTION(invalid_argument() << msg_info("Unsupported format.") << arg_name_info("format"));\r
39                 }\r
40                 if(width < 2 || height < 2)\r
41                         BOOST_THROW_EXCEPTION(invalid_argument() << msg_info("Invalid dimensions.")  << arg_name_info("width/height"));\r
42         }\r
43 \r
44         ~implementation()\r
45         {\r
46                 if(pbo_ != 0)\r
47                         glDeleteBuffers(1, &pbo_);\r
48                 if(texture_ != 0)\r
49                         glDeleteTextures(1, &texture_);\r
50         }       \r
51 \r
52         void bind_pbo(GLenum mode)\r
53         {\r
54                 if(pbo_ == 0)\r
55                         GL(glGenBuffers(1, &pbo_));\r
56                 GL(glBindBuffer(mode, pbo_));\r
57         }\r
58 \r
59         void unbind_pbo(GLenum mode)\r
60         {\r
61                 GL(glBindBuffer(mode, 0));\r
62         }\r
63         \r
64         void bind_texture()\r
65         {\r
66                 if(texture_ == 0)\r
67                 {\r
68                         GL(glGenTextures(1, &texture_));\r
69 \r
70                         GL(glBindTexture(GL_TEXTURE_2D, texture_));\r
71 \r
72                         GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));\r
73                         GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));\r
74                         GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));\r
75                         GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));\r
76 \r
77                         GL(glTexImage2D(GL_TEXTURE_2D, 0, internal_, width_, height_, 0, format_, GL_UNSIGNED_BYTE, NULL));\r
78                 }\r
79                 GL(glBindTexture(GL_TEXTURE_2D, texture_));\r
80         }\r
81 \r
82         void begin_write()\r
83         {\r
84                 bind_pbo(GL_PIXEL_UNPACK_BUFFER);\r
85                 if(mapped_)\r
86                         GL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));\r
87                 mapped_ = false;\r
88                 bind_texture();\r
89                 GL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, format_, GL_UNSIGNED_BYTE, NULL));\r
90                 unbind_pbo(GL_PIXEL_UNPACK_BUFFER);\r
91                 writing_ = true;\r
92         }\r
93 \r
94         void* end_write()\r
95         {\r
96                 if(mapped_)\r
97                 {\r
98                         if(!writing_)\r
99                                 return data_;\r
100                         else\r
101                                 BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("Buffer is already mapped."));\r
102                 }\r
103 \r
104                 bind_pbo(GL_PIXEL_UNPACK_BUFFER);\r
105                 GL(glBufferData(GL_PIXEL_UNPACK_BUFFER, size_, NULL, GL_STREAM_DRAW));\r
106                 data_= glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);\r
107                 unbind_pbo(GL_PIXEL_UNPACK_BUFFER);             \r
108                 if(!data_)\r
109                         BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("glMapBuffer failed"));\r
110                 writing_ = false;\r
111                 mapped_ = true;\r
112                 return data_;\r
113         }\r
114         \r
115         void begin_read()\r
116         {       \r
117                 bind_pbo(GL_PIXEL_PACK_BUFFER);\r
118                 if(mapped_)\r
119                         GL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));        \r
120                 mapped_ = false;\r
121                 GL(glBufferData(GL_PIXEL_PACK_BUFFER, size_, NULL, GL_STREAM_READ));    \r
122                 GL(glReadPixels(0, 0, width_, height_, GL_BGRA, GL_UNSIGNED_BYTE, NULL));\r
123                 unbind_pbo(GL_PIXEL_PACK_BUFFER);\r
124                 reading_ = true;\r
125         }\r
126 \r
127         void* end_read()\r
128         {\r
129                 if(mapped_)\r
130                 {\r
131                         if(!reading_)\r
132                                 return data_;\r
133                         else\r
134                                 BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("Buffer is already mapped."));\r
135                 }\r
136 \r
137                 bind_pbo(GL_PIXEL_PACK_BUFFER);\r
138                 data_ = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);   \r
139                 unbind_pbo(GL_PIXEL_PACK_BUFFER);\r
140                 if(!data_)\r
141                         BOOST_THROW_EXCEPTION(std::bad_alloc());\r
142                 reading_ = false;\r
143                 mapped_ = true;\r
144                 return data_;\r
145         }\r
146 \r
147         void is_smooth(bool smooth)\r
148         {\r
149                 if(smooth)\r
150                 {\r
151                         GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));\r
152                         GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));\r
153                 }\r
154                 else\r
155                 {\r
156                         GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));\r
157                         GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));\r
158                 }\r
159         }\r
160         \r
161         GLuint pbo_;\r
162         GLuint texture_;\r
163         size_t width_;\r
164         size_t height_;\r
165         size_t size_;\r
166 \r
167         bool mapped_;\r
168         bool writing_;\r
169         bool reading_;\r
170 \r
171         GLint internal_;\r
172         GLenum format_;\r
173         void* data_;\r
174 };\r
175 \r
176 pixel_buffer_object::pixel_buffer_object(){}\r
177 pixel_buffer_object::pixel_buffer_object(size_t width, size_t height, GLenum format) \r
178         : impl_(new implementation(width, height, format)){}\r
179 void pixel_buffer_object::create(size_t width, size_t height, GLenum format)\r
180 {\r
181         impl_.reset(new implementation(width, height, format));\r
182 }\r
183 void pixel_buffer_object::begin_write() { impl_->begin_write();}\r
184 void* pixel_buffer_object::end_write() {return impl_->end_write();} \r
185 void pixel_buffer_object::begin_read() { impl_->begin_read();}\r
186 void* pixel_buffer_object::end_read(){return impl_->end_read();}\r
187 void pixel_buffer_object::bind_texture() {impl_->bind_texture();}\r
188 size_t pixel_buffer_object::width() const {return impl_->width_;}\r
189 size_t pixel_buffer_object::height() const {return impl_->height_;}\r
190 size_t pixel_buffer_object::size() const {return impl_->size_;}\r
191 bool pixel_buffer_object::is_reading() const { return impl_->reading_;}\r
192 bool pixel_buffer_object::is_writing() const { return impl_->writing_;}\r
193 void pixel_buffer_object::is_smooth(bool smooth){impl_->is_smooth(smooth);}\r
194 }}}