Back to index

lightning-sunbird  0.9+nobinonly
nsGCCache.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the Mozilla browser.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Stuart Parmenter <pavlov@netscape.com>
00025  *   Mike Shaver <shaver@zeroknowledge.com>
00026  *   Tomi Leppikangas <Tomi.Leppikangas@oulu.fi>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include <stdio.h>
00043 #include "nsGCCache.h"
00044 
00045 #include <X11/Xlib.h>
00046 #include <X11/Xutil.h>
00047 
00048 /* The GC cache is shared among all windows, since it doesn't hog
00049    any scarce resources (like colormap entries.) */
00050 
00051 Region nsGCCacheXlib::copyRegion = 0;
00052 
00053 nsGCCacheXlib::nsGCCacheXlib()
00054 {
00055   PR_INIT_CLIST(&GCCache);
00056   PR_INIT_CLIST(&GCFreeList);
00057   for (int i = 0; i < GC_CACHE_SIZE; i++) {
00058     GCCacheEntryXlib *entry = new GCCacheEntryXlib();
00059     entry->gc=NULL;
00060     PR_INSERT_LINK(&entry->clist, &GCFreeList);
00061   }
00062   DEBUG_METER(memset(&GCCacheStats, 0, sizeof(GCCacheStats));)
00063 }
00064 
00065 void
00066 nsGCCacheXlib::move_cache_entry(PRCList *clist)
00067 {
00068   /* thread on the freelist, at the front */
00069   PR_REMOVE_LINK(clist);
00070   PR_INSERT_LINK(clist, &GCFreeList);
00071 }
00072 
00073 void
00074 nsGCCacheXlib::free_cache_entry(PRCList *clist)
00075 {
00076   GCCacheEntryXlib *entry = (GCCacheEntryXlib *)clist;
00077   entry->gc->Release();
00078   if (entry->clipRegion)
00079     ::XDestroyRegion(entry->clipRegion);
00080   
00081   /* thread on the freelist, at the front */
00082   PR_REMOVE_LINK(clist);
00083   memset(entry, 0, sizeof(*entry));
00084   PR_INSERT_LINK(clist, &GCFreeList);
00085 }
00086 
00087 nsGCCacheXlib::~nsGCCacheXlib()
00088 {
00089   PRCList *head;
00090 
00091   ReportStats();
00092 
00093   while (!PR_CLIST_IS_EMPTY(&GCCache)) {
00094     head = PR_LIST_HEAD(&GCCache);
00095     if (head == &GCCache)
00096       break;
00097     free_cache_entry(head);
00098   }
00099 
00100   while (!PR_CLIST_IS_EMPTY(&GCFreeList)) {
00101     head = PR_LIST_HEAD(&GCFreeList);
00102     if (head == &GCFreeList)
00103       break;
00104     PR_REMOVE_LINK(head);
00105     delete (GCCacheEntryXlib *)head;
00106   }
00107 }
00108 
00109 void
00110 nsGCCacheXlib::ReportStats() { 
00111   DEBUG_METER(
00112               fprintf(stderr, "GC Cache:\n\thits:");
00113               int hits = 0;
00114               for (int i = 0; i < GC_CACHE_SIZE; i++) {
00115                 fprintf(stderr, " %4d", GCCacheStats.hits[i]);
00116                 hits+=GCCacheStats.hits[i];
00117               }
00118               int total = hits + GCCacheStats.misses;
00119               float percent = float(float(hits) / float(total));
00120               percent *= 100;
00121               fprintf(stderr, "\n\thits: %d, misses: %d, hit percent: %f%%\n", 
00122                       hits, GCCacheStats.misses, percent);
00123               );
00124 }
00125 
00126 
00127 void
00128 nsGCCacheXlib::XCopyRegion(Region srca, Region dr_return)
00129 {
00130   if (!copyRegion) copyRegion = ::XCreateRegion();
00131   ::XUnionRegion(srca, copyRegion, dr_return);
00132 }
00133 
00134 /* Dispose of entries matching the given flags, compressing the GC cache */
00135 void nsGCCacheXlib::Flush(unsigned long flags)
00136 {
00137   while (!PR_CLIST_IS_EMPTY(&GCCache)) {
00138     PRCList *head = PR_LIST_HEAD(&GCCache);
00139     if (head == &GCCache)
00140       break;
00141     GCCacheEntryXlib *entry = (GCCacheEntryXlib *)head;
00142     if (entry->flags & flags)
00143       free_cache_entry(head);
00144   }
00145 }
00146 
00147 xGC *nsGCCacheXlib::GetGC(Display *display, Drawable drawable, unsigned long flags, XGCValues *gcv, Region clipRegion)
00148 {
00149   PRCList *iter;
00150   GCCacheEntryXlib *entry;
00151   DEBUG_METER(int i = 0;)
00152   
00153   for (iter = PR_LIST_HEAD(&GCCache); iter != &GCCache;
00154        iter = PR_NEXT_LINK(iter)) {
00155 
00156     entry = (GCCacheEntryXlib *)iter;
00157     if (flags == entry->flags && 
00158         !memcmp (gcv, &entry->gcv, sizeof (*gcv))) {
00159       /* if there's a clipRegion, we have to match */
00160 
00161       if ((clipRegion && entry->clipRegion &&
00162            ::XEqualRegion(clipRegion, entry->clipRegion)) ||
00163           /* and if there isn't, we can't have one */
00164           (!clipRegion && !entry->clipRegion)) {
00165 
00166         /* move to the front of the list, if needed */
00167         if (iter != PR_LIST_HEAD(&GCCache)) {
00168           PR_REMOVE_LINK(iter);
00169           PR_INSERT_LINK(iter, &GCCache);
00170         }
00171         DEBUG_METER(GCCacheStats.hits[i]++;)
00172 
00173         entry->gc->AddRef();
00174         return entry->gc;
00175       }
00176     }
00177     DEBUG_METER(++i;)
00178   }
00179     
00180   /* might need to forcibly free the LRU cache entry */
00181   if (PR_CLIST_IS_EMPTY(&GCFreeList)) {
00182     DEBUG_METER(GCCacheStats.reclaim++);
00183     move_cache_entry(PR_LIST_TAIL(&GCCache));
00184   }
00185 
00186   DEBUG_METER(GCCacheStats.misses++;)
00187   
00188   iter = PR_LIST_HEAD(&GCFreeList);
00189   PR_REMOVE_LINK(iter);
00190   PR_INSERT_LINK(iter, &GCCache);
00191   entry = (GCCacheEntryXlib *)iter;
00192 
00193   if (!entry->gc) {
00194     // No old GC, greate new
00195     entry->gc = new xGC(display, drawable, flags, gcv);
00196     entry->gc->AddRef(); // addref the newly created xGC
00197     entry->flags = flags;
00198     entry->gcv = *gcv;
00199     entry->clipRegion = NULL;
00200     //printf("creating new gc=%X\n",entry->gc); 
00201   }
00202   else if (entry->gc->mRefCnt > 0) {
00203     // Old GC still in use, create new
00204     entry->gc->Release();
00205     entry->gc = new xGC(display, drawable, flags, gcv);
00206     entry->gc->AddRef(); // addref the newly created xGC
00207     entry->flags = flags;
00208     entry->gcv = *gcv;
00209     if (entry->clipRegion)
00210        XDestroyRegion(entry->clipRegion);
00211     entry->clipRegion = NULL;
00212     //printf("creating new (use)gc=%X\n",entry->gc); 
00213   }
00214   else {
00215     ReuseGC(entry, flags, gcv);
00216   }
00217 
00218   if (clipRegion) {
00219     entry->clipRegion = ::XCreateRegion();
00220     XCopyRegion(clipRegion, entry->clipRegion);
00221     if (entry->clipRegion)
00222       ::XSetRegion(display, entry->gc->mGC, entry->clipRegion);
00223     /* XXX what if it fails? */
00224   }
00225   
00226   entry->gc->AddRef();
00227   return entry->gc;
00228 }
00229 
00230 void nsGCCacheXlib::ReuseGC(GCCacheEntryXlib *entry, unsigned long flags, XGCValues *gcv)
00231 {
00232   // We have old GC, reuse it and check what
00233   // we have to change
00234 
00235   if (entry->clipRegion) {
00236     // set it to none here and then set the clip region with
00237     // gdk_gc_set_clip_region in GetGC()
00238     gcv->clip_mask = None;
00239     flags |= GCClipMask;
00240     ::XDestroyRegion(entry->clipRegion);
00241     entry->clipRegion = NULL;
00242   }
00243 
00244   if (flags != 0) {
00245     ::XChangeGC(entry->gc->mDisplay, entry->gc->mGC,
00246                 flags, gcv);
00247   }
00248   entry->flags = flags;  entry->gcv = *gcv;
00249 }