[Fuse-for-macosx-commits] SF.net SVN: fuse-for-macosx: [395] trunk/fuse
Brought to you by:
fredm
|
From: <fr...@us...> - 2007-06-23 16:11:42
|
Revision: 395
http://svn.sourceforge.net/fuse-for-macosx/?rev=395&view=rev
Author: fredm
Date: 2007-06-23 09:11:43 -0700 (Sat, 23 Jun 2007)
Log Message:
-----------
Attempt to limit the memory bus requirements of the program by tracking dirty
rects through the render pipeline and just updating the dirty info in the
texture. Should help older PPC machines in particular.
Modified Paths:
--------------
trunk/fuse/fusepb/Fuse.xcodeproj/project.pbxproj
trunk/fuse/fusepb/views/DisplayOpenGLView.h
trunk/fuse/fusepb/views/DisplayOpenGLView.m
trunk/fuse/ui/cocoa/cocoadisplay.h
trunk/fuse/ui/cocoa/cocoadisplay.m
Added Paths:
-----------
trunk/fuse/ui/cocoa/dirty.c
trunk/fuse/ui/cocoa/dirty.h
trunk/fuse/ui/cocoa/dirtyrects.txt
Modified: trunk/fuse/fusepb/Fuse.xcodeproj/project.pbxproj
===================================================================
--- trunk/fuse/fusepb/Fuse.xcodeproj/project.pbxproj 2007-06-23 15:20:36 UTC (rev 394)
+++ trunk/fuse/fusepb/Fuse.xcodeproj/project.pbxproj 2007-06-23 16:11:43 UTC (rev 395)
@@ -280,6 +280,8 @@
B6B076B20B59FE9A00D4F95C /* Emulator.h in Headers */ = {isa = PBXBuildFile; fileRef = B6B076B00B59FE9A00D4F95C /* Emulator.h */; };
B6B076B30B59FE9A00D4F95C /* Emulator.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B076B10B59FE9A00D4F95C /* Emulator.m */; };
B6B99F8A0B5F798700EE408F /* cocoadisplay.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B99F890B5F798700EE408F /* cocoadisplay.m */; };
+ B6CA27A70C2CDBC500F06FB3 /* dirty.c in Sources */ = {isa = PBXBuildFile; fileRef = B6CA27A50C2CDBC500F06FB3 /* dirty.c */; };
+ B6CA27A80C2CDBC500F06FB3 /* dirty.h in Headers */ = {isa = PBXBuildFile; fileRef = B6CA27A60C2CDBC500F06FB3 /* dirty.h */; };
B6CE7F410B2830A300EB65B3 /* cocoadisplay.h in Headers */ = {isa = PBXBuildFile; fileRef = B6CE7F3B0B2830A300EB65B3 /* cocoadisplay.h */; };
B6CE7F420B2830A300EB65B3 /* cocoajoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = B6CE7F3C0B2830A300EB65B3 /* cocoajoystick.c */; };
B6CE7F440B2830A300EB65B3 /* cocoaui.h in Headers */ = {isa = PBXBuildFile; fileRef = B6CE7F3E0B2830A300EB65B3 /* cocoaui.h */; };
@@ -514,6 +516,8 @@
B6C86978065611B3003000A6 /* intern.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = intern.h; path = scaler/intern.h; sourceTree = SOURCE_ROOT; };
B6C8B723076D2B1A0007B7B5 /* if1.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = if1.c; path = ../if1.c; sourceTree = SOURCE_ROOT; };
B6C8B724076D2B1A0007B7B5 /* if1.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = if1.h; path = ../if1.h; sourceTree = SOURCE_ROOT; };
+ B6CA27A50C2CDBC500F06FB3 /* dirty.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = dirty.c; sourceTree = "<group>"; };
+ B6CA27A60C2CDBC500F06FB3 /* dirty.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dirty.h; sourceTree = "<group>"; };
B6CA304C049CEC410037E9F2 /* psg.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = psg.c; path = ../psg.c; sourceTree = SOURCE_ROOT; };
B6CA304D049CEC410037E9F2 /* psg.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = psg.h; path = ../psg.h; sourceTree = SOURCE_ROOT; };
B6CC82FF0800E408006EFFB9 /* CAMachines.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CAMachines.h; path = content_arrays/CAMachines.h; sourceTree = SOURCE_ROOT; };
@@ -959,6 +963,8 @@
B67DC2180B63835100FA31B6 /* cocoastatusbar.m */,
B6CE7F3E0B2830A300EB65B3 /* cocoaui.h */,
B6A6F0D90B3D141B000B88E9 /* cocoaui.m */,
+ B6CA27A50C2CDBC500F06FB3 /* dirty.c */,
+ B6CA27A60C2CDBC500F06FB3 /* dirty.h */,
B6E0252B0B38AFE500E23A0F /* keysyms.m */,
);
path = cocoa;
@@ -1278,6 +1284,7 @@
B6B076B20B59FE9A00D4F95C /* Emulator.h in Headers */,
B65352DB0B8CF6780083F942 /* SDL_joystick.h in Headers */,
B65352F30B8CF6CC0083F942 /* SDL_sysjoystick.h in Headers */,
+ B6CA27A80C2CDBC500F06FB3 /* dirty.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1519,6 +1526,7 @@
B67DC2190B63835100FA31B6 /* cocoastatusbar.m in Sources */,
B65352F20B8CF6CC0083F942 /* SDL_sysjoystick.c in Sources */,
B65353150B8FF3D20083F942 /* SDL_joystick.c in Sources */,
+ B6CA27A70C2CDBC500F06FB3 /* dirty.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Modified: trunk/fuse/fusepb/views/DisplayOpenGLView.h
===================================================================
--- trunk/fuse/fusepb/views/DisplayOpenGLView.h 2007-06-23 15:20:36 UTC (rev 394)
+++ trunk/fuse/fusepb/views/DisplayOpenGLView.h 2007-06-23 16:11:43 UTC (rev 395)
@@ -42,8 +42,8 @@
@interface DisplayOpenGLView : NSOpenGLView
{
- /* Need texture size and dimensions and two backing textures */
- Cocoa_Texture screenTex[MAX_SCREEN_BUFFERS]; /* Screen texture */
+ /* Two backing textures */
+ Cocoa_Texture screenTex[MAX_SCREEN_BUFFERS];
GLuint screenTexId[MAX_SCREEN_BUFFERS];
int currentScreenTex;
Modified: trunk/fuse/fusepb/views/DisplayOpenGLView.m
===================================================================
--- trunk/fuse/fusepb/views/DisplayOpenGLView.m 2007-06-23 15:20:36 UTC (rev 394)
+++ trunk/fuse/fusepb/views/DisplayOpenGLView.m 2007-06-23 16:11:43 UTC (rev 395)
@@ -33,6 +33,7 @@
#include "fuse.h"
#include "fusepb/main.h"
#include "settings.h"
+#include "ui/cocoa/dirty.h"
unsigned char *
NSBitmapImageRepToRGBAPixelArray(NSBitmapImageRep * bitmap, int red)
@@ -383,19 +384,33 @@
-(void) drawRect:(NSRect)aRect
{
+ int i;
+ PIG_dirtytable *workdirty = NULL;
+
if( NO == screenTexInitialised ) return;
+ [buffered_screen_lock lock];
+ if( screenTex[currentScreenTex].dirty )
+ pig_dirty_copy( &workdirty, screenTex[currentScreenTex].dirty );
+
currentScreenTex = !currentScreenTex;
- /* Need to draw texture to screen here */
- /* FIXME: lock screen - direct lock probably faster [emulation lockScreen]; */
- [buffered_screen_lock lock];
- /* should draw directly from emulation screen instead of screenTex, switch between
- buffers there */
- memcpy( screenTex[currentScreenTex].pixels, buffered_screen.pixels, screenTex[currentScreenTex].pitch * screenTex[currentScreenTex].full_height );
- /* FIXME: unlock screen - direct lock probably faster [emulation unlockScreen]; */
- [buffered_screen_lock unlock];
+ pig_dirty_copy( &screenTex[currentScreenTex].dirty, buffered_screen.dirty );
+ if( workdirty )
+ pig_dirty_merge(workdirty, screenTex[currentScreenTex].dirty);
+ else
+ pig_dirty_copy(&workdirty, screenTex[currentScreenTex].dirty);
+
+ /* Draw texture to screen */
+ for(i = 0; i < workdirty->count; ++i)
+ copy_area( &screenTex[currentScreenTex], &buffered_screen,
+ workdirty->rects + i );
+
+ buffered_screen.dirty->count = 0;
+
+ pig_dirty_close( workdirty );
+
[[self openGLContext] makeCurrentContext];
glClear( GL_COLOR_BUFFER_BIT );
@@ -427,6 +442,8 @@
/* Swap buffer to screen */
[[self openGLContext] flushBuffer];
+
+ [buffered_screen_lock unlock];
}
/* want to keep image in the original aspect ratio in the face of window resizing */
@@ -496,12 +513,15 @@
GLuint i;
if( screenTexInitialised == NO) return;
- /* FIXME: May want to have a double buffered texture later */
+
glDeleteTextures( MAX_SCREEN_BUFFERS, screenTexId );
for(i = 0; i < MAX_SCREEN_BUFFERS; i++)
{
free( screenTex[i].pixels );
screenTex[i].pixels = NULL;
+ if(screenTex[i].dirty)
+ pig_dirty_close( screenTex[i].dirty );
+ screenTex[i].dirty = NULL;
}
screenTexInitialised = NO;
}
@@ -513,7 +533,6 @@
[[self openGLContext] makeCurrentContext];
[[self openGLContext] update];
- /* FIXME: May want to have a double buffered texture later */
glGenTextures( MAX_SCREEN_BUFFERS, screenTexId );
for(i = 0; i < MAX_SCREEN_BUFFERS; i++)
Modified: trunk/fuse/ui/cocoa/cocoadisplay.h
===================================================================
--- trunk/fuse/ui/cocoa/cocoadisplay.h 2007-06-23 15:20:36 UTC (rev 394)
+++ trunk/fuse/ui/cocoa/cocoadisplay.h 2007-06-23 16:11:43 UTC (rev 395)
@@ -30,9 +30,11 @@
#import <Foundation/NSLock.h>
#include "ui/ui.h"
+#include "dirty.h"
typedef struct Cocoa_Texture {
void *pixels;
+ PIG_dirtytable *dirty;
int full_height;
int full_width;
int image_height;
@@ -50,4 +52,6 @@
void cocoadisplay_display_updated( void );
+void copy_area( Cocoa_Texture *dest_screen, Cocoa_Texture *src_screen, PIG_rect *r );
+
#endif /* #ifndef FUSE_COCOADISPLAY_H */
Modified: trunk/fuse/ui/cocoa/cocoadisplay.m
===================================================================
--- trunk/fuse/ui/cocoa/cocoadisplay.m 2007-06-23 15:20:36 UTC (rev 394)
+++ trunk/fuse/ui/cocoa/cocoadisplay.m 2007-06-23 16:11:43 UTC (rev 395)
@@ -36,6 +36,7 @@
#import "DisplayOpenGLView.h"
#include "cocoadisplay.h"
+#include "dirty.h"
#include "display.h"
#include "fuse.h"
#include "machine.h"
@@ -92,6 +93,10 @@
static int display_updated = 0;
+/* This is a rule of thumb for the maximum number of rects that can be updated
+ each frame. */
+#define MAX_UPDATE_RECT 300
+
static void
init_scalers( void )
{
@@ -138,12 +143,20 @@
screen->full_height = screen->image_height+3;
screen->image_yoffset = 1;
- screen->pixels = calloc( screen->full_width*screen->full_height, sizeof(uint16_t) );
+ screen->pixels = calloc( screen->full_width*screen->full_height,
+ sizeof(uint16_t) );
if( !screen->pixels ) {
fprintf( stderr, "%s: couldn't allocate screen.pixels\n", fuse_progname );
return 1;
}
+ screen->dirty = pig_dirty_open( MAX_UPDATE_RECT );
+ if( !screen->dirty ) {
+ free( screen->pixels );
+ fprintf( stderr, "%s: couldn't allocate screen.dirty\n", fuse_progname );
+ return 1;
+ }
+
screen->pitch = screen->full_width * sizeof(uint16_t);
return 0;
@@ -154,8 +167,12 @@
{
if( screen->pixels ) {
free( screen->pixels );
- screen->pixels=NULL;
+ screen->pixels = NULL;
}
+ if( screen->dirty ) {
+ pig_dirty_close( screen->dirty );
+ screen->dirty = NULL;
+ }
}
static int
@@ -182,6 +199,9 @@
screen->image_width, 1.0f );
if( error ) return error;
+ /* Destroy any existing OpenGL textures (and their dirty lists) */
+ [[DisplayOpenGLView instance] destroyTexture];
+
/* Create OpenGL textures for the image in DisplayOpenGLView */
[[DisplayOpenGLView instance] createTexture:&buffered_screen];
@@ -388,44 +408,82 @@
}
void
+copy_area( Cocoa_Texture *dest_screen, Cocoa_Texture *src_screen, PIG_rect *r )
+{
+ int y;
+
+ for( y = r->y; y <= r->y + r->h; y++ ) {
+ int src_offset = (y + src_screen->image_yoffset) * src_screen->pitch +
+ sizeof(uint16_t) * ( r->x + src_screen->image_xoffset);
+ int dest_offset = (y + dest_screen->image_yoffset) * dest_screen->pitch +
+ sizeof(uint16_t) * ( r->x + dest_screen->image_xoffset);
+ memcpy( dest_screen->pixels + dest_offset, src_screen->pixels + src_offset,
+ r->w * sizeof(uint16_t) );
+ }
+}
+
+void
uidisplay_frame_end( void )
{
- if( display_updated )
+ int i;
+
+ if( display_updated ) {
/* obtain lock for buffered screen */
[buffered_screen_lock lock];
+
/* copy screen data to buffered screen */
- memcpy( buffered_screen.pixels, screen->pixels, screen->pitch * screen->full_height );
+ for(i = 0; i < screen->dirty->count; ++i)
+ copy_area( &buffered_screen, screen, screen->dirty->rects + i );
+
+ pig_dirty_merge( buffered_screen.dirty, screen->dirty );
+
+ /* We usually can't call AppKit stuff from other threads, but we seem to be
+ able to call setNeedsDisplay as long as we are not in drawRect: in the
+ main thread - prefer this to sending a message to the main thread as we
+ can send loads of messages when we are fastloading and can clog up the
+ pipes, the buffered_screen_lock should protect us from drawRect: */
+ [[DisplayOpenGLView instance] setNeedsDisplay:YES];
+
/* release lock for buffered screen */
[buffered_screen_lock unlock];
- [[DisplayOpenGLView instance]
- performSelectorOnMainThread:@selector(setNeedsDisplayYes)
- withObject:nil
- waitUntilDone:NO
- ];
- display_updated = 0;
+
+ display_updated = 0;
+ unscaled_screen.dirty->count = 0;
+ if( current_scaler != SCALER_NORMAL ) scaled_screen.dirty->count = 0;
+ }
}
void
uidisplay_area( int x, int y, int width, int height )
{
- int scaled_x, scaled_y;
+ PIG_rect r = { x, y, width, height };
- display_updated=1;
+ display_updated = 1;
- if( current_scaler == SCALER_NORMAL ) return;
+ if( current_scaler == SCALER_NORMAL ) {
+ pig_dirty_add( unscaled_screen.dirty, &r );
+ return;
+ }
/* Extend the dirty region by 1 pixel for scalers that "smear" the screen,
e.g. 2xSAI */
if( scaler_flags & SCALER_FLAGS_EXPAND )
scaler_expander( &x, &y, &width, &height, image_width, image_height );
- scaled_x = display_current_size * x;
- scaled_y = display_current_size * y;
+ r.x = display_current_size * x;
+ r.y = display_current_size * y;
+ r.w = display_current_size * width;
+ r.h = display_current_size * height;
+ pig_dirty_add( scaled_screen.dirty, &r );
/* Create scaled image */
- scaler_proc16( unscaled_screen.pixels + ( y + 1 ) * unscaled_screen.pitch + sizeof(uint16_t) * ( x + 1 ),
+ scaler_proc16( unscaled_screen.pixels + ( y + unscaled_screen.image_yoffset ) *
+ unscaled_screen.pitch + sizeof(uint16_t) *
+ ( x + unscaled_screen.image_xoffset ),
unscaled_screen.pitch,
- scaled_screen.pixels + scaled_y * scaled_screen.pitch + sizeof(uint16_t) * scaled_x,
+ scaled_screen.pixels + ( r.y + scaled_screen.image_yoffset ) *
+ scaled_screen.pitch + sizeof(uint16_t) *
+ ( r.x + scaled_screen.image_xoffset ),
scaled_screen.pitch, width, height );
}
@@ -448,5 +506,5 @@
void
cocoadisplay_display_updated( void )
{
- display_updated=1;
+ display_updated = 1;
}
Added: trunk/fuse/ui/cocoa/dirty.c
===================================================================
--- trunk/fuse/ui/cocoa/dirty.c (rev 0)
+++ trunk/fuse/ui/cocoa/dirty.c 2007-06-23 16:11:43 UTC (rev 395)
@@ -0,0 +1,188 @@
+/*
+------------------------------------------------------------
+ Fixed Rate Pig - a fixed logic frame rate demo
+------------------------------------------------------------
+ * Copyright (C) 2004 David Olofson <da...@ol...>
+ *
+ * This software is released under the terms of the GPL.
+ *
+ * Contact author for permission if you want to use this
+ * software, or work derived from it, under other terms.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "dirty.h"
+
+/* Approximate worth of one dirtyrect in pixels. */
+#define PIG_WORST_MERGE 300
+
+/*
+ * If the merged result gets at most this many percent
+ * bigger than the larger of the two input rects,
+ * accept it as Perfect.
+ */
+#define PIG_INSTANT_MERGE 10
+
+
+PIG_dirtytable *pig_dirty_open(int size)
+{
+ PIG_dirtytable *pdt = (PIG_dirtytable *)malloc(sizeof(PIG_dirtytable));
+ if(!pdt)
+ return NULL;
+
+ pdt->size = size;
+ pdt->rects = (PIG_rect *)calloc(size, sizeof(PIG_rect));
+ if(!pdt->rects)
+ {
+ free(pdt);
+ return NULL;
+ }
+
+ pdt->count = 0;
+ pdt->best = 0;
+ return pdt;
+}
+
+
+void pig_dirty_close(PIG_dirtytable *pdt)
+{
+ free(pdt->rects);
+ free(pdt);
+}
+
+
+void pig_mergerect(PIG_rect *from, PIG_rect *to)
+{
+ int x1 = from->x;
+ int y1 = from->y;
+ int x2 = from->x + from->w;
+ int y2 = from->y + from->h;
+ if(to->x < x1)
+ x1 = to->x;
+ if(to->y < y1)
+ y1 = to->y;
+ if(to->x + to->w > x2)
+ x2 = to->x + to->w;
+ if(to->y + to->h > y2)
+ y2 = to->y + to->h;
+ to->x = x1;
+ to->y = y1;
+ to->w = x2 - x1;
+ to->h = y2 - y1;
+}
+
+
+void pig_intersectrect(PIG_rect *from, PIG_rect *to)
+{
+ int Amin, Amax, Bmin, Bmax;
+ Amin = to->x;
+ Amax = Amin + to->w;
+ Bmin = from->x;
+ Bmax = Bmin + from->w;
+ if(Bmin > Amin)
+ Amin = Bmin;
+ to->x = Amin;
+ if(Bmax < Amax)
+ Amax = Bmax;
+ to->w = Amax - Amin > 0 ? Amax - Amin : 0;
+
+ Amin = to->y;
+ Amax = Amin + to->h;
+ Bmin = from->y;
+ Bmax = Bmin + from->h;
+ if(Bmin > Amin)
+ Amin = Bmin;
+ to->y = Amin;
+ if(Bmax < Amax)
+ Amax = Bmax;
+ to->h = Amax - Amin > 0 ? Amax - Amin : 0;
+}
+
+
+void pig_dirty_add(PIG_dirtytable *pdt, PIG_rect *dr)
+{
+ int i, j, best_i, best_loss;
+ /*
+ * Look for merger candidates.
+ *
+ * We start right before the best match we
+ * had the last time around. This can give
+ * us large numbers of direct or quick hits
+ * when dealing with old/new rects for moving
+ * objects and the like.
+ */
+ best_i = -1;
+ best_loss = 100000000;
+ if(pdt->count)
+ i = (pdt->best + pdt->count - 1) % pdt->count;
+ for(j = 0; j < pdt->count; ++j)
+ {
+ int a1, a2, am, ratio, loss;
+ PIG_rect testr;
+
+ a1 = dr->w * dr->h;
+
+ testr = pdt->rects[i];
+ a2 = testr.w * testr.h;
+
+ pig_mergerect(dr, &testr);
+ am = testr.w * testr.h;
+
+ /* Perfect or Instant Pick? */
+ ratio = 100 * am / (a1 > a2 ? a1 : a2);
+ if(ratio < PIG_INSTANT_MERGE)
+ {
+ /* Ok, this is good enough! Stop searching. */
+ pig_mergerect(dr, &pdt->rects[i]);
+ pdt->best = i;
+ return;
+ }
+
+ loss = am - a1 - a2;
+ if(loss < best_loss)
+ {
+ best_i = i;
+ best_loss = loss;
+ pdt->best = i;
+ }
+
+ ++i;
+ i %= pdt->count;
+ }
+ /* ...and if the best result is good enough, merge! */
+ if((best_i >= 0) && (best_loss < PIG_WORST_MERGE))
+ {
+ pig_mergerect(dr, &pdt->rects[best_i]);
+ return;
+ }
+
+ /* Try to add to table... */
+ if(pdt->count < pdt->size)
+ {
+ pdt->rects[pdt->count++] = *dr;
+ return;
+ }
+
+ /* Emergency: Table full! Grab best candidate... */
+ pig_mergerect(dr, &pdt->rects[best_i]);
+}
+
+
+void pig_dirty_copy(PIG_dirtytable **pdt, PIG_dirtytable *from)
+{
+ if(*pdt) pig_dirty_close(*pdt);
+ *pdt = pig_dirty_open(from->size);
+ if(*pdt) {
+ memcpy((*pdt)->rects, from->rects, from->count * sizeof(PIG_rect));
+ (*pdt)->count = from->count;
+ }
+}
+
+
+void pig_dirty_merge(PIG_dirtytable *pdt, PIG_dirtytable *from)
+{
+ int i;
+ for(i = 0; i < from->count; ++i)
+ pig_dirty_add(pdt, from->rects + i);
+}
Added: trunk/fuse/ui/cocoa/dirty.h
===================================================================
--- trunk/fuse/ui/cocoa/dirty.h (rev 0)
+++ trunk/fuse/ui/cocoa/dirty.h 2007-06-23 16:11:43 UTC (rev 395)
@@ -0,0 +1,52 @@
+/*
+------------------------------------------------------------
+ Fixed Rate Pig - a fixed logic frame rate demo
+------------------------------------------------------------
+ * Copyright (C) 2004 David Olofson <da...@ol...>
+ *
+ * This software is released under the terms of the GPL.
+ *
+ * Contact author for permission if you want to use this
+ * software, or work derived from it, under other terms.
+ */
+
+#ifndef PIG_DIRTY_H
+#define PIG_DIRTY_H
+
+typedef struct PIG_rect
+{
+ int x;
+ int y;
+ int w;
+ int h;
+} PIG_rect;
+
+/* A table of dirtyrects for one display page */
+typedef struct PIG_dirtytable
+{
+ int size; /* Table size */
+ PIG_rect *rects; /* Table of rects */
+ int count; /* # of rects currently used */
+ int best; /* Merge testing starts here! */
+} PIG_dirtytable;
+
+
+PIG_dirtytable *pig_dirty_open(int size);
+void pig_dirty_close(PIG_dirtytable *pdt);
+
+/* Add rectangle 'dr' to table 'pdt' */
+void pig_dirty_add(PIG_dirtytable *pdt, PIG_rect *dr);
+
+/* Copy table 'from' into 'pdt' */
+void pig_dirty_copy(PIG_dirtytable **pdt, PIG_dirtytable *from);
+
+/* Merge table 'from' into 'pdt' */
+void pig_dirty_merge(PIG_dirtytable *pdt, PIG_dirtytable *from);
+
+/* Extend 'to' to a new rect that includes both 'from' and 'to' */
+void pig_mergerect(PIG_rect *from, PIG_rect *to);
+
+/* Clip 'to' into a rect that is the intersection of 'from' and 'to' */
+void pig_intersectrect(PIG_rect *from, PIG_rect *to);
+
+#endif /* PIG_DIRTY_H */
Added: trunk/fuse/ui/cocoa/dirtyrects.txt
===================================================================
--- trunk/fuse/ui/cocoa/dirtyrects.txt (rev 0)
+++ trunk/fuse/ui/cocoa/dirtyrects.txt 2007-06-23 16:11:43 UTC (rev 395)
@@ -0,0 +1,83 @@
+
+ Smart Dirty Rectangle Management
+ --------------------------------
+
+pig_dirty() contains an algorithm that tries to find
+the the best dirtyrect candidates for merging. While
+searching, it looks out for perfect or sufficiently
+good candidates.
+
+(Perfect candidate:)
+ The merged rectangle is of the same size
+ as the largest of the two input rectangles:
+
+ Amerged <= MAX(A1, A2)
+
+ We don't actually test for this, but rather for...
+
+Instant Pick candidate:
+ Not Perfect, but good enough to be treated
+ as such, considering the cost of going on
+ searching for a better candidate:
+ Amerged 100 / MAX(A1, A2) - 100 <= PIG_INSTANT_MERGE
+
+ (That is, the area of the merged rect must be
+ no more than PIG_INSTANT_MERGE % bigger than
+ the area of the larger of the two input rects.)
+
+ Note that this is also about how likely it is
+ that thereis* a better candidate. Assuming
+ that PIG_INSTANT_MERGE is set to a sensible
+ value, it is not very likely at all. There
+ would have to be another dirtyrect nearby, and
+ the chance of that being a better match is
+ rather low, since that would most likely have
+ caused it to be merged with the tested dirtyrect
+ long before our new rect came in.
+
+(Good candidate:)
+ The area of the merged rectangle is smaller
+ than the total area of the two input rectangles:
+
+ (Amerged - A1 - A2) < 0
+
+ We don't actually test for this, but rather for...
+
+Acceptable candidate:
+ The area of the merged rectangle is larger
+ than the total of the two input rectangles, but
+ since there is some per-rectangle overhead,
+ merging is still a win:
+
+ (Amerged - A1 - A2) <= PIG_WORST_MERGE
+
+ The default setting assumes that the cost of a
+ rectangle is in the range of 300 pixels. One
+ should probably benchmark a few different systems
+ to see if that's reasonable.
+
+Unacceptable candidate:
+ The area of the merged rectangle is larger than
+ the total of the input rectangles to the degree
+ that merging is a definite loss:
+
+ (Amerged - A1 - A2) > PIG_WORST_MERGE
+
+The algorithm instantly returns Perfect candidates as
+solutions. If there are only Good and Acceptable
+candidates, the best one (lowest number of wasted
+pixels) is the solution.
+
+If there are only Unacceptable candidates, there is no
+sensible merger solution, so pig_dirty() will try to add
+a new dirtyrect.
+
+If that fails (table full), the best candidate is used,
+even though it would have been Unacceptable under normal
+circumstances.
+
+
+TODO: Thereare* more alternatives than just "merge" or
+TODO: "don't merge"! For example, it might pay off to
+TODO: detect overlapping and clip dirtyrects to avoid
+TODO: it, when merging is not a viable option.
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|