Line data Source code
1 :
2 : /* Copyright (c) 2007-2017, Stefan Eilemann <eile@equalizergraphics.com>
3 : * Daniel Nachbaur <danielnachbaur@gmail.com>
4 : * Cedric Stalder <cedric.stalder@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 <lunchbox/perThread.h>
21 :
22 : #include "channel.h"
23 : #include "channelStatistics.h"
24 : #include "client.h"
25 : #include "compositor.h"
26 : #include "config.h"
27 : #include "exception.h"
28 : #include "frameData.h"
29 : #include "gl.h"
30 : #include "image.h"
31 : #include "imageOp.h"
32 : #include "log.h"
33 : #include "pixelData.h"
34 : #include "server.h"
35 : #include "window.h"
36 : #include "windowSystem.h"
37 :
38 : #include <eq/util/accum.h>
39 : #include <eq/util/objectManager.h>
40 : #include <eq/util/shader.h>
41 :
42 : #include <co/global.h>
43 : #include <lunchbox/debug.h>
44 : #include <lunchbox/monitor.h>
45 : #include <lunchbox/os.h>
46 : #include <pression/plugins/compressor.h>
47 :
48 : using lunchbox::Monitor;
49 :
50 : namespace eq
51 : {
52 : #define glewGetContext channel->glewGetContext
53 :
54 : namespace
55 : {
56 : // use to address one shader and program per shared context set
57 : static const char seed = 42;
58 : static const char* shaderDBKey = &seed;
59 10 : static const char* colorDBKey = shaderDBKey + 1;
60 10 : static const char* depthDBKey = shaderDBKey + 2;
61 :
62 : // Image used for CPU-based assembly
63 10 : static lunchbox::PerThread<Image> _resultImage;
64 :
65 : struct CPUAssemblyFormat
66 : {
67 0 : CPUAssemblyFormat(const bool blend_)
68 0 : : colorInt(0)
69 : , colorExt(0)
70 : , depthInt(0)
71 : , depthExt(0)
72 0 : , blend(blend_)
73 : {
74 0 : }
75 :
76 : uint32_t colorInt;
77 : uint32_t colorExt;
78 : uint32_t depthInt;
79 : uint32_t depthExt;
80 : const bool blend;
81 : };
82 :
83 0 : bool _useCPUAssembly(const Image* image, CPUAssemblyFormat& format)
84 : {
85 0 : const bool hasColor = image->hasPixelData(Frame::Buffer::color);
86 0 : const bool hasDepth = image->hasPixelData(Frame::Buffer::depth);
87 :
88 0 : if ( // Not an alpha-blending compositing
89 0 : (!format.blend || !hasColor || !image->hasAlpha()) &&
90 : // and not a depth-sorting compositing
91 0 : (!hasColor || !hasDepth))
92 : {
93 0 : return false;
94 : }
95 :
96 0 : if (format.colorInt == 0)
97 0 : format.colorInt = image->getInternalFormat(Frame::Buffer::color);
98 0 : if (format.colorExt == 0)
99 0 : format.colorExt = image->getExternalFormat(Frame::Buffer::color);
100 :
101 0 : if (format.colorInt != image->getInternalFormat(Frame::Buffer::color) ||
102 0 : format.colorExt != image->getExternalFormat(Frame::Buffer::color))
103 : {
104 0 : return false;
105 : }
106 :
107 0 : switch (format.colorExt)
108 : {
109 : case EQ_COMPRESSOR_DATATYPE_RGB10_A2:
110 : case EQ_COMPRESSOR_DATATYPE_BGR10_A2:
111 0 : if (!hasDepth)
112 : // blending of RGB10A2 not implemented
113 0 : return false;
114 0 : break;
115 :
116 : case EQ_COMPRESSOR_DATATYPE_RGBA:
117 : case EQ_COMPRESSOR_DATATYPE_BGRA:
118 0 : break;
119 :
120 : default:
121 0 : return false;
122 : }
123 :
124 0 : if (!hasDepth)
125 0 : return true;
126 :
127 0 : if (format.depthInt == 0)
128 0 : format.depthInt = image->getInternalFormat(Frame::Buffer::depth);
129 0 : if (format.depthExt == 0)
130 0 : format.depthExt = image->getExternalFormat(Frame::Buffer::depth);
131 :
132 0 : if (format.depthInt != image->getInternalFormat(Frame::Buffer::depth) ||
133 0 : format.depthExt != image->getExternalFormat(Frame::Buffer::depth))
134 : {
135 0 : return false;
136 : }
137 :
138 0 : return format.depthExt == EQ_COMPRESSOR_DATATYPE_DEPTH_UNSIGNED_INT;
139 : }
140 :
141 1 : bool _useCPUAssembly(const Frames& frames, Channel* channel,
142 : const bool blend = false)
143 : {
144 : // It doesn't make sense to use CPU-assembly for only one frame
145 1 : if (frames.size() < 2)
146 1 : return false;
147 :
148 : // Test that the input frames have color and depth buffers or that
149 : // alpha-blended assembly is used with multiple RGBA buffers. We assume then
150 : // that we will have at least one image per frame so most likely it's worth
151 : // to wait for the images and to do a CPU-based assembly. Also test early
152 : // for unsupported decomposition modes
153 : const Frame::Buffer desiredBuffers =
154 0 : blend ? Frame::Buffer::color
155 0 : : Frame::Buffer::color | Frame::Buffer::depth;
156 0 : for (const Frame* frame : frames)
157 : {
158 0 : const RenderContext& context = frame->getFrameData()->getContext();
159 :
160 0 : if (frame->getBuffers() != desiredBuffers ||
161 0 : context.pixel != Pixel::ALL || context.subPixel != SubPixel::ALL ||
162 0 : frame->getFrameData()->getZoom() != Zoom::NONE ||
163 0 : frame->getZoom() != Zoom::NONE) // Not supported by CPU compositor
164 : {
165 0 : return false;
166 : }
167 : }
168 :
169 : // Wait for all images to be ready and test if our assumption was correct,
170 : // that there are enough images to make a CPU-based assembly worthwhile and
171 : // all other preconditions for our CPU-based assembly code are true.
172 0 : size_t nImages = 0;
173 0 : const uint32_t timeout = channel->getConfig()->getTimeout();
174 0 : CPUAssemblyFormat format(blend);
175 :
176 0 : for (const Frame* frame : frames)
177 : {
178 : {
179 : ChannelStatistics event(Statistic::CHANNEL_FRAME_WAIT_READY,
180 0 : channel);
181 0 : frame->waitReady(timeout);
182 : }
183 :
184 0 : const Images& images = frame->getImages();
185 0 : for (const Image* image : images)
186 : {
187 0 : if (!_useCPUAssembly(image, format))
188 0 : return false;
189 0 : ++nImages;
190 : }
191 : }
192 0 : return (nImages > 1);
193 : }
194 :
195 0 : bool _useCPUAssembly(const ImageOps& ops, const bool blend)
196 : {
197 0 : CPUAssemblyFormat format(blend);
198 0 : size_t nImages = 0;
199 :
200 0 : for (const ImageOp& op : ops)
201 : {
202 0 : if (!_useCPUAssembly(op.image, format))
203 0 : return false;
204 0 : ++nImages;
205 : }
206 0 : return (nImages > 1);
207 : }
208 :
209 0 : uint32_t _assembleCPUImage(const Image* image, Channel* channel)
210 : {
211 0 : if (!image)
212 0 : return 0;
213 :
214 : // assemble result on dest channel
215 0 : ImageOp operation;
216 0 : operation.image = image;
217 0 : operation.buffers = Frame::Buffer::color | Frame::Buffer::depth;
218 0 : Compositor::assembleImage(operation, channel);
219 :
220 : #if 0
221 : static uint32_t counter = 0;
222 : std::ostringstream stringstream;
223 : stringstream << "Image_" << ++counter;
224 : image->writeImages( stringstream.str( ));
225 : #endif
226 :
227 0 : return 1;
228 : }
229 :
230 84 : void _collectOutputData(const PixelData& pixelData, uint32_t& internalFormat,
231 : uint32_t& pixelSize, uint32_t& externalFormat)
232 : {
233 84 : LBASSERT(internalFormat == GL_NONE ||
234 : internalFormat == pixelData.internalFormat);
235 84 : LBASSERT(externalFormat == GL_NONE ||
236 : externalFormat == pixelData.externalFormat);
237 84 : LBASSERT(pixelSize == GL_NONE || pixelSize == pixelData.pixelSize);
238 84 : internalFormat = pixelData.internalFormat;
239 84 : pixelSize = pixelData.pixelSize;
240 84 : externalFormat = pixelData.externalFormat;
241 84 : }
242 :
243 9 : bool _collectOutputData(const ImageOps& ops, PixelViewport& destPVP,
244 : uint32_t& colorInt, uint32_t& colorPixelSize,
245 : uint32_t& colorExt, uint32_t& depthInt,
246 : uint32_t& depthPixelSize, uint32_t& depthExt)
247 : {
248 72 : for (const ImageOp& op : ops)
249 : {
250 63 : const RenderContext& context = op.image->getContext();
251 252 : if (context.pixel != Pixel::ALL || context.subPixel != SubPixel::ALL ||
252 189 : op.zoom != Zoom::NONE ||
253 63 : op.image->getStorageType() != Frame::TYPE_MEMORY)
254 : {
255 0 : return false;
256 : }
257 :
258 63 : if (!op.image->hasPixelData(Frame::Buffer::color))
259 0 : continue;
260 :
261 63 : destPVP.merge(op.image->getPixelViewport() + op.offset);
262 :
263 63 : _collectOutputData(op.image->getPixelData(Frame::Buffer::color),
264 63 : colorInt, colorPixelSize, colorExt);
265 :
266 63 : if (op.image->hasPixelData(Frame::Buffer::depth))
267 : {
268 21 : _collectOutputData(op.image->getPixelData(Frame::Buffer::depth),
269 21 : depthInt, depthPixelSize, depthExt);
270 : }
271 : }
272 :
273 9 : if (!destPVP.hasArea())
274 0 : LBWARN << "Nothing to assemble: " << destPVP << std::endl;
275 9 : return destPVP.hasArea();
276 : }
277 :
278 21 : void _mergeDBImage(void* destColor, void* destDepth,
279 : const PixelViewport& destPVP, const Image* image,
280 : const Vector2i& offset)
281 : {
282 21 : LBASSERT(destColor && destDepth);
283 :
284 21 : LBVERB << "CPU-DB assembly" << std::endl;
285 :
286 21 : uint32_t* destC = reinterpret_cast<uint32_t*>(destColor);
287 21 : uint32_t* destD = reinterpret_cast<uint32_t*>(destDepth);
288 :
289 21 : const PixelViewport& pvp = image->getPixelViewport();
290 :
291 21 : const int32_t destX = offset.x() + pvp.x - destPVP.x;
292 21 : const int32_t destY = offset.y() + pvp.y - destPVP.y;
293 :
294 : const uint32_t* color = reinterpret_cast<const uint32_t*>(
295 21 : image->getPixelPointer(Frame::Buffer::color));
296 : const uint32_t* depth = reinterpret_cast<const uint32_t*>(
297 21 : image->getPixelPointer(Frame::Buffer::depth));
298 :
299 131 : #pragma omp parallel for
300 110 : for (int32_t y = 0; y < pvp.h; ++y)
301 : {
302 21333 : const uint32_t skip = (destY + y) * destPVP.w + destX;
303 21333 : uint32_t* destColorIt = destC + skip;
304 21333 : uint32_t* destDepthIt = destD + skip;
305 21333 : const uint32_t* colorIt = color + y * pvp.w;
306 21333 : const uint32_t* depthIt = depth + y * pvp.w;
307 :
308 7164817 : for (int32_t x = 0; x < pvp.w; ++x)
309 : {
310 7143484 : if (*destDepthIt > *depthIt)
311 : {
312 1751206 : *destColorIt = *colorIt;
313 1751206 : *destDepthIt = *depthIt;
314 : }
315 :
316 7143484 : ++destColorIt;
317 7143484 : ++destDepthIt;
318 7143484 : ++colorIt;
319 7143484 : ++depthIt;
320 : }
321 : }
322 21 : }
323 :
324 21 : void _merge2DImage(void* destColor, void* destDepth,
325 : const eq::PixelViewport& destPVP, const Image* image,
326 : const Vector2i& offset)
327 : {
328 : // This is mostly copy&paste code from _mergeDBImage :-/
329 21 : LBVERB << "CPU-2D assembly" << std::endl;
330 :
331 21 : uint8_t* destC = reinterpret_cast<uint8_t*>(destColor);
332 21 : uint8_t* destD = reinterpret_cast<uint8_t*>(destDepth);
333 :
334 21 : const PixelViewport& pvp = image->getPixelViewport();
335 21 : const int32_t destX = offset.x() + pvp.x - destPVP.x;
336 21 : const int32_t destY = offset.y() + pvp.y - destPVP.y;
337 :
338 21 : LBASSERT(image->hasPixelData(Frame::Buffer::color));
339 :
340 21 : const uint8_t* color = image->getPixelPointer(Frame::Buffer::color);
341 21 : const size_t pixelSize = image->getPixelSize(Frame::Buffer::color);
342 21 : const size_t rowLength = pvp.w * pixelSize;
343 :
344 127 : #pragma omp parallel for
345 106 : for (int32_t y = 0; y < pvp.h; ++y)
346 : {
347 16564 : const size_t skip = ((destY + y) * destPVP.w + destX) * pixelSize;
348 16564 : memcpy(destC + skip, color + y * pvp.w * pixelSize, rowLength);
349 : // clear depth, for depth-assembly into existing FB
350 16564 : if (destD)
351 0 : lunchbox::setZero(destD + skip, rowLength);
352 : }
353 21 : }
354 :
355 21 : void _blendImage(void* dest, const eq::PixelViewport& destPVP,
356 : const Image* image, const Vector2i& offset)
357 : {
358 21 : LBVERB << "CPU-Blend assembly" << std::endl;
359 :
360 21 : int32_t* destColor = reinterpret_cast<int32_t*>(dest);
361 :
362 21 : const PixelViewport& pvp = image->getPixelViewport();
363 21 : const int32_t destX = offset.x() + pvp.x - destPVP.x;
364 21 : const int32_t destY = offset.y() + pvp.y - destPVP.y;
365 :
366 21 : LBASSERT(image->getPixelSize(Frame::Buffer::color) == 4);
367 21 : LBASSERT(image->hasPixelData(Frame::Buffer::color));
368 21 : LBASSERT(image->hasAlpha());
369 :
370 : const int32_t* color = reinterpret_cast<const int32_t*>(
371 21 : image->getPixelPointer(Frame::Buffer::color));
372 :
373 : // Blending of two slices, none of which is on final image (i.e. result
374 : // could be blended on to something else) should be performed with:
375 : // glBlendFuncSeparate( GL_ONE, GL_SRC_ALPHA, GL_ZERO, GL_SRC_ALPHA )
376 : // which means:
377 : // dstColor = 1*srcColor + srcAlpha*dstColor
378 : // dstAlpha = 0*srcAlpha + srcAlpha*dstAlpha
379 : // because we accumulate light which is go through (= 1-Alpha) and we
380 : // already have colors as Alpha*Color
381 :
382 21 : int32_t* destColorStart = destColor + destY * destPVP.w + destX;
383 21 : const uint32_t step = sizeof(int32_t);
384 :
385 130 : #pragma omp parallel for
386 109 : for (int32_t y = 0; y < pvp.h; ++y)
387 : {
388 : const unsigned char* src =
389 25109 : reinterpret_cast<const uint8_t*>(color + pvp.w * y);
390 : unsigned char* dst =
391 25109 : reinterpret_cast<uint8_t*>(destColorStart + destPVP.w * y);
392 :
393 11371317 : for (int32_t x = 0; x < pvp.w; ++x)
394 : {
395 11346208 : dst[0] = LB_MIN(src[0] + (src[3] * dst[0] >> 8), 255);
396 11346208 : dst[1] = LB_MIN(src[1] + (src[3] * dst[1] >> 8), 255);
397 11346208 : dst[2] = LB_MIN(src[2] + (src[3] * dst[2] >> 8), 255);
398 11346208 : dst[3] = src[3] * dst[3] >> 8;
399 :
400 11346208 : src += step;
401 11346208 : dst += step;
402 : }
403 : }
404 21 : }
405 :
406 9 : void _mergeImages(const ImageOps& ops, const bool blend, void* colorBuffer,
407 : void* depthBuffer, const PixelViewport& destPVP)
408 : {
409 72 : for (const ImageOp& op : ops)
410 : {
411 63 : if (!op.image->hasPixelData(Frame::Buffer::color))
412 0 : continue;
413 :
414 63 : if (op.image->hasPixelData(Frame::Buffer::depth))
415 21 : _mergeDBImage(colorBuffer, depthBuffer, destPVP, op.image,
416 21 : op.offset);
417 42 : else if (blend && op.image->hasAlpha())
418 21 : _blendImage(colorBuffer, destPVP, op.image, op.offset);
419 : else
420 21 : _merge2DImage(colorBuffer, depthBuffer, destPVP, op.image,
421 21 : op.offset);
422 : }
423 9 : }
424 :
425 0 : Vector4f _getCoords(const ImageOp& op, const PixelViewport& pvp)
426 : {
427 0 : const Pixel& pixel = op.image->getContext().pixel;
428 0 : return Vector4f(op.offset.x() + pvp.x * pixel.w + pixel.x,
429 0 : op.offset.x() + pvp.getXEnd() * pixel.w * op.zoom.x() +
430 0 : pixel.x,
431 0 : op.offset.y() + pvp.y * pixel.h + pixel.y,
432 0 : op.offset.y() + pvp.getYEnd() * pixel.h * op.zoom.y() +
433 0 : pixel.y);
434 : }
435 :
436 1 : bool _setupDrawPixels(const ImageOp& op, const Frame::Buffer which,
437 : Channel* channel)
438 : {
439 1 : const PixelViewport& pvp = op.image->getPixelViewport();
440 1 : const util::Texture* texture = 0;
441 1 : if (op.image->getStorageType() == Frame::TYPE_MEMORY)
442 : {
443 1 : LBASSERT(op.image->hasPixelData(which));
444 1 : util::ObjectManager& objects = channel->getObjectManager();
445 :
446 : const bool coreProfile =
447 1 : channel->getWindow()->getIAttribute(
448 1 : WindowSettings::IATTR_HINT_CORE_PROFILE) == ON;
449 1 : if (op.zoom == Zoom::NONE && !coreProfile)
450 : {
451 1 : op.image->upload(which, 0, op.offset, objects);
452 1 : return false;
453 : }
454 : util::Texture* ncTexture =
455 0 : objects.obtainEqTexture(which == Frame::Buffer::color ? colorDBKey
456 : : depthDBKey,
457 0 : GL_TEXTURE_RECTANGLE_ARB);
458 0 : texture = ncTexture;
459 :
460 0 : const Vector2i offset(-pvp.x, -pvp.y); // will be applied with quad
461 0 : op.image->upload(which, ncTexture, offset, objects);
462 : }
463 : else // texture image
464 : {
465 0 : LBASSERT(op.image->hasTextureData(which));
466 0 : texture = &op.image->getTexture(which);
467 : }
468 :
469 0 : EQ_GL_CALL(glActiveTexture(GL_TEXTURE0));
470 0 : texture->bind();
471 0 : texture->applyZoomFilter(op.zoomFilter);
472 0 : texture->applyWrap();
473 :
474 0 : if (which == Frame::Buffer::color)
475 0 : EQ_GL_CALL(glDepthMask(false))
476 : else
477 : {
478 0 : LBASSERT(which == Frame::Buffer::depth)
479 0 : EQ_GL_CALL(glColorMask(false, false, false, false));
480 : }
481 0 : return true;
482 : }
483 :
484 0 : void _drawPixelsFF(const ImageOp& op, const Frame::Buffer which,
485 : Channel* channel)
486 : {
487 0 : const PixelViewport& pvp = op.image->getPixelViewport();
488 0 : LBLOG(LOG_ASSEMBLY) << "_drawPixelsFF " << pvp << " offset " << op.offset
489 0 : << std::endl;
490 :
491 0 : if (!_setupDrawPixels(op, which, channel))
492 0 : return;
493 :
494 0 : const Vector4f& coords = _getCoords(op, pvp);
495 :
496 0 : EQ_GL_CALL(glDisable(GL_LIGHTING));
497 0 : EQ_GL_CALL(glEnable(GL_TEXTURE_RECTANGLE_ARB));
498 :
499 0 : EQ_GL_CALL(glColor3f(1.0f, 1.0f, 1.0f));
500 :
501 0 : glBegin(GL_QUADS);
502 0 : glTexCoord2f(0.0f, 0.0f);
503 0 : glVertex3f(coords[0], coords[2], 0.0f);
504 :
505 0 : glTexCoord2f(float(pvp.w), 0.0f);
506 0 : glVertex3f(coords[1], coords[2], 0.0f);
507 :
508 0 : glTexCoord2f(float(pvp.w), float(pvp.h));
509 0 : glVertex3f(coords[1], coords[3], 0.0f);
510 :
511 0 : glTexCoord2f(0.0f, float(pvp.h));
512 0 : glVertex3f(coords[0], coords[3], 0.0f);
513 0 : glEnd();
514 :
515 : // restore state
516 0 : EQ_GL_CALL(glDisable(GL_TEXTURE_RECTANGLE_ARB));
517 :
518 0 : if (which == Frame::Buffer::color)
519 0 : EQ_GL_CALL(glDepthMask(true))
520 : else
521 : {
522 0 : const ColorMask& colorMask = channel->getDrawBufferMask();
523 0 : EQ_GL_CALL(
524 : glColorMask(colorMask.red, colorMask.green, colorMask.blue, true));
525 : }
526 : }
527 :
528 : template <typename T>
529 0 : void _drawTexturedQuad(const T* key, const ImageOp& op,
530 : const PixelViewport& pvp, const bool withDepth,
531 : Channel* channel)
532 : {
533 0 : util::ObjectManager& om = channel->getObjectManager();
534 0 : GLuint program = om.getProgram(key);
535 0 : GLuint vertexArray = om.getVertexArray(key);
536 0 : GLuint vertexBuffer = om.getBuffer(key);
537 0 : GLuint uvBuffer = om.getBuffer(key + 1);
538 0 : if (program == util::ObjectManager::INVALID)
539 : {
540 0 : vertexBuffer = om.newBuffer(key);
541 0 : uvBuffer = om.newBuffer(key + 1);
542 0 : vertexArray = om.newVertexArray(key);
543 0 : program = om.newProgram(key);
544 :
545 : const char* vertexShaderGLSL = {
546 : "#version 330 core\n"
547 : "layout(location = 0) in vec3 vert;\n"
548 : "layout(location = 1) in vec2 vertTexCoord;\n"
549 : "uniform mat4 proj;\n"
550 : "out vec2 fragTexCoord;\n"
551 : "void main() {\n"
552 : " fragTexCoord = vertTexCoord;\n"
553 : " gl_Position = proj * vec4(vert, 1);\n"
554 0 : "}\n"};
555 :
556 : const char* fragmentShaderGLSL = {
557 : withDepth
558 : ? "#version 330 core\n"
559 : "#extension GL_ARB_texture_rectangle : enable\n"
560 : "uniform sampler2DRect color;\n"
561 : "uniform sampler2DRect depth;\n"
562 : "in vec2 fragTexCoord;\n"
563 : "out vec4 finalColor;\n"
564 : "void main() {\n"
565 : " finalColor = texture2DRect(color, fragTexCoord);\n"
566 : " gl_FragDepth = texture2DRect(depth, fragTexCoord).x;\n"
567 : "}\n"
568 : : // no depth
569 : "#version 330 core\n"
570 : "#extension GL_ARB_texture_rectangle : enable\n"
571 : "uniform sampler2DRect color;\n"
572 : "in vec2 fragTexCoord;\n"
573 : "out vec4 finalColor;\n"
574 : "void main() {\n"
575 : " finalColor = texture2DRect(color, fragTexCoord);\n"
576 0 : "}\n"};
577 :
578 0 : LBCHECK(util::shader::linkProgram(glewGetContext(), program,
579 : vertexShaderGLSL,
580 : fragmentShaderGLSL));
581 :
582 0 : EQ_GL_CALL(glUseProgram(program));
583 :
584 0 : GLint colorParam = glGetUniformLocation(program, "color");
585 0 : EQ_GL_CALL(glUniform1i(colorParam, 0));
586 0 : if (withDepth)
587 : {
588 0 : const GLint depthParam = glGetUniformLocation(program, "depth");
589 0 : EQ_GL_CALL(glUniform1i(depthParam, 1));
590 : }
591 : }
592 :
593 0 : const Vector4f& coords = _getCoords(op, pvp);
594 : const GLfloat vertices[] = {coords[0], coords[2], 0.0f, coords[1],
595 : coords[2], 0.0f, coords[0], coords[3],
596 0 : 0.0f, coords[1], coords[3], 0.0f};
597 :
598 0 : const GLfloat uvs[] = {0.0f, 0.0f, float(pvp.w), 0.0f,
599 0 : 0.0f, float(pvp.h), float(pvp.w), float(pvp.h)};
600 : const eq::Matrix4f& proj =
601 0 : eq::Frustumf(channel->getPixelViewport().x,
602 0 : channel->getPixelViewport().getXEnd(),
603 0 : channel->getPixelViewport().y,
604 0 : channel->getPixelViewport().getYEnd(), -1.0f, 1.0f)
605 0 : .computeOrthoMatrix();
606 0 : if (withDepth)
607 0 : EQ_GL_CALL(glEnable(GL_DEPTH_TEST));
608 :
609 0 : EQ_GL_CALL(glBindVertexArray(vertexArray));
610 0 : EQ_GL_CALL(glUseProgram(program));
611 :
612 0 : const GLuint projection = glGetUniformLocation(program, "proj");
613 0 : EQ_GL_CALL(glUniformMatrix4fv(projection, 1, GL_FALSE, proj.data()));
614 :
615 0 : EQ_GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer));
616 0 : EQ_GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
617 : GL_DYNAMIC_DRAW));
618 0 : EQ_GL_CALL(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0));
619 :
620 0 : EQ_GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, uvBuffer));
621 0 : EQ_GL_CALL(
622 : glBufferData(GL_ARRAY_BUFFER, sizeof(uvs), uvs, GL_DYNAMIC_DRAW));
623 0 : EQ_GL_CALL(glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0));
624 0 : EQ_GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, 0));
625 :
626 0 : EQ_GL_CALL(glEnableVertexAttribArray(0));
627 0 : EQ_GL_CALL(glEnableVertexAttribArray(1));
628 0 : EQ_GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
629 0 : EQ_GL_CALL(glDisableVertexAttribArray(0));
630 0 : EQ_GL_CALL(glDisableVertexAttribArray(1));
631 :
632 0 : EQ_GL_CALL(glBindVertexArray(0));
633 0 : EQ_GL_CALL(glUseProgram(0));
634 :
635 0 : if (withDepth)
636 0 : EQ_GL_CALL(glDisable(GL_DEPTH_TEST));
637 0 : }
638 :
639 1 : void _drawPixelsGLSL(const ImageOp& op, const Frame::Buffer which,
640 : Channel* channel)
641 : {
642 1 : const PixelViewport& pvp = op.image->getPixelViewport();
643 1 : LBLOG(LOG_ASSEMBLY) << "_drawPixelsGLSL " << pvp << " offset " << op.offset
644 1 : << std::endl;
645 :
646 1 : if (!_setupDrawPixels(op, which, channel))
647 1 : return;
648 :
649 0 : _drawTexturedQuad(channel, op, pvp, false, channel);
650 :
651 : // restore state
652 0 : if (which == Frame::Buffer::color)
653 0 : EQ_GL_CALL(glDepthMask(true))
654 : else
655 : {
656 0 : const ColorMask& colorMask = channel->getDrawBufferMask();
657 0 : EQ_GL_CALL(
658 : glColorMask(colorMask.red, colorMask.green, colorMask.blue, true));
659 : }
660 : }
661 :
662 0 : util::Accum* _obtainAccum(Channel* channel)
663 : {
664 0 : const PixelViewport& pvp = channel->getPixelViewport();
665 :
666 0 : LBASSERT(pvp.isValid());
667 :
668 0 : util::ObjectManager& objects = channel->getObjectManager();
669 0 : util::Accum* accum = objects.getEqAccum(channel);
670 0 : if (!accum)
671 : {
672 0 : accum = objects.newEqAccum(channel);
673 0 : if (!accum->init(pvp, channel->getWindow()->getColorFormat()))
674 : {
675 0 : LBERROR << "Accumulation initialization failed." << std::endl;
676 : }
677 : }
678 : else
679 0 : accum->resize(pvp.w, pvp.h);
680 :
681 0 : accum->clear();
682 0 : return accum;
683 : }
684 : }
685 :
686 1 : uint32_t Compositor::assembleFrames(const Frames& frames, Channel* channel,
687 : util::Accum* accum)
688 : {
689 1 : if (frames.empty())
690 0 : return 0;
691 :
692 1 : if (_useCPUAssembly(frames, channel))
693 0 : return assembleFramesCPU(frames, channel);
694 :
695 : // else
696 1 : return assembleFramesUnsorted(frames, channel, accum);
697 : }
698 :
699 0 : uint32_t Compositor::blendImages(const ImageOps& ops, Channel* channel,
700 : util::Accum* accum)
701 : {
702 0 : if (ops.empty())
703 0 : return 0;
704 :
705 0 : if (isSubPixelDecomposition(ops))
706 : {
707 : const bool coreProfile =
708 0 : channel->getWindow()->getIAttribute(
709 0 : WindowSettings::IATTR_HINT_CORE_PROFILE) == ON;
710 0 : if (coreProfile)
711 : {
712 0 : LBERROR << "No support for sub pixel assembly for OpenGL core"
713 0 : "profile, skipping assemble"
714 0 : << std::endl;
715 0 : return 0;
716 : }
717 :
718 0 : if (!accum)
719 : {
720 0 : accum = _obtainAccum(channel);
721 0 : accum->clear();
722 :
723 0 : const SubPixel& subPixel = ops.front().image->getContext().subPixel;
724 0 : accum->setTotalSteps(subPixel.size);
725 : }
726 :
727 0 : uint32_t count = 0;
728 0 : ImageOps opsLeft = ops;
729 0 : while (!opsLeft.empty())
730 : {
731 0 : ImageOps current = extractOneSubPixel(opsLeft);
732 0 : const uint32_t subCount = blendImages(current, channel, accum);
733 0 : LBASSERT(subCount < 2);
734 :
735 0 : if (subCount > 0)
736 0 : accum->accum();
737 0 : count += subCount;
738 : }
739 0 : if (count > 0)
740 0 : accum->display();
741 0 : return count;
742 : }
743 :
744 0 : uint32_t count = 1;
745 0 : if (_useCPUAssembly(ops, true))
746 0 : count = assembleImagesCPU(ops, channel, true);
747 : else
748 0 : for (const ImageOp& op : ops)
749 0 : assembleImage(op, channel);
750 0 : return count;
751 : }
752 :
753 0 : uint32_t Compositor::blendFrames(const Frames& frames, Channel* channel,
754 : util::Accum* accum)
755 : {
756 0 : ImageOps ops;
757 0 : for (const Frame* frame : frames)
758 : {
759 : {
760 0 : const uint32_t timeout = channel->getConfig()->getTimeout();
761 : ChannelStatistics event(Statistic::CHANNEL_FRAME_WAIT_READY,
762 0 : channel);
763 0 : frame->waitReady(timeout);
764 : }
765 :
766 0 : for (const Image* image : frame->getImages())
767 0 : ops.push_back(ImageOp(frame, image));
768 : }
769 :
770 0 : return blendImages(ops, channel, accum);
771 : }
772 :
773 1 : bool Compositor::isSubPixelDecomposition(const Frames& frames)
774 : {
775 1 : if (frames.empty())
776 0 : return false;
777 :
778 : const SubPixel& subpixel =
779 1 : frames.front()->getFrameData()->getContext().subPixel;
780 2 : for (const Frame* frame : frames)
781 1 : if (subpixel != frame->getFrameData()->getContext().subPixel)
782 0 : return true;
783 1 : return false;
784 : }
785 :
786 0 : bool Compositor::isSubPixelDecomposition(const ImageOps& ops)
787 : {
788 0 : if (ops.empty())
789 0 : return false;
790 :
791 0 : const SubPixel& subPixel = ops.front().image->getContext().subPixel;
792 0 : for (const ImageOp& op : ops)
793 0 : if (op.image->getContext().subPixel != subPixel)
794 0 : return true;
795 0 : return false;
796 : }
797 :
798 0 : Frames Compositor::extractOneSubPixel(Frames& frames)
799 : {
800 0 : Frames current;
801 :
802 : const SubPixel& subPixel =
803 0 : frames.back()->getFrameData()->getContext().subPixel;
804 0 : current.push_back(frames.back());
805 0 : frames.pop_back();
806 :
807 0 : for (Frames::iterator i = frames.begin(); i != frames.end();)
808 : {
809 0 : Frame* frame = *i;
810 :
811 0 : if (frame->getFrameData()->getContext().subPixel == subPixel)
812 : {
813 0 : current.push_back(frame);
814 0 : i = frames.erase(i);
815 : }
816 : else
817 0 : ++i;
818 : }
819 :
820 0 : return current;
821 : }
822 :
823 0 : ImageOps Compositor::extractOneSubPixel(ImageOps& ops)
824 : {
825 0 : ImageOps current;
826 :
827 0 : const SubPixel& subPixel = ops.back().image->getContext().subPixel;
828 0 : current.push_back(ops.back());
829 0 : ops.pop_back();
830 :
831 0 : for (ImageOps::iterator i = ops.begin(); i != ops.end();)
832 : {
833 0 : ImageOp& op = *i;
834 :
835 0 : if (op.image->getContext().subPixel == subPixel)
836 : {
837 0 : current.push_back(op);
838 0 : i = ops.erase(i);
839 : }
840 : else
841 0 : ++i;
842 : }
843 :
844 0 : return current;
845 : }
846 :
847 1 : uint32_t Compositor::assembleFramesUnsorted(const Frames& frames,
848 : Channel* channel,
849 : util::Accum* accum)
850 : {
851 1 : if (frames.empty())
852 0 : return 0;
853 :
854 1 : LBVERB << "Unsorted GPU assembly" << std::endl;
855 1 : if (isSubPixelDecomposition(frames))
856 : {
857 : const bool coreProfile =
858 0 : channel->getWindow()->getIAttribute(
859 0 : WindowSettings::IATTR_HINT_CORE_PROFILE) == ON;
860 0 : if (coreProfile)
861 : {
862 0 : LBERROR << "No support for sub pixel assembly for OpenGL core"
863 0 : "profile, skipping assemble"
864 0 : << std::endl;
865 0 : return 0;
866 : }
867 :
868 0 : uint32_t count = 0;
869 :
870 0 : if (!accum)
871 : {
872 0 : accum = _obtainAccum(channel);
873 0 : accum->clear();
874 :
875 : const SubPixel& subPixel =
876 0 : frames.back()->getFrameData()->getContext().subPixel;
877 0 : accum->setTotalSteps(subPixel.size);
878 : }
879 :
880 0 : Frames framesLeft = frames;
881 0 : while (!framesLeft.empty())
882 : {
883 : // get the frames with the same subpixel compound
884 0 : Frames current = extractOneSubPixel(framesLeft);
885 :
886 : // use assembleFrames to potentially benefit from CPU assembly
887 0 : const uint32_t subCount = assembleFrames(current, channel, accum);
888 0 : LBASSERT(subCount < 2)
889 0 : if (subCount > 0)
890 0 : accum->accum();
891 0 : count += subCount;
892 : }
893 0 : if (count > 1)
894 0 : accum->display();
895 0 : return count;
896 : }
897 :
898 : // This is an optimized assembly version. The frames are not assembled in
899 : // the saved order, but in the order they become available, which is faster
900 : // because less time is spent waiting on frame availability.
901 : //
902 : // The ready frames are counted in a monitor. Whenever a frame becomes
903 : // available, it increments the monitor which causes this code to wake up
904 : // and assemble it.
905 :
906 1 : uint32_t count = 0;
907 :
908 : // wait and assemble frames
909 1 : WaitHandle* handle = startWaitFrames(frames, channel);
910 2 : for (Frame* frame = waitFrame(handle); frame; frame = waitFrame(handle))
911 : {
912 1 : if (frame->getImages().empty())
913 0 : continue;
914 :
915 1 : count = 1;
916 1 : assembleFrame(frame, channel);
917 : }
918 :
919 1 : return count;
920 : }
921 :
922 : class Compositor::WaitHandle
923 : {
924 : public:
925 1 : WaitHandle(const Frames& frames, Channel* ch)
926 1 : : left(frames)
927 : , channel(ch)
928 1 : , processed(0)
929 : {
930 1 : }
931 1 : ~WaitHandle()
932 1 : {
933 : // de-register the monitor on eventual left-overs on error/exception
934 1 : for (FramesCIter i = left.begin(); i != left.end(); ++i)
935 0 : (*i)->removeListener(monitor);
936 1 : left.clear();
937 1 : }
938 :
939 : lunchbox::Monitor<uint32_t> monitor;
940 : Frames left;
941 : Channel* const channel;
942 : uint32_t processed;
943 : };
944 :
945 1 : Compositor::WaitHandle* Compositor::startWaitFrames(const Frames& frames,
946 : Channel* channel)
947 : {
948 1 : WaitHandle* handle = new WaitHandle(frames, channel);
949 2 : for (FramesCIter i = frames.begin(); i != frames.end(); ++i)
950 1 : (*i)->addListener(handle->monitor);
951 :
952 1 : return handle;
953 : }
954 :
955 2 : Frame* Compositor::waitFrame(WaitHandle* handle)
956 : {
957 2 : if (handle->left.empty())
958 : {
959 1 : delete handle;
960 1 : return 0;
961 : }
962 :
963 : ChannelStatistics event(Statistic::CHANNEL_FRAME_WAIT_READY,
964 2 : handle->channel);
965 1 : Config* config = handle->channel->getConfig();
966 1 : const uint32_t timeout = config->getTimeout();
967 :
968 1 : ++handle->processed;
969 1 : if (timeout == LB_TIMEOUT_INDEFINITE)
970 1 : handle->monitor.waitGE(handle->processed);
971 : else
972 : {
973 0 : const int64_t time = config->getTime() + timeout;
974 0 : const int64_t aliveTimeout = co::Global::getKeepaliveTimeout();
975 :
976 0 : while (!handle->monitor.timedWaitGE(handle->processed, aliveTimeout))
977 : {
978 : // pings timed out nodes
979 0 : const bool pinged = config->getLocalNode()->pingIdleNodes();
980 :
981 0 : if (config->getTime() >= time || !pinged)
982 : {
983 0 : delete handle;
984 0 : throw Exception(Exception::TIMEOUT_INPUTFRAME);
985 : }
986 : }
987 : }
988 :
989 1 : for (FramesIter i = handle->left.begin(); i != handle->left.end(); ++i)
990 : {
991 1 : Frame* frame = *i;
992 1 : if (!frame->isReady())
993 0 : continue;
994 :
995 1 : frame->removeListener(handle->monitor);
996 1 : handle->left.erase(i);
997 1 : return frame;
998 : }
999 :
1000 0 : LBASSERTINFO(false, "Unreachable code");
1001 0 : delete handle;
1002 0 : return 0;
1003 : }
1004 :
1005 0 : uint32_t Compositor::assembleFramesCPU(const Frames& frames, Channel* channel,
1006 : const bool blend)
1007 : {
1008 0 : if (frames.empty())
1009 0 : return 0;
1010 :
1011 : // Assembles images from DB and 2D compounds using the CPU and then
1012 : // assembles the result image. Does not support Pixel or Eye compounds.
1013 0 : LBVERB << "Sorted CPU assembly" << std::endl;
1014 :
1015 : const Image* result =
1016 0 : mergeFramesCPU(frames, blend, channel->getConfig()->getTimeout());
1017 0 : return _assembleCPUImage(result, channel);
1018 : }
1019 :
1020 0 : uint32_t Compositor::assembleImagesCPU(const ImageOps& images, Channel* channel,
1021 : const bool blend)
1022 : {
1023 0 : if (images.empty())
1024 0 : return 0;
1025 :
1026 : // Assembles images from DB and 2D compounds using the CPU and then
1027 : // assembles the result image. Does not support Pixel or Eye compounds.
1028 0 : LBVERB << "Sorted CPU assembly" << std::endl;
1029 :
1030 0 : const Image* result = mergeImagesCPU(images, blend);
1031 0 : return _assembleCPUImage(result, channel);
1032 : }
1033 :
1034 9 : const Image* Compositor::mergeFramesCPU(const Frames& frames, const bool blend,
1035 : const uint32_t timeout)
1036 : {
1037 18 : ImageOps ops;
1038 30 : for (const Frame* frame : frames)
1039 : {
1040 21 : frame->waitReady(timeout);
1041 84 : for (const Image* image : frame->getImages())
1042 : {
1043 63 : ImageOp op(frame, image);
1044 63 : op.offset = frame->getOffset();
1045 63 : ops.emplace_back(op);
1046 : }
1047 : }
1048 18 : return mergeImagesCPU(ops, blend);
1049 : }
1050 :
1051 9 : const Image* Compositor::mergeImagesCPU(const ImageOps& ops, const bool blend)
1052 : {
1053 9 : LBVERB << "Sorted CPU assembly" << std::endl;
1054 :
1055 : // Collect input image information and check preconditions
1056 9 : PixelViewport destPVP;
1057 9 : uint32_t colorInt = 0;
1058 9 : uint32_t colorExt = 0;
1059 9 : uint32_t colorPixelSize = 0;
1060 9 : uint32_t depthInt = 0;
1061 9 : uint32_t depthExt = 0;
1062 9 : uint32_t depthPixelSize = 0;
1063 :
1064 9 : if (!_collectOutputData(ops, destPVP, colorInt, colorPixelSize, colorExt,
1065 : depthInt, depthPixelSize, depthExt))
1066 : {
1067 0 : return 0;
1068 : }
1069 :
1070 : // prepare output image
1071 9 : if (!_resultImage)
1072 1 : _resultImage = new Image;
1073 9 : Image* result = _resultImage.get();
1074 :
1075 : // pre-condition check for current _merge implementations
1076 9 : LBASSERT(colorInt != 0);
1077 :
1078 9 : result->setPixelViewport(destPVP);
1079 :
1080 18 : PixelData colorPixels;
1081 9 : colorPixels.internalFormat = colorInt;
1082 9 : colorPixels.externalFormat = colorExt;
1083 9 : colorPixels.pixelSize = colorPixelSize;
1084 9 : colorPixels.pvp = destPVP;
1085 9 : result->setPixelData(Frame::Buffer::color, colorPixels);
1086 :
1087 9 : void* destDepth = 0;
1088 9 : if (depthInt != 0) // at least one depth assembly
1089 : {
1090 3 : LBASSERT(depthExt == EQ_COMPRESSOR_DATATYPE_DEPTH_UNSIGNED_INT);
1091 6 : PixelData depthPixels;
1092 3 : depthPixels.internalFormat = depthInt;
1093 3 : depthPixels.externalFormat = depthExt;
1094 3 : depthPixels.pixelSize = depthPixelSize;
1095 3 : depthPixels.pvp = destPVP;
1096 3 : result->setPixelData(Frame::Buffer::depth, depthPixels);
1097 3 : destDepth = result->getPixelPointer(Frame::Buffer::depth);
1098 : }
1099 :
1100 : // assembly
1101 9 : _mergeImages(ops, blend, result->getPixelPointer(Frame::Buffer::color),
1102 9 : destDepth, destPVP);
1103 9 : return result;
1104 : }
1105 :
1106 1 : void Compositor::assembleFrame(const Frame* frame, Channel* channel)
1107 : {
1108 1 : const Images& images = frame->getImages();
1109 1 : if (images.empty())
1110 0 : LBINFO << "No images to assemble" << std::endl;
1111 :
1112 2 : for (Image* image : images)
1113 : {
1114 1 : ImageOp op(frame, image);
1115 1 : op.offset = frame->getOffset();
1116 1 : assembleImage(op, channel);
1117 : }
1118 1 : }
1119 :
1120 1 : void Compositor::assembleImage(const ImageOp& op, Channel* channel)
1121 : {
1122 1 : const bool coreProfile = channel->getWindow()->getIAttribute(
1123 1 : WindowSettings::IATTR_HINT_CORE_PROFILE) == ON;
1124 1 : if (coreProfile && op.image->getContext().pixel != Pixel::ALL)
1125 : {
1126 0 : LBERROR << "No support for pixel assembly for OpenGL core profile,"
1127 0 : "skipping image"
1128 0 : << std::endl;
1129 0 : return;
1130 : }
1131 :
1132 1 : ImageOp operation = op;
1133 1 : operation.buffers = Frame::Buffer::none;
1134 :
1135 1 : const Frame::Buffer buffer[] = {Frame::Buffer::color, Frame::Buffer::depth};
1136 3 : for (unsigned i = 0; i < 2; ++i)
1137 : {
1138 2 : if ((op.buffers & buffer[i]) && (op.image->hasPixelData(buffer[i]) ||
1139 0 : op.image->hasTextureData(buffer[i])))
1140 : {
1141 1 : operation.buffers |= buffer[i];
1142 : }
1143 : }
1144 :
1145 1 : if (operation.buffers == Frame::Buffer::none)
1146 : {
1147 0 : LBWARN << "No image attachment buffers to assemble" << std::endl;
1148 0 : return;
1149 : }
1150 :
1151 1 : setupStencilBuffer(operation, channel);
1152 :
1153 1 : if (operation.buffers == Frame::Buffer::color)
1154 1 : assembleImage2D(operation, channel);
1155 0 : else if (operation.buffers == (Frame::Buffer::color | Frame::Buffer::depth))
1156 0 : assembleImageDB(operation, channel);
1157 : else
1158 0 : LBWARN << "Don't know how to assemble using buffers "
1159 0 : << operation.buffers << std::endl;
1160 :
1161 1 : clearStencilBuffer(operation);
1162 : }
1163 :
1164 1 : void Compositor::setupStencilBuffer(const ImageOp& op, const Channel* channel)
1165 : {
1166 1 : const Pixel& pixel = op.image->getContext().pixel;
1167 1 : if (pixel == Pixel::ALL)
1168 1 : return;
1169 :
1170 : // mark stencil buffer where pixel shall not pass
1171 : // TODO: OPT!
1172 0 : EQ_GL_CALL(glClear(GL_STENCIL_BUFFER_BIT));
1173 0 : EQ_GL_CALL(glEnable(GL_STENCIL_TEST));
1174 0 : EQ_GL_CALL(glEnable(GL_DEPTH_TEST));
1175 :
1176 0 : EQ_GL_CALL(glStencilFunc(GL_ALWAYS, 1, 1));
1177 0 : EQ_GL_CALL(glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE));
1178 :
1179 0 : EQ_GL_CALL(glLineWidth(1.0f));
1180 0 : EQ_GL_CALL(glDepthMask(false));
1181 0 : EQ_GL_CALL(glColorMask(false, false, false, false));
1182 :
1183 0 : const PixelViewport& pvp = op.image->getPixelViewport();
1184 :
1185 0 : EQ_GL_CALL(glPixelZoom(float(pixel.w), float(pixel.h)));
1186 :
1187 0 : if (pixel.w > 1)
1188 : {
1189 0 : const float width = float(pvp.w * pixel.w);
1190 0 : const float step = float(pixel.w);
1191 :
1192 : const float startX =
1193 0 : float(op.offset.x() + pvp.x) + 0.5f - float(pixel.w);
1194 0 : const float endX = startX + width + pixel.w + step;
1195 0 : const float startY = float(op.offset.y() + pvp.y + pixel.y);
1196 0 : const float endY = float(startY + pvp.h * pixel.h);
1197 :
1198 0 : glBegin(GL_QUADS);
1199 0 : for (float x = startX + pixel.x + 1.0f; x < endX; x += step)
1200 : {
1201 0 : glVertex3f(x - step, startY, 0.0f);
1202 0 : glVertex3f(x - 1.0f, startY, 0.0f);
1203 0 : glVertex3f(x - 1.0f, endY, 0.0f);
1204 0 : glVertex3f(x - step, endY, 0.0f);
1205 : }
1206 0 : glEnd();
1207 : }
1208 0 : if (pixel.h > 1)
1209 : {
1210 0 : const float height = float(pvp.h * pixel.h);
1211 0 : const float step = float(pixel.h);
1212 0 : const float startX = float(op.offset.x() + pvp.x + pixel.x);
1213 0 : const float endX = float(startX + pvp.w * pixel.w);
1214 : const float startY =
1215 0 : float(op.offset.y() + pvp.y) + 0.5f - float(pixel.h);
1216 0 : const float endY = startY + height + pixel.h + step;
1217 :
1218 0 : glBegin(GL_QUADS);
1219 0 : for (float y = startY + pixel.y; y < endY; y += step)
1220 : {
1221 0 : glVertex3f(startX, y - step, 0.0f);
1222 0 : glVertex3f(endX, y - step, 0.0f);
1223 0 : glVertex3f(endX, y - 1.0f, 0.0f);
1224 0 : glVertex3f(startX, y - 1.0f, 0.0f);
1225 : }
1226 0 : glEnd();
1227 : }
1228 :
1229 0 : EQ_GL_CALL(glDisable(GL_DEPTH_TEST));
1230 0 : EQ_GL_CALL(glStencilFunc(GL_EQUAL, 0, 1));
1231 0 : EQ_GL_CALL(glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
1232 :
1233 0 : const ColorMask& colorMask = channel->getDrawBufferMask();
1234 0 : EQ_GL_CALL(
1235 : glColorMask(colorMask.red, colorMask.green, colorMask.blue, true));
1236 0 : EQ_GL_CALL(glDepthMask(true));
1237 : }
1238 :
1239 1 : void Compositor::clearStencilBuffer(const ImageOp& op)
1240 : {
1241 1 : if (op.image->getContext().pixel == Pixel::ALL)
1242 1 : return;
1243 :
1244 0 : EQ_GL_CALL(glPixelZoom(1.f, 1.f));
1245 0 : EQ_GL_CALL(glDisable(GL_STENCIL_TEST));
1246 : }
1247 :
1248 1 : void Compositor::assembleImage2D(const ImageOp& op, Channel* channel)
1249 : {
1250 1 : if (GLEW_VERSION_3_3)
1251 1 : _drawPixelsGLSL(op, Frame::Buffer::color, channel);
1252 : else
1253 0 : _drawPixelsFF(op, Frame::Buffer::color, channel);
1254 1 : declareRegion(op, channel);
1255 : #if 0
1256 : static lunchbox::a_int32_t counter;
1257 : std::ostringstream stringstream;
1258 : stringstream << "Image_" << ++counter;
1259 : op.image->writeImages( stringstream.str( ));
1260 : #endif
1261 1 : }
1262 :
1263 0 : void Compositor::assembleImageDB(const ImageOp& op, Channel* channel)
1264 : {
1265 0 : if (GLEW_VERSION_3_3)
1266 0 : assembleImageDB_GLSL(op, channel);
1267 : else
1268 0 : assembleImageDB_FF(op, channel);
1269 0 : }
1270 :
1271 0 : void Compositor::assembleImageDB_FF(const ImageOp& op, Channel* channel)
1272 : {
1273 0 : const PixelViewport& pvp = op.image->getPixelViewport();
1274 0 : LBLOG(LOG_ASSEMBLY) << "assembleImageDB_FF " << pvp << std::endl;
1275 :
1276 : // Z-Based sort-last assembly
1277 0 : EQ_GL_CALL(glRasterPos2i(op.offset.x() + pvp.x, op.offset.y() + pvp.y));
1278 0 : EQ_GL_CALL(glEnable(GL_STENCIL_TEST));
1279 :
1280 : // test who is in front and mark in stencil buffer
1281 0 : EQ_GL_CALL(glEnable(GL_DEPTH_TEST));
1282 :
1283 0 : const bool pixelComposite = (op.image->getContext().pixel != Pixel::ALL);
1284 0 : if (pixelComposite)
1285 : { // keep already marked stencil values
1286 0 : EQ_GL_CALL(glStencilFunc(GL_EQUAL, 1, 1));
1287 0 : EQ_GL_CALL(glStencilOp(GL_KEEP, GL_ZERO, GL_REPLACE));
1288 : }
1289 : else
1290 : {
1291 0 : EQ_GL_CALL(glStencilFunc(GL_ALWAYS, 1, 1));
1292 0 : EQ_GL_CALL(glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE));
1293 : }
1294 :
1295 0 : _drawPixelsFF(op, Frame::Buffer::depth, channel);
1296 :
1297 0 : EQ_GL_CALL(glDisable(GL_DEPTH_TEST));
1298 :
1299 : // draw front-most, visible pixels using stencil mask
1300 0 : EQ_GL_CALL(glStencilFunc(GL_EQUAL, 1, 1));
1301 0 : EQ_GL_CALL(glStencilOp(GL_KEEP, GL_ZERO, GL_ZERO));
1302 :
1303 0 : _drawPixelsFF(op, Frame::Buffer::color, channel);
1304 :
1305 0 : EQ_GL_CALL(glDisable(GL_STENCIL_TEST));
1306 0 : declareRegion(op, channel);
1307 0 : }
1308 :
1309 0 : void Compositor::assembleImageDB_GLSL(const ImageOp& op, Channel* channel)
1310 : {
1311 0 : const PixelViewport& pvp = op.image->getPixelViewport();
1312 0 : LBLOG(LOG_ASSEMBLY) << "assembleImageDB_GLSL " << pvp << std::endl;
1313 :
1314 0 : util::ObjectManager& om = channel->getObjectManager();
1315 : const bool useImageTexture =
1316 0 : op.image->getStorageType() == Frame::TYPE_TEXTURE;
1317 :
1318 0 : const util::Texture* textureColor = 0;
1319 0 : const util::Texture* textureDepth = 0;
1320 0 : if (useImageTexture)
1321 : {
1322 0 : textureColor = &op.image->getTexture(Frame::Buffer::color);
1323 0 : textureDepth = &op.image->getTexture(Frame::Buffer::depth);
1324 : }
1325 : else
1326 : {
1327 : util::Texture* ncTextureColor =
1328 0 : om.obtainEqTexture(colorDBKey, GL_TEXTURE_RECTANGLE_ARB);
1329 : util::Texture* ncTextureDepth =
1330 0 : om.obtainEqTexture(depthDBKey, GL_TEXTURE_RECTANGLE_ARB);
1331 0 : const Vector2i offset(-pvp.x, -pvp.y); // will be applied with quad
1332 :
1333 0 : op.image->upload(Frame::Buffer::color, ncTextureColor, offset, om);
1334 0 : op.image->upload(Frame::Buffer::depth, ncTextureDepth, offset, om);
1335 :
1336 0 : textureColor = ncTextureColor;
1337 0 : textureDepth = ncTextureDepth;
1338 : }
1339 :
1340 0 : EQ_GL_CALL(glActiveTexture(GL_TEXTURE1));
1341 0 : textureDepth->bind();
1342 0 : textureDepth->applyZoomFilter(op.zoomFilter);
1343 :
1344 0 : EQ_GL_CALL(glActiveTexture(GL_TEXTURE0));
1345 0 : textureColor->bind();
1346 0 : textureColor->applyZoomFilter(op.zoomFilter);
1347 :
1348 0 : _drawTexturedQuad(shaderDBKey, op, pvp, true, channel);
1349 :
1350 0 : declareRegion(op, channel);
1351 0 : }
1352 :
1353 1 : void Compositor::declareRegion(const ImageOp& op, Channel* channel)
1354 : {
1355 1 : if (!channel)
1356 0 : return;
1357 :
1358 1 : const eq::PixelViewport area = op.image->getPixelViewport() + op.offset;
1359 1 : channel->declareRegion(area);
1360 : }
1361 :
1362 : #undef glewGetContext
1363 : #define glewGetContext() glCtx
1364 :
1365 2 : void Compositor::setupAssemblyState(const PixelViewport& pvp,
1366 : const GLEWContext* glCtx)
1367 : {
1368 2 : EQ_GL_ERROR("before setupAssemblyState");
1369 : glPushAttrib(GL_ENABLE_BIT | GL_STENCIL_BUFFER_BIT | GL_LINE_BIT |
1370 2 : GL_PIXEL_MODE_BIT | GL_POLYGON_BIT | GL_TEXTURE_BIT);
1371 :
1372 2 : glDisable(GL_DEPTH_TEST);
1373 2 : glDisable(GL_BLEND);
1374 2 : glDisable(GL_ALPHA_TEST);
1375 2 : glDisable(GL_STENCIL_TEST);
1376 2 : glDisable(GL_TEXTURE_1D);
1377 2 : glDisable(GL_TEXTURE_2D);
1378 2 : if (GLEW_VERSION_1_2)
1379 2 : glDisable(GL_TEXTURE_3D);
1380 2 : glDisable(GL_FOG);
1381 2 : glDisable(GL_CLIP_PLANE0);
1382 2 : glDisable(GL_CLIP_PLANE1);
1383 2 : glDisable(GL_CLIP_PLANE2);
1384 2 : glDisable(GL_CLIP_PLANE3);
1385 2 : glDisable(GL_CLIP_PLANE4);
1386 2 : glDisable(GL_CLIP_PLANE5);
1387 :
1388 2 : glPolygonMode(GL_FRONT, GL_FILL);
1389 2 : if (GLEW_VERSION_1_3)
1390 2 : glActiveTexture(GL_TEXTURE0);
1391 :
1392 2 : glMatrixMode(GL_TEXTURE);
1393 2 : glPushMatrix();
1394 2 : glLoadIdentity();
1395 :
1396 2 : glMatrixMode(GL_PROJECTION);
1397 2 : glPushMatrix();
1398 2 : glLoadIdentity();
1399 2 : if (pvp.hasArea())
1400 2 : glOrtho(pvp.x, pvp.getXEnd(), pvp.y, pvp.getYEnd(), -1.0f, 1.0f);
1401 :
1402 2 : glMatrixMode(GL_MODELVIEW);
1403 2 : glPushMatrix();
1404 2 : glLoadIdentity();
1405 2 : EQ_GL_ERROR("after setupAssemblyState");
1406 2 : }
1407 :
1408 2 : void Compositor::resetAssemblyState()
1409 : {
1410 2 : EQ_GL_CALL(glMatrixMode(GL_TEXTURE));
1411 2 : EQ_GL_CALL(glPopMatrix());
1412 2 : EQ_GL_CALL(glMatrixMode(GL_PROJECTION));
1413 2 : EQ_GL_CALL(glPopMatrix());
1414 2 : EQ_GL_CALL(glMatrixMode(GL_MODELVIEW));
1415 2 : EQ_GL_CALL(glPopMatrix());
1416 2 : EQ_GL_CALL(glPopAttrib());
1417 2 : }
1418 30 : }
|