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