Back to index

unity  6.0.0
BackgroundEffectHelper.cpp
Go to the documentation of this file.
00001 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
00002 /*
00003  * Copyright (C) 2010 Canonical Ltd
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License version 3 as
00007  * published by the Free Software Foundation.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016  *
00017  * Authored by: Jay Taoko <jay.taoko@canonical.com>
00018  */
00019 
00020 #include "BackgroundEffectHelper.h"
00021 #include "TimeUtil.h"
00022 
00023 #include <time.h>
00024 #ifdef TRUE
00025 #undef TRUE
00026 #endif
00027 
00028 #ifdef FALSE
00029 #undef FALSE
00030 #endif
00031 
00032 #include <X11/Xregion.h>
00033 #include <boost/utility.hpp>
00034 
00035 
00036 using namespace unity;
00037 
00038 std::list<BackgroundEffectHelper*> BackgroundEffectHelper::registered_list_;
00039 
00040 nux::Geometry BackgroundEffectHelper::monitor_rect_;
00041 
00042 nux::Property<BlurType> BackgroundEffectHelper::blur_type (BLUR_ACTIVE);
00043 nux::Property<float> BackgroundEffectHelper::sigma_high (5.0f);
00044 nux::Property<float> BackgroundEffectHelper::sigma_med (3.0f);
00045 nux::Property<float> BackgroundEffectHelper::sigma_low (1.0f);
00046 nux::Property<bool> BackgroundEffectHelper::updates_enabled (true);
00047 nux::Property<bool> BackgroundEffectHelper::detecting_occlusions (false);
00048 
00049 BackgroundEffectHelper::BackgroundEffectHelper()
00050 {
00051   enabled = false;
00052   cache_dirty = true;
00053   enabled.changed.connect (sigc::mem_fun(this, &BackgroundEffectHelper::OnEnabledChanged));
00054   noise_texture_ = nux::CreateTextureFromFile(PKGDATADIR"/dash_noise.png");
00055 
00056   Register(this);
00057 }
00058 
00059 BackgroundEffectHelper::~BackgroundEffectHelper()
00060 {
00061   noise_texture_->UnReference();
00062   Unregister(this);
00063 }
00064 
00065 void BackgroundEffectHelper::OnEnabledChanged(bool value)
00066 {
00067   if (value)
00068     DirtyCache();
00069 }
00070 
00071 void BackgroundEffectHelper::ProcessDamage(nux::Geometry geo)
00072 {
00073   for (BackgroundEffectHelper * bg_effect_helper : registered_list_)
00074   {
00075     if (bg_effect_helper->cache_dirty || !bg_effect_helper->owner || !bg_effect_helper->enabled)
00076       continue;
00077 
00078     if (!geo.Intersect (bg_effect_helper->blur_geometry_).IsNull())
00079     {
00080       bg_effect_helper->DirtyCache();
00081     }
00082   }
00083 }
00084 
00085 bool BackgroundEffectHelper::HasEnabledHelpers()
00086 {
00087   for (BackgroundEffectHelper * bg_effect_helper : registered_list_)
00088   {
00089     if (bg_effect_helper->enabled)
00090     {
00091       return true;
00092     }
00093   }
00094 
00095   return false;
00096 }
00097 
00098 bool BackgroundEffectHelper::HasDirtyHelpers()
00099 {
00100   for (BackgroundEffectHelper * bg_effect_helper : registered_list_)
00101     if (bg_effect_helper->enabled && bg_effect_helper->cache_dirty)
00102       return true;
00103 
00104   return false;
00105 }
00106 
00107 void BackgroundEffectHelper::Register(BackgroundEffectHelper* self)
00108 {
00109   registered_list_.push_back(self);
00110 }
00111 
00112 void BackgroundEffectHelper::Unregister(BackgroundEffectHelper* self)
00113 {
00114   registered_list_.remove(self);
00115 }
00116 
00117 void BackgroundEffectHelper::DirtyCache ()
00118 {
00119   if (cache_dirty)
00120     return;
00121 
00122   cache_dirty = true;
00123   if (owner)
00124     owner()->QueueDraw();
00125 }
00126 
00127 nux::ObjectPtr<nux::IOpenGLBaseTexture> BackgroundEffectHelper::GetBlurRegion(nux::Geometry geo, bool force_update)
00128 {
00129   bool should_update = force_update || cache_dirty;
00130 
00131   /* Static blur: only update when the size changed */
00132   if ((blur_type != BLUR_ACTIVE || !should_update)
00133       && blur_texture_.IsValid()
00134       && (geo == blur_geometry_))
00135   {
00136     return blur_texture_;
00137   }
00138 
00139   nux::GraphicsEngine* graphics_engine = nux::GetGraphicsDisplay()->GetGraphicsEngine();
00140   
00141   int monitor_width = BackgroundEffectHelper::monitor_rect_.width;
00142   int monitor_height = BackgroundEffectHelper::monitor_rect_.height;
00143 
00144   nux::Geometry temp = geo;
00145   temp.OffsetPosition(-monitor_rect_.x, -monitor_rect_.y);
00146   blur_geometry_ =  nux::Geometry(0, 0, monitor_width, monitor_height).Intersect(temp);
00147 
00148   nux::GpuDevice* gpu_device = nux::GetGraphicsDisplay()->GetGpuDevice();
00149   if (blur_geometry_.IsNull() || blur_type == BLUR_NONE || !gpu_device->backup_texture0_.IsValid())
00150   {
00151     return nux::ObjectPtr<nux::IOpenGLBaseTexture>();
00152   }
00153 
00154   int opengl_version = gpu_device->GetOpenGLMajorVersion();
00155   int sigma = opengl_version >= 3 ? sigma_high : sigma_med;
00156   int radius = 3 * sigma;
00157 
00158   // Define a larger region of that account for the blur radius
00159   nux::Geometry larger_blur_geometry;
00160   larger_blur_geometry.x = std::max(blur_geometry_.x - radius, 0);
00161   larger_blur_geometry.y = std::max(blur_geometry_.y - radius, 0);
00162   
00163   int xx = std::min(blur_geometry_.x + blur_geometry_.width + radius, monitor_width);
00164 
00165   larger_blur_geometry.width = xx - larger_blur_geometry.x;
00166 
00167   int yy = std::min(blur_geometry_.y + blur_geometry_.height + radius, monitor_height);
00168 
00169   larger_blur_geometry.height = yy - larger_blur_geometry.y;
00170 
00171   int dleft     = blur_geometry_.x - larger_blur_geometry.x;
00172   int dbottom   = (larger_blur_geometry.y + larger_blur_geometry.height) - (blur_geometry_.y + blur_geometry_.height);
00173 
00174   // save the current fbo
00175   nux::ObjectPtr<nux::IOpenGLFrameBufferObject> current_fbo = gpu_device->GetCurrentFrameBufferObject();
00176   gpu_device->DeactivateFrameBuffer();
00177 
00178   // Set a viewport to the requested size
00179   // FIXME: We need to do multiple passes for the dirty region
00180   // on the underlying backup texture so that we're only updating
00181   // the bits that we need
00182   graphics_engine->SetViewport(0, 0, larger_blur_geometry.width, larger_blur_geometry.height);
00183   graphics_engine->SetScissor(0, 0, larger_blur_geometry.width, larger_blur_geometry.height);
00184   // Disable nux scissoring
00185   graphics_engine->GetRenderStates ().EnableScissor (false);
00186 
00187   // The background texture is the same size as the monitor where we are rendering.
00188   nux::TexCoordXForm texxform__bg;
00189   texxform__bg.flip_v_coord = false;
00190   texxform__bg.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD);
00191   texxform__bg.uoffset = ((float) larger_blur_geometry.x) / monitor_width;
00192   texxform__bg.voffset = ((float) monitor_height - larger_blur_geometry.y - larger_blur_geometry.height) / monitor_height;
00193 
00194   bool support_frag = gpu_device->GetGpuInfo().Support_ARB_Fragment_Shader();
00195   bool support_vert = gpu_device->GetGpuInfo().Support_ARB_Vertex_Shader();
00196 
00197   if (support_vert && support_frag && opengl_version >= 2)
00198   {
00199     float noise_factor = 1.1f;
00200     float gaussian_sigma = opengl_version >= 3 ? sigma_high : sigma_med;
00201     int blur_passes = 1;
00202 
00203     nux::ObjectPtr<nux::IOpenGLBaseTexture> device_texture = gpu_device->backup_texture0_;
00204     nux::ObjectPtr<nux::CachedBaseTexture> noise_device_texture = graphics_engine->CacheResource(noise_texture_);
00205 
00206     unsigned int buffer_width = larger_blur_geometry.width;
00207     unsigned int buffer_height = larger_blur_geometry.height;
00208 
00209     blur_fx_struct_.src_texture = device_texture;
00210     graphics_engine->QRP_GLSL_GetHQBlurFx(0, 0, buffer_width, buffer_height,
00211                                                   &blur_fx_struct_, texxform__bg, nux::color::White,
00212                                                  gaussian_sigma, blur_passes);
00213 
00214     nux::TexCoordXForm texxform;
00215     nux::TexCoordXForm noise_texxform;
00216 
00217     texxform.SetFilter(nux::TEXFILTER_NEAREST, nux::TEXFILTER_NEAREST);
00218 
00219     noise_texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD);
00220     noise_texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT);
00221     noise_texxform.SetFilter(nux::TEXFILTER_NEAREST, nux::TEXFILTER_NEAREST);
00222 
00223     noise_fx_struct_.src_texture = blur_fx_struct_.dst_texture;
00224 
00225     // Add Noise
00226     nux::Color noise_color(noise_factor * 1.0f/buffer_width,
00227                            noise_factor * 1.0f/buffer_height,
00228                            1.0f, 1.0f);
00229 
00230     texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD);
00231     texxform.uoffset = (blur_geometry_.x - larger_blur_geometry.x) / (float) buffer_width;
00232     texxform.voffset = (blur_geometry_.y - larger_blur_geometry.y) / (float) buffer_height;
00233     graphics_engine->QRP_GLSL_GetDisturbedTextureFx(
00234       0, 0, blur_geometry_.width, blur_geometry_.height,
00235       noise_device_texture->m_Texture, noise_texxform, noise_color,
00236       &noise_fx_struct_, texxform, nux::color::White);
00237     
00238     blur_texture_ = noise_fx_struct_.dst_texture;
00239   }
00240   else
00241   {
00242     // GPUs with only ARB support are treated here
00243 
00244     float gaussian_sigma = sigma_low;
00245     int blur_passes = 1;
00246 
00247     nux::ObjectPtr<nux::IOpenGLBaseTexture> device_texture = gpu_device->backup_texture0_;
00248     nux::ObjectPtr<nux::CachedBaseTexture> noise_device_texture = graphics_engine->CacheResource(noise_texture_);
00249 
00250     unsigned int offset = 0;
00251     int quad_width = larger_blur_geometry.width;
00252     int quad_height = larger_blur_geometry.height;
00253 
00254     int down_size_factor = 4;
00255     unsigned int buffer_width = quad_width + 2 * offset;
00256     unsigned int buffer_height = quad_height + 2 * offset;
00257 
00258     int x = (buffer_width - quad_width) / 2;
00259     int y = (buffer_height - quad_height) / 2;
00260 
00261     unsigned int down_size_width = buffer_width / down_size_factor;
00262     unsigned int down_size_height = buffer_height / down_size_factor;
00263 
00264     nux::TexCoordXForm texxform;
00265     nux::TexCoordXForm noise_texxform;
00266 
00267     texxform.SetFilter(nux::TEXFILTER_NEAREST, nux::TEXFILTER_NEAREST);
00268 
00269     noise_texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD);
00270     noise_texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT);
00271     noise_texxform.SetFilter(nux::TEXFILTER_NEAREST, nux::TEXFILTER_NEAREST);
00272 
00273     // Copy source texture
00274     nux::ObjectPtr<nux::IOpenGLBaseTexture> texture_copy;
00275     graphics_engine->QRP_GetCopyTexture(buffer_width, buffer_height,
00276                                         texture_copy, device_texture,
00277                                         texxform__bg, nux::color::White);
00278 
00279     // Down size
00280     nux::ObjectPtr<nux::IOpenGLBaseTexture> resized_texture;
00281     graphics_engine->QRP_GetCopyTexture(down_size_width, down_size_height,
00282                                         resized_texture, texture_copy,
00283                                         texxform, nux::color::White);
00284 
00285     // Blur at a lower resolution (less pixels to process)
00286     nux::ObjectPtr<nux::IOpenGLBaseTexture> low_res_blur;
00287     low_res_blur = graphics_engine->QRP_GetBlurTexture(x, y, down_size_width, down_size_height,
00288                                                        resized_texture, texxform,
00289                                                        nux::color::White,
00290                                                        gaussian_sigma, blur_passes);
00291 
00292     // Up size
00293     texxform.SetFilter(nux::TEXFILTER_LINEAR, nux::TEXFILTER_LINEAR);
00294     graphics_engine->QRP_GetCopyTexture(buffer_width, buffer_height, resized_texture,
00295                                         low_res_blur, texxform, nux::color::White);
00296 
00297     // Returns a smaller blur region (minus blur radius).
00298     texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD);
00299     texxform.flip_v_coord = true;
00300     texxform.uoffset = dleft / (float) buffer_width;
00301     texxform.voffset = dbottom / (float) buffer_height;
00302     graphics_engine->QRP_GetCopyTexture(blur_geometry_.width, blur_geometry_.height,
00303                                         blur_texture_, resized_texture,
00304                                         texxform, nux::color::White);
00305   }
00306 
00307   if (current_fbo.IsValid())
00308   {
00309     current_fbo->Activate(true);
00310     graphics_engine->Push2DWindow(current_fbo->GetWidth(), current_fbo->GetHeight());
00311     graphics_engine->GetRenderStates ().EnableScissor (true);
00312   }
00313   else
00314   {
00315     graphics_engine->SetViewport(0, 0, monitor_width, monitor_height);
00316     graphics_engine->Push2DWindow(monitor_width, monitor_height);
00317 
00318     graphics_engine->ApplyClippingRectangle();
00319   }
00320 
00321   cache_dirty = false;
00322   return blur_texture_;
00323 }
00324 
00325 nux::ObjectPtr<nux::IOpenGLBaseTexture> BackgroundEffectHelper::GetRegion(nux::Geometry geo, bool force_update)
00326 {
00327   bool should_update = force_update || cache_dirty;
00328 
00329   /* Static blur: only update when the size changed */
00330   if ((!should_update)
00331       && blur_texture_.IsValid()
00332       && (geo == blur_geometry_))
00333   {
00334     return blur_texture_;
00335   }
00336 
00337   nux::GraphicsEngine* graphics_engine = nux::GetGraphicsDisplay()->GetGraphicsEngine();
00338   
00339   int monitor_width = BackgroundEffectHelper::monitor_rect_.width;
00340   int monitor_height = BackgroundEffectHelper::monitor_rect_.height;
00341 
00342   nux::Geometry temp = geo;
00343   temp.OffsetPosition(-monitor_rect_.x, -monitor_rect_.y);
00344   blur_geometry_ =  nux::Geometry(0, 0, monitor_width, monitor_height).Intersect(temp);
00345 
00346   nux::GpuDevice* gpu_device = nux::GetGraphicsDisplay()->GetGpuDevice();
00347   if (blur_geometry_.IsNull() || !gpu_device->backup_texture0_.IsValid())
00348   {
00349     return nux::ObjectPtr<nux::IOpenGLBaseTexture>();
00350   }
00351 
00352   // save the current fbo
00353   nux::ObjectPtr<nux::IOpenGLFrameBufferObject> current_fbo = gpu_device->GetCurrentFrameBufferObject();
00354   gpu_device->DeactivateFrameBuffer();
00355 
00356   // Set a viewport to the requested size
00357   // FIXME: We need to do multiple passes for the dirty region
00358   // on the underlying backup texture so that we're only updating
00359   // the bits that we need
00360   graphics_engine->SetViewport(0, 0, blur_geometry_.width, blur_geometry_.height);
00361   graphics_engine->SetScissor(0, 0, blur_geometry_.width, blur_geometry_.height);
00362   // Disable nux scissoring
00363   graphics_engine->GetRenderStates ().EnableScissor (false);
00364 
00365   // The background texture is the same size as the monitor where we are rendering.
00366   nux::TexCoordXForm texxform__bg;
00367   texxform__bg.flip_v_coord = false;
00368   texxform__bg.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD);
00369   texxform__bg.uoffset = ((float) blur_geometry_.x) / monitor_width;
00370   texxform__bg.voffset = ((float) monitor_height - blur_geometry_.y - blur_geometry_.height) / monitor_height;
00371 
00372   {
00373     nux::ObjectPtr<nux::IOpenGLBaseTexture> device_texture = gpu_device->backup_texture0_;
00374     nux::ObjectPtr<nux::CachedBaseTexture> noise_device_texture = graphics_engine->CacheResource(noise_texture_);
00375 
00376     unsigned int offset = 0;
00377     int quad_width = blur_geometry_.width;
00378     int quad_height = blur_geometry_.height;
00379 
00380     unsigned int buffer_width = quad_width + 2 * offset;
00381     unsigned int buffer_height = quad_height + 2 * offset;
00382 
00383     texxform__bg.SetFilter(nux::TEXFILTER_NEAREST, nux::TEXFILTER_NEAREST);
00384     texxform__bg.flip_v_coord = true;
00385 
00386     // Copy source texture
00387     graphics_engine->QRP_GetCopyTexture(buffer_width, buffer_height,
00388                                         blur_texture_, device_texture,
00389                                         texxform__bg, nux::color::White);
00390   }
00391 
00392   if (current_fbo.IsValid())
00393   {
00394     current_fbo->Activate(true);
00395     graphics_engine->Push2DWindow(current_fbo->GetWidth(), current_fbo->GetHeight());
00396     graphics_engine->GetRenderStates ().EnableScissor (true);
00397   }
00398   else
00399   {
00400     graphics_engine->SetViewport(0, 0, monitor_width, monitor_height);
00401     graphics_engine->Push2DWindow(monitor_width, monitor_height);
00402 
00403     graphics_engine->ApplyClippingRectangle();
00404   }
00405 
00406   cache_dirty = false;
00407   return blur_texture_;
00408 }
00409