1 /*
2 * Copyright © 2010-2011 Linaro Limited
3 *
4 * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
5 *
6 * glmark2 is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version.
10 *
11 * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * glmark2. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Authors:
20 * Alexandros Frantzis (glmark2)
21 * Jesse Barker (glmark2)
22 */
23 #include <algorithm>
24 #include <cmath>
25 #include <cstdlib>
26
27 #include "scene.h"
28 #include "mat.h"
29 #include "options.h"
30 #include "stack.h"
31 #include "vec.h"
32 #include "log.h"
33 #include "program.h"
34 #include "shader-source.h"
35 #include "util.h"
36 #include "texture.h"
37
38 enum BlurDirection {
39 BlurDirectionHorizontal,
40 BlurDirectionVertical,
41 BlurDirectionBoth
42 };
43
44 static void
create_blur_shaders(ShaderSource & vtx_source,ShaderSource & frg_source,unsigned int radius,float sigma,BlurDirection direction)45 create_blur_shaders(ShaderSource& vtx_source, ShaderSource& frg_source,
46 unsigned int radius, float sigma, BlurDirection direction)
47 {
48 vtx_source.append_file(Options::data_path + "/shaders/desktop.vert");
49 frg_source.append_file(Options::data_path + "/shaders/desktop-blur.frag");
50
51 /* Don't let the gaussian curve become too narrow */
52 if (sigma < 1.0)
53 sigma = 1.0;
54
55 unsigned int side = 2 * radius + 1;
56
57 for (unsigned int i = 0; i < radius + 1; i++) {
58 float s2 = 2.0 * sigma * sigma;
59 float k = 1.0 / std::sqrt(M_PI * s2) * std::exp( - (static_cast<float>(i) * i) / s2);
60 std::stringstream ss_tmp;
61 ss_tmp << "Kernel" << i;
62 frg_source.add_const(ss_tmp.str(), k);
63 }
64
65 std::stringstream ss;
66 ss << "result = " << std::endl;
67
68 if (direction == BlurDirectionHorizontal) {
69 for (unsigned int i = 0; i < side; i++) {
70 int offset = static_cast<int>(i - radius);
71 ss << "texture2D(Texture0, TextureCoord + vec2(" <<
72 offset << ".0 * TextureStepX, 0.0)) * Kernel" <<
73 std::abs(offset) << " +" << std::endl;
74 }
75 ss << "0.0 ;" << std::endl;
76 }
77 else if (direction == BlurDirectionVertical) {
78 for (unsigned int i = 0; i < side; i++) {
79 int offset = static_cast<int>(i - radius);
80 ss << "texture2D(Texture0, TextureCoord + vec2(0.0, " <<
81 offset << ".0 * TextureStepY)) * Kernel" <<
82 std::abs(offset) << " +" << std::endl;
83 }
84 ss << "0.0 ;" << std::endl;
85 }
86 else if (direction == BlurDirectionBoth) {
87 for (unsigned int i = 0; i < side; i++) {
88 int ioffset = static_cast<int>(i - radius);
89 for (unsigned int j = 0; j < side; j++) {
90 int joffset = static_cast<int>(j - radius);
91 ss << "texture2D(Texture0, TextureCoord + vec2(" <<
92 ioffset << ".0 * TextureStepX, " <<
93 joffset << ".0 * TextureStepY))" <<
94 " * Kernel" << std::abs(ioffset) <<
95 " * Kernel" << std::abs(joffset) << " +" << std::endl;
96 }
97 }
98 ss << " 0.0;" << std::endl;
99 }
100
101 frg_source.replace("$CONVOLUTION$", ss.str());
102 }
103
104 /**
105 * A RenderObject represents a source and target of rendering
106 * operations.
107 */
108 class RenderObject
109 {
110 public:
RenderObject()111 RenderObject() :
112 texture_(0), fbo_(0), rotation_rad_(0),
113 texture_contents_invalid_(true) { }
114
~RenderObject()115 virtual ~RenderObject() {}
116
init()117 virtual void init()
118 {
119 /* Create a texture to draw to */
120 glGenTextures(1, &texture_);
121 glBindTexture(GL_TEXTURE_2D, texture_);
122 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
123 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
124 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
125 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
126
127 GLint prev_fbo;
128 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo);
129
130 /* Create a FBO */
131 GLExtensions::GenFramebuffers(1, &fbo_);
132 GLExtensions::BindFramebuffer(GL_FRAMEBUFFER, fbo_);
133
134 /*
135 * Only create the texture image and attach it to the framebuffer
136 * if the size has been set. Framebuffer completeness depends
137 * upon non-zero width and height images, and some implementations
138 * are overly aggressive in checking at attachment time rather than
139 * at draw time.
140 */
141 if (size_.x() != 0 && size_.y() != 0) {
142 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size_.x(), size_.y(), 0,
143 GL_RGBA, GL_UNSIGNED_BYTE, 0);
144 GLExtensions::FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
145 GL_TEXTURE_2D, texture_, 0);
146 unsigned int status = GLExtensions::CheckFramebufferStatus(GL_FRAMEBUFFER);
147 if (status != GL_FRAMEBUFFER_COMPLETE) {
148 Log::error("RenderObject::init: glCheckFramebufferStatus failed (0x%x)\n", status);
149 }
150 }
151
152 GLExtensions::BindFramebuffer(GL_FRAMEBUFFER, prev_fbo);
153
154 /* Load the shader program when this class if first used */
155 if (RenderObject::use_count == 0) {
156 ShaderSource vtx_source(Options::data_path + "/shaders/desktop.vert");
157 ShaderSource frg_source(Options::data_path + "/shaders/desktop.frag");
158 Scene::load_shaders_from_strings(main_program, vtx_source.str(),
159 frg_source.str());
160 }
161
162 texture_contents_invalid_ = true;
163 RenderObject::use_count++;
164 }
165
release()166 virtual void release()
167 {
168 /* Release resources */
169 if (texture_ != 0)
170 {
171 glDeleteTextures(1, &texture_);
172 texture_ = 0;
173 }
174 if (fbo_ != 0)
175 {
176 GLExtensions::DeleteFramebuffers(1, &fbo_);
177 fbo_ = 0;
178 }
179
180 /*
181 * Release the shader program when object of this class
182 * are no longer in use.
183 */
184 RenderObject::use_count--;
185 if (RenderObject::use_count == 0)
186 RenderObject::main_program.release();
187 }
188
make_current()189 void make_current()
190 {
191 GLExtensions::BindFramebuffer(GL_FRAMEBUFFER, fbo_);
192 glViewport(0, 0, size_.x(), size_.y());
193 }
194
position(const LibMatrix::vec2 & pos)195 void position(const LibMatrix::vec2& pos) { pos_ = pos; }
position()196 const LibMatrix::vec2& position() { return pos_; }
197
198
size(const LibMatrix::vec2 & size)199 virtual void size(const LibMatrix::vec2& size)
200 {
201 /* Recreate the backing texture with correct size */
202 if (size_.x() != size.x() || size_.y() != size.y()) {
203 size_ = size;
204 if (fbo_ == 0)
205 return;
206 /* If we're resizing the texture, we need to tell the framebuffer*/
207 glBindTexture(GL_TEXTURE_2D, texture_);
208 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size_.x(), size_.y(), 0,
209 GL_RGBA, GL_UNSIGNED_BYTE, 0);
210 GLExtensions::BindFramebuffer(GL_FRAMEBUFFER, fbo_);
211 GLExtensions::FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
212 GL_TEXTURE_2D, texture_, 0);
213 unsigned int status = GLExtensions::CheckFramebufferStatus(GL_FRAMEBUFFER);
214 if (status != GL_FRAMEBUFFER_COMPLETE) {
215 Log::error("RenderObject::size: glCheckFramebufferStatus failed (0x%x)\n", status);
216 }
217 texture_contents_invalid_ = true;
218 }
219
220 if (texture_contents_invalid_) {
221 clear();
222 texture_contents_invalid_ = false;
223 }
224 }
225
size()226 const LibMatrix::vec2& size() { return size_; }
227
speed()228 const LibMatrix::vec2& speed() { return speed_; }
speed(const LibMatrix::vec2 & speed)229 void speed(const LibMatrix::vec2& speed) { speed_ = speed; }
230
texture()231 GLuint texture() { return texture_; }
232
clear()233 virtual void clear()
234 {
235 make_current();
236 glClear(GL_COLOR_BUFFER_BIT);
237 }
238
render_to(RenderObject & target)239 virtual void render_to(RenderObject& target)
240 {
241 render_to(target, main_program);
242 }
243
render_to(RenderObject & target,Program & program)244 virtual void render_to(RenderObject& target, Program& program)
245 {
246 LibMatrix::vec2 anchor(pos_);
247 LibMatrix::vec2 ll(pos_ - anchor);
248 LibMatrix::vec2 ur(pos_ + size_ - anchor);
249
250 /* Calculate new position according to rotation value */
251 GLfloat position[2 * 4] = {
252 rotate_x(ll.x(), ll.y()) + anchor.x(), rotate_y(ll.x(), ll.y()) + anchor.y(),
253 rotate_x(ur.x(), ll.y()) + anchor.x(), rotate_y(ur.x(), ll.y()) + anchor.y(),
254 rotate_x(ll.x(), ur.y()) + anchor.x(), rotate_y(ll.x(), ur.y()) + anchor.y(),
255 rotate_x(ur.x(), ur.y()) + anchor.x(), rotate_y(ur.x(), ur.y()) + anchor.y(),
256 };
257
258 /* Normalize position and write back to array */
259 for (int i = 0; i < 4; i++) {
260 const LibMatrix::vec2& v2(
261 target.normalize_position(
262 LibMatrix::vec2(position[2 * i], position[2 * i + 1])
263 )
264 );
265 position[2 * i] = v2.x();
266 position[2 * i + 1] = v2.y();
267 }
268
269 static const GLfloat texcoord[2 * 4] = {
270 0.0, 0.0,
271 1.0, 0.0,
272 0.0, 1.0,
273 1.0, 1.0,
274 };
275
276 target.make_current();
277
278 glActiveTexture(GL_TEXTURE0);
279 glBindTexture(GL_TEXTURE_2D, texture_);
280 draw_quad_with_program(position, texcoord, program);
281 }
282
render_from(RenderObject & target,Program & program=main_program)283 virtual void render_from(RenderObject& target, Program& program = main_program)
284 {
285 LibMatrix::vec2 final_pos(pos_ + size_);
286 LibMatrix::vec2 ll_tex(target.normalize_texcoord(pos_));
287 LibMatrix::vec2 ur_tex(target.normalize_texcoord(final_pos));
288
289 static const GLfloat position_blur[2 * 4] = {
290 -1.0, -1.0,
291 1.0, -1.0,
292 -1.0, 1.0,
293 1.0, 1.0,
294 };
295 GLfloat texcoord_blur[2 * 4] = {
296 ll_tex.x(), ll_tex.y(),
297 ur_tex.x(), ll_tex.y(),
298 ll_tex.x(), ur_tex.y(),
299 ur_tex.x(), ur_tex.y(),
300 };
301
302 make_current();
303 glBindTexture(GL_TEXTURE_2D, target.texture());
304 draw_quad_with_program(position_blur, texcoord_blur, program);
305 }
306
307 /**
308 * Normalizes a position from [0, size] to [-1.0, 1.0]
309 */
normalize_position(const LibMatrix::vec2 & pos)310 LibMatrix::vec2 normalize_position(const LibMatrix::vec2& pos)
311 {
312 return pos * 2.0 / size_ - 1.0;
313 }
314
315 /**
316 * Normalizes a position from [0, size] to [0.0, 1.0]
317 */
normalize_texcoord(const LibMatrix::vec2 & pos)318 LibMatrix::vec2 normalize_texcoord(const LibMatrix::vec2& pos)
319 {
320 return pos / size_;
321 }
322
rotation(float degrees)323 void rotation(float degrees)
324 {
325 rotation_rad_ = (M_PI * degrees / 180.0);
326 }
327
328 protected:
draw_quad_with_program(const GLfloat * position,const GLfloat * texcoord,Program & program)329 void draw_quad_with_program(const GLfloat *position, const GLfloat *texcoord,
330 Program &program)
331 {
332 int pos_index = program["position"].location();
333 int tex_index = program["texcoord"].location();
334
335 program.start();
336
337 glEnableVertexAttribArray(pos_index);
338 glEnableVertexAttribArray(tex_index);
339 glVertexAttribPointer(pos_index, 2,
340 GL_FLOAT, GL_FALSE, 0, position);
341 glVertexAttribPointer(tex_index, 2,
342 GL_FLOAT, GL_FALSE, 0, texcoord);
343
344 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
345
346 glDisableVertexAttribArray(tex_index);
347 glDisableVertexAttribArray(pos_index);
348
349 program.stop();
350 }
351
352 static Program main_program;
353
354 LibMatrix::vec2 pos_;
355 LibMatrix::vec2 size_;
356 LibMatrix::vec2 speed_;
357 GLuint texture_;
358 GLuint fbo_;
359
360 private:
rotate_x(float x,float y)361 float rotate_x(float x, float y)
362 {
363 return x * cos(rotation_rad_) - y * sin(rotation_rad_);
364 }
365
rotate_y(float x,float y)366 float rotate_y(float x, float y)
367 {
368 return x * sin(rotation_rad_) + y * cos(rotation_rad_);
369 }
370
371 float rotation_rad_;
372 bool texture_contents_invalid_;
373 static int use_count;
374
375 };
376
377 int RenderObject::use_count = 0;
378 Program RenderObject::main_program;
379
380 /**
381 * A RenderObject representing the screen.
382 *
383 * Rendering to this objects renders to the screen framebuffer.
384 */
385 class RenderScreen : public RenderObject
386 {
387 Canvas &canvas_;
388 public:
RenderScreen(Canvas & canvas)389 RenderScreen(Canvas &canvas) : canvas_(canvas) {}
init()390 virtual void init() { fbo_ = canvas_.fbo(); }
size(const LibMatrix::vec2 & size)391 virtual void size(const LibMatrix::vec2& size) { size_ = size; clear(); }
release()392 virtual void release() {}
393 };
394
395 /**
396 * A RenderObject with a background image.
397 *
398 * The image is drawn to the RenderObject automatically when the
399 * object is cleared, resized etc
400 */
401 class RenderClearImage : public RenderObject
402 {
403 public:
RenderClearImage(const std::string & texture)404 RenderClearImage(const std::string& texture) :
405 RenderObject(), background_texture_name(texture),
406 background_texture_(0) {}
407
init()408 virtual void init()
409 {
410 RenderObject::init();
411
412 /* Load the image into a texture */
413 Texture::load(background_texture_name,
414 &background_texture_, GL_LINEAR, GL_LINEAR, 0);
415
416 }
417
release()418 virtual void release()
419 {
420 glDeleteTextures(1, &background_texture_);
421 background_texture_ = 0;
422
423 RenderObject::release();
424 }
425
clear()426 virtual void clear()
427 {
428 static const GLfloat position[2 * 4] = {
429 -1.0, -1.0,
430 1.0, -1.0,
431 -1.0, 1.0,
432 1.0, 1.0,
433 };
434 static const GLfloat texcoord[2 * 4] = {
435 0.0, 0.0,
436 1.0, 0.0,
437 0.0, 1.0,
438 1.0, 1.0,
439 };
440
441 make_current();
442 glClear(GL_COLOR_BUFFER_BIT);
443
444 glActiveTexture(GL_TEXTURE0);
445 glBindTexture(GL_TEXTURE_2D, background_texture_);
446
447 glEnable(GL_BLEND);
448 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
449 draw_quad_with_program(position, texcoord, main_program);
450 glDisable(GL_BLEND);
451 }
452
453 private:
454 std::string background_texture_name;
455 GLuint background_texture_;
456 };
457
458 /**
459 * A RenderObject that blurs the target it is drawn to.
460 */
461 class RenderWindowBlur : public RenderObject
462 {
463 public:
RenderWindowBlur(unsigned int passes,unsigned int radius,bool separable,bool draw_contents=true)464 RenderWindowBlur(unsigned int passes, unsigned int radius, bool separable,
465 bool draw_contents = true) :
466 RenderObject(), passes_(passes), radius_(radius), separable_(separable),
467 draw_contents_(draw_contents) {}
468
init()469 virtual void init()
470 {
471 RenderObject::init();
472
473 /* Only have one instance of the window contents data */
474 if (draw_contents_ && RenderWindowBlur::use_count == 0)
475 window_contents_.init();
476
477 RenderWindowBlur::use_count++;
478 }
479
release()480 virtual void release()
481 {
482 RenderWindowBlur::use_count--;
483
484 /* Only have one instance of the window contents data */
485 if (draw_contents_ && RenderWindowBlur::use_count == 0)
486 window_contents_.release();
487
488 RenderObject::release();
489 }
490
size(const LibMatrix::vec2 & size)491 virtual void size(const LibMatrix::vec2& size)
492 {
493 RenderObject::size(size);
494 if (draw_contents_)
495 window_contents_.size(size);
496 }
497
render_to(RenderObject & target)498 virtual void render_to(RenderObject& target)
499 {
500 if (separable_) {
501 Program& blur_program_h1 = blur_program_h(target.size().x());
502 Program& blur_program_v1 = blur_program_v(target.size().y());
503
504 for (unsigned int i = 0; i < passes_; i++) {
505 render_from(target, blur_program_h1);
506 RenderObject::render_to(target, blur_program_v1);
507 }
508 }
509 else {
510 Program& blur_program1 = blur_program(target.size().x(), target.size().y());
511
512 for (unsigned int i = 0; i < passes_; i++) {
513 if (i % 2 == 0)
514 render_from(target, blur_program1);
515 else
516 RenderObject::render_to(target, blur_program1);
517 }
518
519 if (passes_ % 2 == 1)
520 RenderObject::render_to(target);
521 }
522
523 /*
524 * Blend the window contents with the target texture.
525 */
526 if (draw_contents_) {
527 glEnable(GL_BLEND);
528 /*
529 * Blend the colors normally, but don't change the
530 * destination alpha value.
531 */
532 glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
533 GL_ZERO, GL_ONE);
534 window_contents_.position(position());
535 window_contents_.render_to(target);
536 glDisable(GL_BLEND);
537 }
538 }
539
540 private:
blur_program(unsigned int w,unsigned int h)541 Program& blur_program(unsigned int w, unsigned int h)
542 {
543 /*
544 * If the size of the window has changed we must recreate
545 * the shader to contain the correct texture step values.
546 */
547 if (blur_program_dim_.x() != w || blur_program_dim_.y() != h ||
548 !blur_program_.ready())
549 {
550 blur_program_dim_.x(w);
551 blur_program_dim_.y(h);
552
553 blur_program_.release();
554
555 ShaderSource vtx_source;
556 ShaderSource frg_source;
557 create_blur_shaders(vtx_source, frg_source, radius_,
558 radius_ / 3.0, BlurDirectionBoth);
559 frg_source.add_const("TextureStepX", 1.0 / w);
560 frg_source.add_const("TextureStepY", 1.0 / h);
561 Scene::load_shaders_from_strings(blur_program_, vtx_source.str(),
562 frg_source.str());
563 }
564
565 return blur_program_;
566 }
567
blur_program_h(unsigned int w)568 Program& blur_program_h(unsigned int w)
569 {
570 /*
571 * If the size of the window has changed we must recreate
572 * the shader to contain the correct texture step values.
573 */
574 if (blur_program_dim_.x() != w ||
575 !blur_program_h_.ready())
576 {
577 blur_program_dim_.x(w);
578
579 blur_program_h_.release();
580
581 ShaderSource vtx_source;
582 ShaderSource frg_source;
583 create_blur_shaders(vtx_source, frg_source, radius_,
584 radius_ / 3.0, BlurDirectionHorizontal);
585 frg_source.add_const("TextureStepX", 1.0 / w);
586 Scene::load_shaders_from_strings(blur_program_h_, vtx_source.str(),
587 frg_source.str());
588 }
589
590 return blur_program_h_;
591 }
592
blur_program_v(unsigned int h)593 Program& blur_program_v(unsigned int h)
594 {
595 /*
596 * If the size of the window has changed we must recreate
597 * the shader to contain the correct texture step values.
598 */
599 if (blur_program_dim_.y() != h ||
600 !blur_program_v_.ready())
601 {
602 blur_program_dim_.y(h);
603
604 blur_program_v_.release();
605
606 ShaderSource vtx_source;
607 ShaderSource frg_source;
608 create_blur_shaders(vtx_source, frg_source, radius_,
609 radius_ / 3.0, BlurDirectionVertical);
610 frg_source.add_const("TextureStepY", 1.0 / h);
611 Scene::load_shaders_from_strings(blur_program_v_, vtx_source.str(),
612 frg_source.str());
613 }
614
615 return blur_program_v_;
616 }
617
618 LibMatrix::uvec2 blur_program_dim_;
619 Program blur_program_;
620 Program blur_program_h_;
621 Program blur_program_v_;
622 unsigned int passes_;
623 unsigned int radius_;
624 bool separable_;
625 bool draw_contents_;
626
627 static int use_count;
628 static RenderClearImage window_contents_;
629
630 };
631
632 /**
633 * A RenderObject that draws a drop shadow around the window.
634 */
635 class RenderWindowShadow : public RenderObject
636 {
637 public:
638 using RenderObject::size;
639
RenderWindowShadow(unsigned int shadow_size,bool draw_contents=true)640 RenderWindowShadow(unsigned int shadow_size, bool draw_contents = true) :
641 RenderObject(), shadow_size_(shadow_size), draw_contents_(draw_contents) {}
642
init()643 virtual void init()
644 {
645 RenderObject::init();
646
647 /*
648 * Only have one instance of the resources.
649 * This works only if all windows have the same size, which
650 * is currently the case for this scene. If this condition
651 * ceases to be true we will need to create the resources per
652 * object.
653 */
654 if (RenderWindowShadow::use_count == 0) {
655 shadow_h_.init();
656 shadow_v_.init();
657 shadow_corner_.init();
658 if (draw_contents_)
659 window_contents_.init();
660 }
661
662 RenderWindowShadow::use_count++;
663 }
664
release()665 virtual void release()
666 {
667 RenderWindowShadow::use_count--;
668
669 /* Only have one instance of the data */
670 if (RenderWindowShadow::use_count == 0) {
671 shadow_h_.release();
672 shadow_v_.release();
673 shadow_corner_.release();
674 if (draw_contents_)
675 window_contents_.release();
676 }
677
678 RenderObject::release();
679 }
680
size(const LibMatrix::vec2 & size)681 virtual void size(const LibMatrix::vec2& size)
682 {
683 RenderObject::size(size);
684 shadow_h_.size(LibMatrix::vec2(size.x() - shadow_size_,
685 static_cast<double>(shadow_size_)));
686 shadow_v_.size(LibMatrix::vec2(size.y() - shadow_size_,
687 static_cast<double>(shadow_size_)));
688 shadow_corner_.size(LibMatrix::vec2(static_cast<double>(shadow_size_),
689 static_cast<double>(shadow_size_)));
690 if (draw_contents_)
691 window_contents_.size(size);
692 }
693
render_to(RenderObject & target)694 virtual void render_to(RenderObject& target)
695 {
696 glEnable(GL_BLEND);
697 /*
698 * Blend the colors normally, but don't change the
699 * destination alpha value.
700 */
701 glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
702 GL_ZERO, GL_ONE);
703
704 /* Bottom shadow */
705 shadow_h_.rotation(0.0);
706 shadow_h_.position(position() +
707 LibMatrix::vec2(shadow_size_,
708 -shadow_h_.size().y()));
709 shadow_h_.render_to(target);
710
711 /* Right shadow */
712 shadow_v_.rotation(90.0);
713 shadow_v_.position(position() +
714 LibMatrix::vec2(size().x() + shadow_v_.size().y(), 0.0));
715 shadow_v_.render_to(target);
716
717 /* Bottom right shadow */
718 shadow_corner_.rotation(0.0);
719 shadow_corner_.position(position() +
720 LibMatrix::vec2(size().x(),
721 -shadow_corner_.size().y()));
722 shadow_corner_.render_to(target);
723
724 /* Top right shadow */
725 shadow_corner_.rotation(90.0);
726 shadow_corner_.position(position() + size() +
727 LibMatrix::vec2(shadow_corner_.size().x(),
728 -shadow_corner_.size().y()));
729 shadow_corner_.render_to(target);
730
731 /* Bottom left shadow */
732 shadow_corner_.rotation(-90.0);
733 shadow_corner_.position(position());
734 shadow_corner_.render_to(target);
735
736 /*
737 * Blend the window contents with the target texture.
738 */
739 if (draw_contents_) {
740 window_contents_.position(position());
741 window_contents_.render_to(target);
742 }
743
744 glDisable(GL_BLEND);
745 }
746
747 private:
748 unsigned int shadow_size_;
749 bool draw_contents_;
750
751 static int use_count;
752 static RenderClearImage window_contents_;
753 static RenderClearImage shadow_h_;
754 static RenderClearImage shadow_v_;
755 static RenderClearImage shadow_corner_;
756
757 };
758
759 int RenderWindowBlur::use_count = 0;
760 RenderClearImage RenderWindowBlur::window_contents_("desktop-window");
761 int RenderWindowShadow::use_count = 0;
762 RenderClearImage RenderWindowShadow::window_contents_("desktop-window");
763 RenderClearImage RenderWindowShadow::shadow_h_("desktop-shadow");
764 RenderClearImage RenderWindowShadow::shadow_v_("desktop-shadow");
765 RenderClearImage RenderWindowShadow::shadow_corner_("desktop-shadow-corner");
766
767 /*******************************
768 * SceneDesktop implementation *
769 *******************************/
770
771 /**
772 * Private structure used to avoid contaminating scene.h with all of the
773 * SceneDesktop internal classes.
774 */
775 struct SceneDesktopPrivate
776 {
777 RenderScreen screen;
778 RenderClearImage desktop;
779 std::vector<RenderObject *> windows;
780
SceneDesktopPrivateSceneDesktopPrivate781 SceneDesktopPrivate(Canvas &canvas) :
782 screen(canvas), desktop("effect-2d") {}
783
~SceneDesktopPrivateSceneDesktopPrivate784 ~SceneDesktopPrivate() { Util::dispose_pointer_vector(windows); }
785
786 };
787
788
SceneDesktop(Canvas & canvas)789 SceneDesktop::SceneDesktop(Canvas &canvas) :
790 Scene(canvas, "desktop")
791 {
792 priv_ = new SceneDesktopPrivate(canvas);
793 options_["effect"] = Scene::Option("effect", "blur", "The effect to use",
794 "blur,shadow");
795 options_["windows"] = Scene::Option("windows", "4",
796 "the number of windows");
797 options_["window-size"] = Scene::Option("window-size", "0.35",
798 "the window size as a percentage of the minimum screen dimension [0.0 - 0.5]");
799 options_["passes"] = Scene::Option("passes", "1",
800 "the number of effect passes (effect dependent)");
801 options_["blur-radius"] = Scene::Option("blur-radius", "5",
802 "the blur effect radius (in pixels)");
803 options_["separable"] = Scene::Option("separable", "true",
804 "use separable convolution for the blur effect",
805 "false,true");
806 options_["shadow-size"] = Scene::Option("shadow-size", "20",
807 "the size of the shadow (in pixels)");
808 }
809
~SceneDesktop()810 SceneDesktop::~SceneDesktop()
811 {
812 delete priv_;
813 }
814
815 bool
supported(bool show_errors)816 SceneDesktop::supported(bool show_errors)
817 {
818 if (show_errors && !GLExtensions::GenFramebuffers) {
819 Log::error("SceneDesktop requires GL framebuffer support\n");
820 }
821
822 return GLExtensions::GenFramebuffers;
823 }
824
825 bool
load()826 SceneDesktop::load()
827 {
828 return true;
829 }
830
831 void
unload()832 SceneDesktop::unload()
833 {
834 }
835
836 bool
setup()837 SceneDesktop::setup()
838 {
839 if (!Scene::setup())
840 return false;
841
842 /* Parse the options */
843 unsigned int windows(0);
844 unsigned int passes(0);
845 unsigned int blur_radius(0);
846 float window_size_factor(0.0);
847 unsigned int shadow_size(0);
848 bool separable(options_["separable"].value == "true");
849
850 windows = Util::fromString<unsigned int>(options_["windows"].value);
851 window_size_factor = Util::fromString<float>(options_["window-size"].value);
852 passes = Util::fromString<unsigned int>(options_["passes"].value);
853 blur_radius = Util::fromString<unsigned int>(options_["blur-radius"].value);
854 shadow_size = Util::fromString<unsigned int>(options_["shadow-size"].value);
855
856 // Make sure the Texture object knows where to find our images.
857 Texture::find_textures();
858
859 /* Ensure we get a transparent clear color for all following operations */
860 glClearColor(0.0, 0.0, 0.0, 0.0);
861 glDisable(GL_DEPTH_TEST);
862 glDepthMask(GL_FALSE);
863
864 /* Set up the screen and desktop RenderObjects */
865 priv_->screen.init();
866 priv_->desktop.init();
867 priv_->screen.size(LibMatrix::vec2(canvas_.width(), canvas_.height()));
868 priv_->desktop.size(LibMatrix::vec2(canvas_.width(), canvas_.height()));
869
870 /* Create the windows */
871 const float angular_step(2.0 * M_PI / windows);
872 unsigned int min_dimension = std::min(canvas_.width(), canvas_.height());
873 float window_size(min_dimension * window_size_factor);
874 static const LibMatrix::vec2 corner_offset(window_size / 2.0,
875 window_size / 2.0);
876
877 for (unsigned int i = 0; i < windows; i++) {
878 LibMatrix::vec2 center(canvas_.width() * (0.5 + 0.25 * cos(i * angular_step)),
879 canvas_.height() * (0.5 + 0.25 * sin(i * angular_step)));
880 RenderObject* win;
881 if (options_["effect"].value == "shadow")
882 win = new RenderWindowShadow(shadow_size);
883 else
884 win = new RenderWindowBlur(passes, blur_radius, separable);
885
886 win->init();
887 win->position(center - corner_offset);
888 win->size(LibMatrix::vec2(window_size, window_size));
889 /*
890 * Set the speed in increments of about 30 degrees (but not exactly,
891 * so we don't get windows moving just on the X axis or Y axis).
892 */
893 win->speed(LibMatrix::vec2(cos(0.1 + i * M_PI / 6.0) * canvas_.width() / 3,
894 sin(0.1 + i * M_PI / 6.0) * canvas_.height() / 3));
895 /*
896 * Perform a dummy rendering to ensure internal shaders are initialized
897 * now, in order not to affect the benchmarking.
898 */
899 win->render_to(priv_->desktop);
900 priv_->windows.push_back(win);
901 }
902
903 /*
904 * Ensure the screen is the current rendering target (it might have changed
905 * to a FBO in the previous steps).
906 */
907 priv_->screen.make_current();
908
909 currentFrame_ = 0;
910 running_ = true;
911 startTime_ = Util::get_timestamp_us() / 1000000.0;
912 lastUpdateTime_ = startTime_;
913
914 return true;
915 }
916
917 void
teardown()918 SceneDesktop::teardown()
919 {
920 for (std::vector<RenderObject*>::iterator winIt = priv_->windows.begin();
921 winIt != priv_->windows.end();
922 winIt++)
923 {
924 RenderObject* curObj = *winIt;
925 curObj->release();
926 delete curObj;
927 }
928 priv_->windows.clear();
929 if (supported(false))
930 priv_->screen.make_current();
931
932 glEnable(GL_DEPTH_TEST);
933 glDepthMask(GL_TRUE);
934
935 priv_->desktop.release();
936 priv_->screen.release();
937
938 Scene::teardown();
939 }
940
941 void
update()942 SceneDesktop::update()
943 {
944 double current_time = Util::get_timestamp_us() / 1000000.0;
945 double dt = current_time - lastUpdateTime_;
946
947 Scene::update();
948
949 std::vector<RenderObject *>& windows(priv_->windows);
950
951 /*
952 * Move the windows around the screen, bouncing them back when
953 * they reach the edge.
954 */
955 for (std::vector<RenderObject *>::const_iterator iter = windows.begin();
956 iter != windows.end();
957 iter++)
958 {
959 bool should_update = true;
960 RenderObject *win = *iter;
961 LibMatrix::vec2 new_pos(
962 win->position().x() + win->speed().x() * dt,
963 win->position().y() + win->speed().y() * dt);
964
965 if (new_pos.x() < 0.0 ||
966 new_pos.x() + win->size().x() > static_cast<float>(canvas_.width()))
967 {
968 win->speed(LibMatrix::vec2(-win->speed().x(), win->speed().y()));
969 should_update = false;
970 }
971
972 if (new_pos.y() < 0.0 ||
973 new_pos.y() + win->size().y() > static_cast<float>(canvas_.height()))
974 {
975 win->speed(LibMatrix::vec2(win->speed().x(), -win->speed().y()));
976 should_update = false;
977 }
978
979 if (should_update)
980 win->position(new_pos);
981 }
982 }
983
984 void
draw()985 SceneDesktop::draw()
986 {
987 std::vector<RenderObject *>& windows(priv_->windows);
988
989 /* Ensure we get a transparent clear color for all following operations */
990 glClearColor(0.0, 0.0, 0.0, 0.0);
991
992 priv_->desktop.clear();
993
994 for (std::vector<RenderObject *>::const_iterator iter = windows.begin();
995 iter != windows.end();
996 iter++)
997 {
998 RenderObject *win = *iter;
999 win->render_to(priv_->desktop);
1000 }
1001
1002 priv_->desktop.render_to(priv_->screen);
1003
1004 }
1005
1006 Scene::ValidationResult
validate()1007 SceneDesktop::validate()
1008 {
1009 static const double radius_3d(std::sqrt(3.0 * 2.0 * 2.0));
1010
1011 Canvas::Pixel ref;
1012
1013 /* Parse the options */
1014 unsigned int windows(0);
1015 unsigned int passes(0);
1016 unsigned int blur_radius(0);
1017 float window_size_factor(0.0);
1018 unsigned int shadow_size(0);
1019
1020 windows = Util::fromString<unsigned int>(options_["windows"].value);
1021 window_size_factor = Util::fromString<float>(options_["window-size"].value);
1022 passes = Util::fromString<unsigned int>(options_["passes"].value);
1023 blur_radius = Util::fromString<unsigned int>(options_["blur-radius"].value);
1024 shadow_size = Util::fromString<unsigned int>(options_["shadow-size"].value);
1025
1026 if (options_["effect"].value == "blur")
1027 {
1028 if (windows == 4 && passes == 1 && blur_radius == 5)
1029 ref = Canvas::Pixel(0x89, 0xa3, 0x53, 0xff);
1030 else
1031 return Scene::ValidationUnknown;
1032 }
1033 else if (options_["effect"].value == "shadow")
1034 {
1035 if (windows == 4 && fabs(window_size_factor - 0.35) < 0.0001 &&
1036 shadow_size == 20)
1037 {
1038 ref = Canvas::Pixel(0x1f, 0x27, 0x0d, 0xff);
1039 }
1040 else
1041 {
1042 return Scene::ValidationUnknown;
1043 }
1044 }
1045
1046 Canvas::Pixel pixel = canvas_.read_pixel(512, 209);
1047
1048 double dist = pixel.distance_rgb(ref);
1049 if (dist < radius_3d + 0.01) {
1050 return Scene::ValidationSuccess;
1051 }
1052 Log::debug("Validation failed! Expected: 0x%x Actual: 0x%x Distance: %f\n",
1053 ref.to_le32(), pixel.to_le32(), dist);
1054 return Scene::ValidationFailure;
1055 }
1056