Line data Source code
1 : /* Copyright (c) 2009, Cedric Stalder <cedric.stalder@gmail.com>
2 : * 2009, Maxim Makhinya
3 : * 2010-2013, Stefan Eilemann <eile@eyescale.ch>
4 : * 2012, Daniel Nachbaur <danielnachbaur@gmail.com>
5 : *
6 : * This library is free software; you can redistribute it and/or modify it under
7 : * the terms of the GNU Lesser General Public License version 2.1 as published
8 : * by the Free Software Foundation.
9 : *
10 : * This library is distributed in the hope that it will be useful, but WITHOUT
11 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 : * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
13 : * details.
14 : *
15 : * You should have received a copy of the GNU Lesser General Public License
16 : * along with this library; if not, write to the Free Software Foundation, Inc.,
17 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 : */
19 :
20 : #include "compressorYUV.h"
21 :
22 : #include <eq/fabric/pixelViewport.h>
23 : #include <eq/gl.h>
24 : #include <eq/util/frameBufferObject.h>
25 : #include <eq/zoomFilter.h>
26 :
27 : #include "yuv420readback_glsl.h"
28 : #include "yuv420unpack_glsl.h"
29 :
30 : #define glewGetContext() glewContext
31 :
32 : namespace eq
33 : {
34 : namespace plugin
35 : {
36 : namespace
37 : {
38 6 : static void _getInfo(EqCompressorInfo* const info)
39 : {
40 6 : info->version = EQ_COMPRESSOR_VERSION;
41 6 : info->name = EQ_COMPRESSOR_TRANSFER_RGBA_TO_YUVA_50P;
42 6 : info->capabilities = EQ_COMPRESSOR_TRANSFER | EQ_COMPRESSOR_DATA_2D |
43 : EQ_COMPRESSOR_USE_TEXTURE_RECT |
44 : EQ_COMPRESSOR_USE_FRAMEBUFFER;
45 6 : info->tokenType = EQ_COMPRESSOR_DATATYPE_RGBA;
46 6 : info->outputTokenType = EQ_COMPRESSOR_DATATYPE_YUVA_50P;
47 6 : info->outputTokenSize = 4;
48 6 : info->quality = 0.5f;
49 6 : info->ratio = 0.5f;
50 6 : info->speed = 0.5f;
51 6 : }
52 :
53 6 : static bool _register()
54 : {
55 : Compressor::registerEngine(
56 12 : Compressor::Functions(EQ_COMPRESSOR_TRANSFER_RGBA_TO_YUVA_50P, _getInfo,
57 : CompressorYUV::getNewCompressor,
58 : CompressorYUV::getNewDecompressor, 0,
59 6 : CompressorYUV::isCompatible));
60 6 : return true;
61 : }
62 :
63 6 : static bool _initialized LB_UNUSED = _register();
64 : }
65 :
66 : /** Construct a new compressor Yuv */
67 0 : CompressorYUV::CompressorYUV()
68 : : Compressor()
69 : , _program(0)
70 : , _fbo(0)
71 0 : , _texture(0)
72 : {
73 0 : }
74 :
75 : /** Destruct the compressor Yuv */
76 0 : CompressorYUV::~CompressorYUV()
77 : {
78 : // replace original texture
79 0 : delete _fbo;
80 0 : _fbo = 0;
81 :
82 0 : if (_texture)
83 : {
84 0 : _texture->flush();
85 0 : delete _texture;
86 : }
87 0 : _texture = 0;
88 0 : }
89 :
90 0 : bool CompressorYUV::isCompatible(const GLEWContext* glewContext)
91 : {
92 0 : return (GLEW_ARB_texture_non_power_of_two && GLEW_VERSION_2_0 &&
93 0 : GLEW_EXT_framebuffer_object);
94 : }
95 :
96 0 : void CompressorYUV::_initShader(const GLEWContext* glewContext LB_UNUSED,
97 : const char* fShaderPtr)
98 : {
99 0 : if (_program)
100 : {
101 : // use fragment shader and setup uniforms
102 0 : EQ_GL_CALL(glUseProgram(_program));
103 0 : return;
104 : }
105 :
106 : // Create fragment shader which reads depth values from
107 : // rectangular textures
108 0 : const GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
109 :
110 0 : EQ_GL_CALL(glShaderSource(shader, 1, &fShaderPtr, 0));
111 0 : EQ_GL_CALL(glCompileShader(shader));
112 :
113 : GLint status;
114 0 : glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
115 0 : LBASSERT(status);
116 :
117 0 : _program = glCreateProgram();
118 :
119 0 : EQ_GL_CALL(glAttachShader(_program, shader));
120 0 : EQ_GL_CALL(glLinkProgram(_program));
121 :
122 0 : glGetProgramiv(_program, GL_LINK_STATUS, &status);
123 0 : LBASSERT(status);
124 :
125 : // use fragment shader and setup uniforms
126 0 : EQ_GL_CALL(glUseProgram(_program));
127 : }
128 :
129 0 : void CompressorYUV::_compress(const GLEWContext* glewContext,
130 : const eq_uint64_t* /*inDims*/,
131 : eq_uint64_t outDims[4])
132 : {
133 : /* save the current FBO ID for bind it at the end of the compression */
134 0 : GLint oldFBO = 0;
135 0 : glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &oldFBO);
136 :
137 0 : if (_fbo)
138 : {
139 0 : const auto error = _fbo->resize(outDims[1], outDims[3]);
140 0 : if (error != ERROR_NONE)
141 : {
142 0 : LBERROR << "FBO resize failed: " << error << std::endl;
143 0 : return;
144 : }
145 0 : _fbo->bind();
146 : }
147 : else
148 : {
149 0 : _fbo = new util::FrameBufferObject(glewContext);
150 0 : const auto error = _fbo->init(outDims[1], outDims[3], GL_RGBA, 0, 0);
151 0 : if (error != ERROR_NONE)
152 : {
153 0 : LBERROR << "FBO init failed: " << error << std::endl;
154 0 : return;
155 : }
156 : }
157 :
158 0 : _texture->bind();
159 :
160 0 : glDisable(GL_LIGHTING);
161 0 : glEnable(GL_TEXTURE_RECTANGLE_ARB);
162 :
163 0 : _texture->applyZoomFilter(FILTER_NEAREST);
164 0 : _texture->applyWrap();
165 :
166 0 : _initShader(glewContext, yuv420readback_glsl.c_str());
167 :
168 0 : const GLint colorParam = glGetUniformLocation(_program, "color");
169 0 : EQ_GL_CALL(glUniform1i(colorParam, 0));
170 :
171 0 : glDisable(GL_DEPTH_TEST);
172 0 : glBegin(GL_QUADS);
173 0 : glVertex3i(0, 0, 0);
174 0 : glVertex3i(outDims[1], 0, 0);
175 0 : glVertex3i(outDims[1], outDims[3], 0);
176 0 : glVertex3i(0, outDims[3], 0);
177 0 : glEnd();
178 :
179 : // restore state
180 : // glEnable( GL_DEPTH_TEST );
181 0 : glDisable(GL_TEXTURE_RECTANGLE_ARB);
182 0 : EQ_GL_CALL(glUseProgram(0));
183 :
184 : /* apply the initial fbo */
185 0 : EQ_GL_CALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, oldFBO));
186 : }
187 :
188 0 : void CompressorYUV::_download(void* data)
189 : {
190 0 : util::Texture* texture = _fbo->getColorTextures()[0];
191 0 : LBASSERT(texture->getFormat() == GL_RGBA);
192 0 : LBASSERT(texture->getType() == GL_UNSIGNED_BYTE);
193 0 : texture->download(data);
194 0 : }
195 :
196 0 : void CompressorYUV::download(const GLEWContext* glewContext,
197 : const eq_uint64_t inDims[4], const unsigned source,
198 : const eq_uint64_t flags, eq_uint64_t outDims[4],
199 : void** out)
200 : {
201 : glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_TEXTURE_BIT |
202 0 : GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_LIGHTING_BIT);
203 0 : glColorMask(true, true, true, true);
204 0 : outDims[0] = inDims[0];
205 0 : outDims[1] = (inDims[1] + 1) / 2;
206 0 : outDims[2] = inDims[2];
207 0 : outDims[3] = (inDims[3] + 1) / 2;
208 0 : outDims[3] *= 2;
209 0 : buffer.resize(outDims[1] * outDims[3] * 4);
210 : // first time we instanciate the working texture
211 0 : if (!_texture)
212 0 : _texture = new util::Texture(GL_TEXTURE_RECTANGLE_ARB, glewContext);
213 :
214 : // the data is in the frame buffer
215 0 : if (flags & EQ_COMPRESSOR_USE_FRAMEBUFFER)
216 : {
217 : // read data in frame Buffer
218 0 : const eq::fabric::PixelViewport pvp(inDims[0], inDims[2], inDims[1],
219 0 : inDims[3]);
220 0 : _texture->init(GL_RGBA, outDims[1] * 2, outDims[3]);
221 0 : _texture->copyFromFrameBuffer(GL_RGBA, pvp);
222 :
223 : // compress data
224 0 : _compress(glewContext, inDims, outDims);
225 0 : buffer.resize(outDims[1] * outDims[3] * 4);
226 0 : _download(buffer.getData());
227 : }
228 : // the data is in the texture id define by the field "source"
229 0 : else if (flags & EQ_COMPRESSOR_USE_TEXTURE_RECT)
230 : {
231 : // assign texture id to the local texture class
232 : // compress Data
233 : // allow buffer memory on cpu
234 : // transfer data from gpu to cpu
235 0 : _texture->setGLData(source, GL_RGBA, inDims[1], inDims[3]);
236 0 : _compress(glewContext, inDims, outDims);
237 0 : _download(buffer.getData());
238 0 : _texture->flushNoDelete();
239 : }
240 : else
241 : {
242 0 : LBUNIMPLEMENTED;
243 : }
244 0 : out[0] = buffer.getData();
245 0 : glPopAttrib();
246 0 : }
247 :
248 0 : void CompressorYUV::_decompress(const GLEWContext* glewContext,
249 : const eq_uint64_t inDims[4])
250 : {
251 0 : glDepthMask(false);
252 0 : _initShader(glewContext, yuv420unpack_glsl.c_str());
253 :
254 0 : const GLint colorParam = glGetUniformLocation(_program, "color");
255 0 : glUniform1i(colorParam, 0);
256 :
257 0 : glDisable(GL_LIGHTING);
258 0 : glEnable(GL_TEXTURE_RECTANGLE_ARB);
259 :
260 0 : _texture->applyWrap();
261 0 : _texture->applyZoomFilter(FILTER_NEAREST);
262 :
263 0 : glColor3f(1.0f, 1.0f, 1.0f);
264 0 : const float startX = static_cast<float>(inDims[0]);
265 0 : const float endX = static_cast<float>(inDims[1]) + startX;
266 0 : const float startY = static_cast<float>(inDims[2]);
267 0 : const float endY = static_cast<float>(inDims[3]) + startY;
268 :
269 0 : const GLint shiftX = glGetUniformLocation(_program, "shiftX");
270 0 : glUniform1f(shiftX, startX);
271 0 : const GLint shiftY = glGetUniformLocation(_program, "shiftY");
272 0 : glUniform1f(shiftY, startY);
273 :
274 0 : glBegin(GL_QUADS);
275 0 : glVertex3f(startX, startY, 0.0f);
276 0 : glVertex3f(endX, startY, 0.0f);
277 0 : glVertex3f(endX, endY, 0.0f);
278 0 : glVertex3f(startX, endY, 0.0f);
279 0 : glEnd();
280 :
281 0 : glDisable(GL_TEXTURE_RECTANGLE_ARB);
282 0 : EQ_GL_CALL(glUseProgram(0));
283 0 : glDepthMask(true);
284 0 : }
285 :
286 0 : void CompressorYUV::upload(const GLEWContext* glewContext, const void* data,
287 : const eq_uint64_t inDims[4], const eq_uint64_t flags,
288 : const eq_uint64_t outDims[4],
289 : const unsigned destination)
290 : {
291 : glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_TEXTURE_BIT |
292 0 : GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_LIGHTING_BIT);
293 0 : if (!_texture)
294 : {
295 0 : _texture = new util::Texture(GL_TEXTURE_RECTANGLE_ARB, glewContext);
296 0 : _texture->init(GL_RGBA, outDims[1], outDims[3]);
297 : }
298 :
299 0 : if (flags & EQ_COMPRESSOR_USE_FRAMEBUFFER)
300 : {
301 0 : _texture->upload(inDims[1], inDims[3], data);
302 0 : _decompress(glewContext, outDims);
303 : }
304 0 : else if (flags & EQ_COMPRESSOR_USE_TEXTURE_RECT)
305 : {
306 : /* save the current FBO ID for bind it at the end of the compression */
307 0 : GLint oldFBO = 0;
308 0 : glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &oldFBO);
309 :
310 0 : if (!_fbo)
311 0 : _fbo = new util::FrameBufferObject(glewContext);
312 :
313 0 : util::Texture* texture = _fbo->getColorTextures().front();
314 0 : texture->setGLData(destination, GL_RGBA, outDims[1], outDims[3]);
315 :
316 0 : if (_fbo->isValid())
317 : {
318 0 : _fbo->bind();
319 0 : texture->bindToFBO(GL_COLOR_ATTACHMENT0, outDims[1], outDims[3]);
320 : }
321 : else
322 : {
323 0 : LBCHECK(_fbo->init(outDims[1], outDims[3], GL_RGBA, 0, 0));
324 : }
325 :
326 0 : _texture->upload(inDims[1], inDims[3], data);
327 0 : _decompress(glewContext, outDims);
328 :
329 : /* apply the initial fbo */
330 0 : EQ_GL_CALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, oldFBO));
331 :
332 0 : texture->flushNoDelete();
333 : }
334 : else
335 : {
336 0 : LBASSERT(0);
337 : }
338 0 : glPopAttrib();
339 0 : }
340 : }
341 18 : }
|