Revision f28fffc2
Added by Leszek Koltunski almost 8 years ago
src/main/java/org/distorted/library/DistortedNode.java | ||
---|---|---|
40 | 40 |
private static HashMap<ArrayList<Long>,NodeData> mMapNodeID = new HashMap<>(); |
41 | 41 |
private static long mNextNodeID =0; |
42 | 42 |
|
43 |
private DistortedNode mParent; |
|
43 | 44 |
private MeshObject mMesh; |
44 | 45 |
private DistortedEffects mEffects; |
45 | 46 |
private DistortedInputSurface mSurface; |
... | ... | |
109 | 110 |
|
110 | 111 |
for(i=0; i<depth; i++) tmp +=" "; |
111 | 112 |
tmp += ("NodeID="+mData.ID+" nodes pointing: "+mData.numPointingNodes+" surfaceID="+ |
112 |
mSurface.getID()+" FBO="+(mData.mFBO==null ? "null":mData.mFBO.getID())); |
|
113 |
mSurface.getID()+" FBO="+(mData.mFBO==null ? "null":mData.mFBO.getID()))+ |
|
114 |
" parent sID="+(mParent==null ? "null": (mParent.mSurface.getID())); |
|
113 | 115 |
|
114 | 116 |
android.util.Log.e("NODE", tmp); |
115 | 117 |
|
... | ... | |
133 | 135 |
} |
134 | 136 |
|
135 | 137 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
138 |
// tree isomorphism algorithm |
|
136 | 139 |
|
137 |
void treeIsomorphism()
|
|
140 |
private void adjustIsomorphism()
|
|
138 | 141 |
{ |
139 |
for(int i=0; i<mNumChildren[0]; i++) |
|
140 |
{ |
|
141 |
mChildren.get(i).treeIsomorphism(); |
|
142 |
} |
|
143 |
|
|
144 | 142 |
ArrayList<Long> newList = generateIDList(); |
145 | 143 |
NodeData newData = mMapNodeID.get(newList); |
146 | 144 |
|
147 |
if( newData==null ) |
|
145 |
if( newData!=null ) |
|
146 |
{ |
|
147 |
newData.numPointingNodes++; |
|
148 |
} |
|
149 |
else |
|
148 | 150 |
{ |
149 |
android.util.Log.d("NODE", "list "+newList+" not found!! node surfaceID="+mSurface.getID()); |
|
150 |
|
|
151 | 151 |
newData = new NodeData(++mNextNodeID,newList); |
152 | 152 |
mMapNodeID.put(newList,newData); |
153 | 153 |
} |
154 |
else if( newData.ID != mData.ID ) |
|
155 |
{ |
|
156 |
android.util.Log.d("NODE", "list "+newList+" found!! node surfaceID="+mSurface.getID()); |
|
157 | 154 |
|
158 |
newData.numPointingNodes++;
|
|
159 |
}
|
|
155 |
boolean deleteOldFBO = false;
|
|
156 |
boolean createNewFBO = false;
|
|
160 | 157 |
|
161 |
if( newData.ID != mData.ID )
|
|
158 |
if( --mData.numPointingNodes==0 )
|
|
162 | 159 |
{ |
163 |
boolean fboUsed = false; |
|
164 |
|
|
165 |
if( mNumChildren[0]>0 && newData.mFBO==null ) |
|
166 |
{ |
|
167 |
if( mData.mFBO!=null ) |
|
168 |
{ |
|
169 |
if( mData.numPointingNodes>1 ) |
|
170 |
{ |
|
171 |
android.util.Log.d("NODE", "creating1 new FBO of node surfaceID="+mSurface.getID()); |
|
172 |
newData.mFBO = new DistortedFramebuffer(true,DistortedSurface.TYPE_TREE,mSurface.getWidth(),mSurface.getHeight()); |
|
173 |
} |
|
174 |
else |
|
175 |
{ |
|
176 |
android.util.Log.d("NODE", "copying over FBO of node surfaceID="+mSurface.getID()); |
|
177 |
newData.mFBO = mData.mFBO; |
|
178 |
fboUsed = true; |
|
179 |
} |
|
180 |
} |
|
181 |
else |
|
182 |
{ |
|
183 |
android.util.Log.d("NODE", "creating2 new FBO of node surfaceID="+mSurface.getID()); |
|
184 |
newData.mFBO = new DistortedFramebuffer(true,DistortedSurface.TYPE_TREE,mSurface.getWidth(),mSurface.getHeight()); |
|
185 |
} |
|
186 |
} |
|
187 |
if( mNumChildren[0]==0 && newData.mFBO!=null ) |
|
188 |
{ |
|
189 |
android.util.Log.d("NODE", "deleting FBO of newData node!!"); |
|
190 |
newData.mFBO.markForDeletion(); |
|
191 |
newData.mFBO = null; |
|
192 |
} |
|
193 |
|
|
194 |
if( --mData.numPointingNodes==0 ) |
|
195 |
{ |
|
196 |
android.util.Log.d("NODE", "deleting1 map key "+mData.key); |
|
197 |
|
|
198 |
mMapNodeID.remove(mData.key); |
|
160 |
mMapNodeID.remove(mData.key); |
|
161 |
if( mData.mFBO!=null ) deleteOldFBO=true; |
|
162 |
} |
|
163 |
if( mNumChildren[0]>0 && newData.mFBO==null ) |
|
164 |
{ |
|
165 |
createNewFBO = true; |
|
166 |
} |
|
167 |
if( mNumChildren[0]==0 && newData.mFBO!=null ) |
|
168 |
{ |
|
169 |
newData.mFBO.markForDeletion(); |
|
170 |
android.util.Log.d("NODE", "ERROR!! this NodeData cannot possibly contain a non-null FBO!! "+newData.mFBO.getID() ); |
|
171 |
newData.mFBO = null; |
|
172 |
} |
|
199 | 173 |
|
200 |
if( !fboUsed && mData.mFBO!=null ) |
|
201 |
{ |
|
202 |
android.util.Log.d("NODE", "deleting FBO of node surfaceID="+mSurface.getID()); |
|
174 |
if( deleteOldFBO && createNewFBO ) |
|
175 |
{ |
|
176 |
newData.mFBO = mData.mFBO; // just copy over |
|
177 |
android.util.Log.d("NODE", "copying over FBOs "+mData.mFBO.getID() ); |
|
178 |
} |
|
179 |
else if( deleteOldFBO ) |
|
180 |
{ |
|
181 |
mData.mFBO.markForDeletion(); |
|
182 |
android.util.Log.d("NODE", "deleting old FBO "+mData.mFBO.getID() ); |
|
183 |
mData.mFBO = null; |
|
184 |
} |
|
185 |
else if( createNewFBO ) |
|
186 |
{ |
|
187 |
newData.mFBO = new DistortedFramebuffer(true, DistortedSurface.TYPE_TREE, mSurface.getWidth(),mSurface.getHeight()); |
|
188 |
android.util.Log.d("NODE", "creating new FBO "+newData.mFBO.getID() ); |
|
189 |
} |
|
203 | 190 |
|
204 |
mData.mFBO.markForDeletion(); |
|
205 |
mData.mFBO = null; |
|
206 |
} |
|
207 |
} |
|
191 |
mData = newData; |
|
208 | 192 |
|
209 |
mData = newData; |
|
210 |
} |
|
193 |
if( mParent!=null ) mParent.adjustIsomorphism(); |
|
211 | 194 |
} |
212 | 195 |
|
213 | 196 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
... | ... | |
268 | 251 |
mChildren = null; |
269 | 252 |
mNumChildren = new int[1]; |
270 | 253 |
mNumChildren[0]= 0; |
271 |
|
|
254 |
mParent = null; |
|
255 |
|
|
272 | 256 |
ArrayList<Long> list = new ArrayList<>(); |
273 | 257 |
list.add(mSurface.getID()); |
274 | 258 |
list.add(-mEffects.getID()); |
... | ... | |
299 | 283 |
public DistortedNode(DistortedNode node, int flags) |
300 | 284 |
{ |
301 | 285 |
mEffects= new DistortedEffects(node.mEffects,flags); |
302 |
mMesh = node.mMesh; |
|
286 |
mMesh = node.mMesh; |
|
287 |
mParent = null; |
|
303 | 288 |
|
304 | 289 |
if( (flags & Distorted.CLONE_SURFACE) != 0 ) |
305 | 290 |
{ |
... | ... | |
397 | 382 |
{ |
398 | 383 |
if( mChildren==null ) mChildren = new ArrayList<>(2); |
399 | 384 |
|
385 |
node.mParent = this; |
|
400 | 386 |
mChildren.add(node); |
401 | 387 |
mNumChildren[0]++; |
388 |
adjustIsomorphism(); |
|
402 | 389 |
} |
403 | 390 |
|
404 | 391 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
... | ... | |
456 | 443 |
{ |
457 | 444 |
if( mNumChildren[0]>0 && mChildren.remove(node) ) |
458 | 445 |
{ |
446 |
node.mParent = null; |
|
459 | 447 |
mNumChildren[0]--; |
460 |
|
|
461 |
if( mNumChildren[0]==0 && mData.mFBO!=null ) |
|
462 |
{ |
|
463 |
if( --mData.numPointingNodes==0 ) |
|
464 |
{ |
|
465 |
mData.mFBO.markForDeletion(); |
|
466 |
android.util.Log.d("NODE", "deleting2 map key "+mData.key); |
|
467 |
|
|
468 |
mMapNodeID.remove(mData.key); |
|
469 |
} |
|
470 |
mData.mFBO = null; |
|
471 |
} |
|
448 |
adjustIsomorphism(); |
|
472 | 449 |
} |
473 | 450 |
} |
474 | 451 |
|
... | ... | |
494 | 471 |
{ |
495 | 472 |
if( mNumChildren[0]>0 ) |
496 | 473 |
{ |
497 |
mNumChildren[0] = 0; |
|
498 |
mChildren.clear(); |
|
474 |
DistortedNode tmp; |
|
499 | 475 |
|
500 |
if( mData.mFBO!=null )
|
|
476 |
for(int i=mNumChildren[0]-1; i>=0; i--)
|
|
501 | 477 |
{ |
502 |
if( --mData.numPointingNodes==0 ) |
|
503 |
{ |
|
504 |
mData.mFBO.markForDeletion(); |
|
505 |
android.util.Log.d("NODE", "deleting3 map key "+mData.key); |
|
506 |
|
|
507 |
mMapNodeID.remove(mData.key); |
|
508 |
} |
|
509 |
mData.mFBO = null; |
|
478 |
tmp = mChildren.remove(i); |
|
479 |
tmp.mParent = null; |
|
510 | 480 |
} |
481 |
|
|
482 |
mNumChildren[0] = 0; |
|
483 |
adjustIsomorphism(); |
|
511 | 484 |
} |
512 | 485 |
} |
513 | 486 |
|
src/main/java/org/distorted/library/DistortedOutputSurface.java | ||
---|---|---|
116 | 116 |
// change tree topology (attach and detach children) |
117 | 117 |
boolean changed = DistortedAttachDaemon.toDo(); |
118 | 118 |
|
119 |
// if some changes have been made, we need to rebuilt our tree-isomorphism data structures.
|
|
119 |
// debugging only
|
|
120 | 120 |
if( changed ) |
121 | 121 |
{ |
122 | 122 |
for(int i=0; i<mNumChildren; i++) |
123 | 123 |
{ |
124 |
mChildren.get(i).treeIsomorphism(); |
|
125 | 124 |
mChildren.get(i).debug(0); |
126 | 125 |
} |
127 | 126 |
|
... | ... | |
135 | 134 |
// to be created immediately, before the calls to drawRecursive() |
136 | 135 |
toDo(); |
137 | 136 |
|
137 |
// debugging only |
|
138 | 138 |
if( changed ) |
139 | 139 |
{ |
140 | 140 |
DistortedSurface.debugLists(); |
... | ... | |
209 | 209 |
|
210 | 210 |
if( mColorCreated==CREATED ) |
211 | 211 |
{ |
212 |
moveToToDo();
|
|
212 |
markForCreation();
|
|
213 | 213 |
recreate(); |
214 | 214 |
} |
215 | 215 |
} |
... | ... | |
227 | 227 |
if( enable && mDepthCreated==DONT_CREATE ) |
228 | 228 |
{ |
229 | 229 |
mDepthCreated = NOT_CREATED_YET; |
230 |
moveToToDo();
|
|
230 |
markForCreation();
|
|
231 | 231 |
} |
232 | 232 |
if( !enable && mDepthCreated!=DONT_CREATE ) |
233 | 233 |
{ |
234 | 234 |
mDepthCreated = DONT_CREATE; |
235 |
moveToToDo();
|
|
235 |
markForCreation();
|
|
236 | 236 |
} |
237 | 237 |
} |
238 | 238 |
|
src/main/java/org/distorted/library/DistortedSurface.java | ||
---|---|---|
19 | 19 |
|
20 | 20 |
package org.distorted.library; |
21 | 21 |
|
22 |
import java.util.HashMap; |
|
22 | 23 |
import java.util.LinkedList; |
23 | 24 |
|
24 | 25 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
... | ... | |
40 | 41 |
static final int TYPE_TREE = 1; |
41 | 42 |
static final int TYPE_SYST = 2; |
42 | 43 |
|
44 |
private static final int JOB_CREATE = 0; |
|
45 |
private static final int JOB_DELETE = 1; |
|
46 |
|
|
47 |
private class Job |
|
48 |
{ |
|
49 |
DistortedSurface surface; |
|
50 |
int action; |
|
51 |
|
|
52 |
Job(DistortedSurface s, int a) |
|
53 |
{ |
|
54 |
surface = s; |
|
55 |
action = a; |
|
56 |
} |
|
57 |
} |
|
58 |
|
|
43 | 59 |
private static boolean mToDo = false; |
44 | 60 |
private static LinkedList<DistortedSurface> mDoneList = new LinkedList<>(); |
45 |
private static LinkedList<DistortedSurface> mToDoList = new LinkedList<>();
|
|
61 |
private static HashMap<Long,Job> mToDoList = new HashMap<>();
|
|
46 | 62 |
private static long mNextClientID = 0; |
47 | 63 |
private static long mNextSystemID = 0; |
48 | 64 |
|
49 | 65 |
private long mID; |
50 |
private boolean mMarked; |
|
51 | 66 |
private int mType; |
52 | 67 |
int mColorCreated; |
53 | 68 |
int[] mColorH = new int[1]; |
... | ... | |
66 | 81 |
{ |
67 | 82 |
if( mToDo ) |
68 | 83 |
{ |
84 |
Job job; |
|
69 | 85 |
DistortedSurface surface; |
70 | 86 |
|
71 |
int num = mToDoList.size(); |
|
72 |
|
|
73 |
for(int i=0; i<num; i++) |
|
87 |
for(Long key: mToDoList.keySet()) |
|
74 | 88 |
{ |
75 |
surface = mToDoList.removeFirst(); |
|
89 |
job = mToDoList.get(key); |
|
90 |
surface = job.surface; |
|
76 | 91 |
|
77 |
if(surface.mMarked) |
|
78 |
{ |
|
79 |
surface.delete(); |
|
80 |
surface.mMarked = false; |
|
81 |
} |
|
82 |
else |
|
92 |
android.util.Log.d("SURFACE", " ---> need to "+(job.action==JOB_CREATE ? "create":"delete")+" surfaceID="+surface.getID() ); |
|
93 |
|
|
94 |
if( job.action==JOB_CREATE ) |
|
83 | 95 |
{ |
84 | 96 |
surface.create(); |
85 | 97 |
mDoneList.add(surface); |
86 | 98 |
} |
99 |
else if( job.action==JOB_DELETE ) |
|
100 |
{ |
|
101 |
surface.delete(); |
|
102 |
} |
|
87 | 103 |
} |
88 | 104 |
|
105 |
mToDoList.clear(); |
|
89 | 106 |
mToDo = false; |
90 | 107 |
} |
91 | 108 |
} |
... | ... | |
94 | 111 |
|
95 | 112 |
static synchronized void onDestroy() |
96 | 113 |
{ |
114 |
Job job; |
|
97 | 115 |
DistortedSurface surface; |
98 | 116 |
|
99 |
int num = mDoneList.size(); |
|
100 |
|
|
101 |
for(int i=0; i<num; i++) |
|
117 |
for(Long key: mToDoList.keySet()) |
|
102 | 118 |
{ |
103 |
surface = mDoneList.removeFirst();
|
|
119 |
job = mToDoList.get(key);
|
|
104 | 120 |
|
105 |
if( surface.mType==TYPE_SYST ) |
|
121 |
if( job.surface.mType==TYPE_SYST )
|
|
106 | 122 |
{ |
107 |
mToDoList.add(surface); |
|
108 |
surface.recreate(); |
|
123 |
mDoneList.add(job.surface); |
|
109 | 124 |
} |
110 | 125 |
} |
111 | 126 |
|
112 |
num = mToDoList.size(); |
|
127 |
mToDoList.clear(); |
|
128 |
|
|
129 |
int num = mDoneList.size(); |
|
113 | 130 |
|
114 | 131 |
for(int i=0; i<num; i++) |
115 | 132 |
{ |
116 |
surface = mToDoList.get(i);
|
|
133 |
surface = mDoneList.removeFirst();
|
|
117 | 134 |
|
118 |
if( surface.mType!=TYPE_SYST )
|
|
135 |
if( surface.mType==TYPE_SYST )
|
|
119 | 136 |
{ |
120 |
mDoneList.remove(i); |
|
121 |
i--; |
|
122 |
num--; |
|
137 |
mToDoList.put(surface.getID(), surface.new Job(surface,JOB_CREATE) ); |
|
138 |
surface.recreate(); |
|
123 | 139 |
} |
124 | 140 |
} |
125 | 141 |
|
126 |
mToDo = (num>0);
|
|
142 |
mToDo = true;
|
|
127 | 143 |
mNextClientID = 0; |
128 | 144 |
} |
129 | 145 |
|
130 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
131 |
|
|
132 |
synchronized void moveToToDo() |
|
133 |
{ |
|
134 |
if ( mDoneList.remove(this) ) |
|
135 |
{ |
|
136 |
mToDoList.add(this); |
|
137 |
mToDo = true; |
|
138 |
} |
|
139 |
} |
|
140 |
|
|
141 | 146 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
142 | 147 |
|
143 | 148 |
@SuppressWarnings("unused") |
144 | 149 |
static void debugLists() |
145 | 150 |
{ |
146 | 151 |
android.util.Log.e("Surface", "Done list:"); |
147 |
debugList(mDoneList); |
|
152 |
|
|
153 |
DistortedSurface surface; |
|
154 |
int num = mDoneList.size(); |
|
155 |
|
|
156 |
for(int i=0; i<num; i++) |
|
157 |
{ |
|
158 |
surface = mDoneList.get(i); |
|
159 |
surface.print(i, ""); |
|
160 |
} |
|
161 |
|
|
148 | 162 |
android.util.Log.e("Surface", "ToDo list:"); |
149 |
debugList(mToDoList); |
|
163 |
|
|
164 |
Job job; |
|
165 |
int i=0; |
|
166 |
|
|
167 |
for(Long key: mToDoList.keySet()) |
|
168 |
{ |
|
169 |
job = mToDoList.get(key); |
|
170 |
job.surface.print(i++, job.action==JOB_CREATE ? " create":" delete"); |
|
171 |
} |
|
150 | 172 |
} |
151 | 173 |
|
152 | 174 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
153 | 175 |
|
154 |
private static void debugList(LinkedList<DistortedSurface> list)
|
|
176 |
private void print(int i, String extra)
|
|
155 | 177 |
{ |
156 |
DistortedSurface surface; |
|
157 | 178 |
String str; |
158 | 179 |
|
159 |
int num = list.size(); |
|
180 |
if( this instanceof DistortedFramebuffer ) str = (i+": Framebuffer "); |
|
181 |
else if( this instanceof DistortedTexture) str = (i+": Texture "); |
|
182 |
else if( this instanceof DistortedScreen ) str = (i+": Screen "); |
|
183 |
else str = (i+": UNKNOWN "); |
|
160 | 184 |
|
161 |
for(int i=0; i<num; i++) |
|
162 |
{ |
|
163 |
surface = list.get(i); |
|
164 |
|
|
165 |
if( surface instanceof DistortedFramebuffer ) str = (i+": Framebuffer "); |
|
166 |
else if( surface instanceof DistortedTexture) str = (i+": Texture "); |
|
167 |
else if( surface instanceof DistortedScreen ) str = (i+": Screen "); |
|
168 |
else str = (i+": UNKNOWN "); |
|
169 |
|
|
170 |
str += ("("+surface.getWidth()+","+surface.getHeight()+") surfaceID:"+surface.getID()); |
|
185 |
str += ("("+getWidth()+","+getHeight()+") surfaceID:"+getID()); |
|
171 | 186 |
|
172 |
switch(surface.mType) |
|
173 |
{ |
|
174 |
case TYPE_SYST: str+=" SYSTEM"; break; |
|
175 |
case TYPE_USER: str+=" USER" ; break; |
|
176 |
case TYPE_TREE: str+=" TREE" ; break; |
|
177 |
default : str+=" ERROR??"; |
|
178 |
} |
|
179 |
|
|
180 |
android.util.Log.e("Surface", str); |
|
187 |
switch(mType) |
|
188 |
{ |
|
189 |
case TYPE_SYST: str+=" SYSTEM"; break; |
|
190 |
case TYPE_USER: str+=" USER" ; break; |
|
191 |
case TYPE_TREE: str+=" TREE" ; break; |
|
192 |
default : str+=" ERROR??"; |
|
181 | 193 |
} |
194 |
|
|
195 |
android.util.Log.e("Surface", str+extra); |
|
182 | 196 |
} |
183 | 197 |
|
184 | 198 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
... | ... | |
189 | 203 |
mSizeY = height; |
190 | 204 |
mColorCreated = create; |
191 | 205 |
mColorH[0] = 0; |
192 |
mMarked = false; |
|
193 | 206 |
mID = type==TYPE_SYST ? --mNextSystemID : ++mNextClientID; |
194 | 207 |
mType = type; |
195 | 208 |
|
196 | 209 |
if( create!=DONT_CREATE ) |
197 | 210 |
{ |
198 |
mToDoList.add(this);
|
|
211 |
mToDoList.put(mID, new Job(this,JOB_CREATE) );
|
|
199 | 212 |
mToDo = true; |
200 | 213 |
} |
201 | 214 |
} |
202 | 215 |
|
216 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
217 |
|
|
218 |
synchronized void markForCreation() |
|
219 |
{ |
|
220 |
mDoneList.remove(this); |
|
221 |
mToDoList.put(mID, new Job(this,JOB_CREATE) ); |
|
222 |
mToDo = true; |
|
223 |
} |
|
224 |
|
|
203 | 225 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
204 | 226 |
// PUBLIC API |
205 | 227 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
206 | 228 |
/** |
207 | 229 |
* Mark the underlying OpenGL object for deletion. Actual deletion will take place on the next render. |
208 | 230 |
*/ |
209 |
public void markForDeletion() |
|
231 |
synchronized public void markForDeletion()
|
|
210 | 232 |
{ |
211 |
if( !mMarked ) |
|
212 |
{ |
|
213 |
mToDo = true; |
|
214 |
mMarked = true; |
|
215 |
mDoneList.remove(this); |
|
216 |
mToDoList.add(this); |
|
217 |
} |
|
233 |
mDoneList.remove(this); |
|
234 |
mToDoList.put(mID, new Job(this,JOB_DELETE) ); |
|
235 |
mToDo = true; |
|
218 | 236 |
} |
219 | 237 |
|
220 | 238 |
//////////////////////////////////////////////////////////////////////////////////////////////////// |
src/main/java/org/distorted/library/DistortedTexture.java | ||
---|---|---|
162 | 162 |
public void setTexture(Bitmap bmp) |
163 | 163 |
{ |
164 | 164 |
mBmp= bmp; |
165 |
moveToToDo();
|
|
165 |
markForCreation();
|
|
166 | 166 |
} |
167 | 167 |
} |
Also available in: Unified diff
A Lot of fixes for the issues uncovered by Olimpic.
Still at least 1 known issue: sometimes, when we re-add a Surface, some garbage pops up on the screen for a brief split second. Visible in Olimpic.