1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.java2d.marlin; 27 28 import java.awt.geom.Path2D; 29 import java.lang.ref.SoftReference; 30 import java.lang.ref.WeakReference; 31 import java.util.concurrent.atomic.AtomicInteger; 32 import static sun.java2d.marlin.ArrayCache.*; 33 import sun.java2d.marlin.MarlinRenderingEngine.NormalizingPathIterator; 34 import static sun.java2d.marlin.MarlinUtils.logInfo; 35 36 /** 37 * This class is a renderer context dedicated to a single thread 38 */ 39 final class RendererContext implements MarlinConst { 40 41 // RendererContext creation counter 42 private static final AtomicInteger contextCount = new AtomicInteger(1); 43 // RendererContext statistics 44 static final RendererStats stats = (doStats || doMonitors) 45 ? RendererStats.getInstance(): null; 46 47 private static final boolean USE_CACHE_HARD_REF = doStats 48 || (MarlinRenderingEngine.REF_TYPE == MarlinRenderingEngine.REF_WEAK); 49 50 /** 51 * Create a new renderer context 52 * 53 * @return new RendererContext instance 54 */ 55 static RendererContext createContext() { 56 final RendererContext newCtx = new RendererContext("ctx" 57 + Integer.toString(contextCount.getAndIncrement())); 58 if (RendererContext.stats != null) { 59 RendererContext.stats.allContexts.add(newCtx); 60 } 61 return newCtx; 62 } 63 64 // context name (debugging purposes) 65 final String name; 66 /* 67 * Reference to this instance (hard, soft or weak). 68 * @see MarlinRenderingEngine#REF_TYPE 69 */ 70 final Object reference; 71 // Smallest object used as Cleaner's parent reference 72 final Object cleanerObj = new Object(); 73 // dirty flag indicating an exception occured during pipeline in pathTo() 74 boolean dirty = false; 75 // dynamic array caches kept using weak reference (low memory footprint) 76 WeakReference<ArrayCachesHolder> refArrayCaches = null; 77 // hard reference to array caches (for statistics) 78 ArrayCachesHolder hardRefArrayCaches = null; 79 // shared data 80 final float[] float6 = new float[6]; 81 // shared curve (dirty) (Renderer / Stroker) 82 final Curve curve = new Curve(); 83 // MarlinRenderingEngine NormalizingPathIterator NearestPixelCenter: 84 final NormalizingPathIterator nPCPathIterator; 85 // MarlinRenderingEngine NearestPixelQuarter NormalizingPathIterator: 86 final NormalizingPathIterator nPQPathIterator; 87 // MarlinRenderingEngine.TransformingPathConsumer2D 88 final TransformingPathConsumer2D transformerPC2D; 89 // recycled Path2D instance 90 Path2D.Float p2d = null; 91 final Renderer renderer; 92 final Stroker stroker; 93 // Simplifies out collinear lines 94 final CollinearSimplifier simplifier = new CollinearSimplifier(); 95 final Dasher dasher; 96 final MarlinTileGenerator ptg; 97 final MarlinCache cache; 98 // flag indicating the shape is stroked (1) or filled (0) 99 int stroking = 0; 100 101 /** 102 * Constructor 103 * 104 * @param name 105 */ 106 RendererContext(final String name) { 107 if (logCreateContext) { 108 MarlinUtils.logInfo("new RendererContext = " + name); 109 } 110 111 this.name = name; 112 113 // NormalizingPathIterator instances: 114 nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(float6); 115 nPQPathIterator = new NormalizingPathIterator.NearestPixelQuarter(float6); 116 117 // MarlinRenderingEngine.TransformingPathConsumer2D 118 transformerPC2D = new TransformingPathConsumer2D(); 119 120 // Renderer: 121 cache = new MarlinCache(this); 122 renderer = new Renderer(this); // needs MarlinCache from rdrCtx.cache 123 ptg = new MarlinTileGenerator(renderer); 124 125 stroker = new Stroker(this); 126 dasher = new Dasher(this); 127 128 // Create the reference to this instance (hard, soft or weak): 129 switch (MarlinRenderingEngine.REF_TYPE) { 130 default: 131 case MarlinRenderingEngine.REF_HARD: 132 reference = this; 133 break; 134 case MarlinRenderingEngine.REF_SOFT: 135 reference = new SoftReference<RendererContext>(this); 136 break; 137 case MarlinRenderingEngine.REF_WEAK: 138 reference = new WeakReference<RendererContext>(this); 139 break; 140 } 141 } 142 143 /** 144 * Disposes this renderer context: 145 * clean up before reusing this context 146 */ 147 void dispose() { 148 stroking = 0; 149 // reset hard reference to array caches if needed: 150 if (!USE_CACHE_HARD_REF) { 151 hardRefArrayCaches = null; 152 } 153 // if context is maked as DIRTY: 154 if (dirty) { 155 // may happen if an exception if thrown in the pipeline processing: 156 // force cleanup of all possible pipelined blocks (except Renderer): 157 158 // NormalizingPathIterator instances: 159 this.nPCPathIterator.dispose(); 160 this.nPQPathIterator.dispose(); 161 // Dasher: 162 this.dasher.dispose(); 163 // Stroker: 164 this.stroker.dispose(); 165 166 // mark context as CLEAN: 167 dirty = false; 168 } 169 } 170 171 // Array caches 172 ArrayCachesHolder getArrayCachesHolder() { 173 // Use hard reference first (cached resolved weak reference): 174 ArrayCachesHolder holder = hardRefArrayCaches; 175 if (holder == null) { 176 // resolve reference: 177 holder = (refArrayCaches != null) 178 ? refArrayCaches.get() 179 : null; 180 // create a new ArrayCachesHolder if none is available 181 if (holder == null) { 182 if (logCreateContext) { 183 MarlinUtils.logInfo("new ArrayCachesHolder for " 184 + "RendererContext = " + name); 185 } 186 187 holder = new ArrayCachesHolder(); 188 189 if (USE_CACHE_HARD_REF) { 190 // update hard reference: 191 hardRefArrayCaches = holder; 192 } else { 193 // update weak reference: 194 refArrayCaches = new WeakReference<ArrayCachesHolder>(holder); 195 } 196 } 197 } 198 return holder; 199 } 200 201 // dirty byte array cache 202 ByteArrayCache getDirtyByteArrayCache(final int length) { 203 final int bucket = ArrayCache.getBucketDirtyBytes(length); 204 return getArrayCachesHolder().dirtyByteArrayCaches[bucket]; 205 } 206 207 byte[] getDirtyByteArray(final int length) { 208 if (length <= MAX_DIRTY_BYTE_ARRAY_SIZE) { 209 return getDirtyByteArrayCache(length).getArray(); 210 } 211 212 if (doStats) { 213 incOversize(); 214 } 215 216 if (doLogOverSize) { 217 logInfo("getDirtyByteArray[oversize]: length=\t" + length); 218 } 219 220 return new byte[length]; 221 } 222 223 void putDirtyByteArray(final byte[] array) { 224 final int length = array.length; 225 // odd sized array are non-cached arrays (initial arrays) 226 // ensure to never store initial arrays in cache: 227 if (((length & 0x1) == 0) && (length <= MAX_DIRTY_BYTE_ARRAY_SIZE)) { 228 getDirtyByteArrayCache(length).putDirtyArray(array, length); 229 } 230 } 231 232 byte[] widenDirtyByteArray(final byte[] in, 233 final int usedSize, final int needSize) 234 { 235 final int length = in.length; 236 if (doChecks && length >= needSize) { 237 return in; 238 } 239 if (doStats) { 240 incResizeDirtyByte(); 241 } 242 243 // maybe change bucket: 244 // ensure getNewSize() > newSize: 245 final byte[] res = getDirtyByteArray(getNewSize(usedSize, needSize)); 246 247 System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements 248 249 // maybe return current array: 250 // NO clean-up of array data = DIRTY ARRAY 251 putDirtyByteArray(in); 252 253 if (doLogWidenArray) { 254 logInfo("widenDirtyByteArray[" + res.length + "]: usedSize=\t" 255 + usedSize + "\tlength=\t" + length + "\tneeded length=\t" 256 + needSize); 257 } 258 return res; 259 } 260 261 // int array cache 262 IntArrayCache getIntArrayCache(final int length) { 263 final int bucket = ArrayCache.getBucket(length); 264 return getArrayCachesHolder().intArrayCaches[bucket]; 265 } 266 267 int[] getIntArray(final int length) { 268 if (length <= MAX_ARRAY_SIZE) { 269 return getIntArrayCache(length).getArray(); 270 } 271 272 if (doStats) { 273 incOversize(); 274 } 275 276 if (doLogOverSize) { 277 logInfo("getIntArray[oversize]: length=\t" + length); 278 } 279 280 return new int[length]; 281 } 282 283 // unused 284 int[] widenIntArray(final int[] in, final int usedSize, 285 final int needSize, final int clearTo) 286 { 287 final int length = in.length; 288 if (doChecks && length >= needSize) { 289 return in; 290 } 291 if (doStats) { 292 incResizeInt(); 293 } 294 295 // maybe change bucket: 296 // ensure getNewSize() > newSize: 297 final int[] res = getIntArray(getNewSize(usedSize, needSize)); 298 299 System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements 300 301 // maybe return current array: 302 putIntArray(in, 0, clearTo); // ensure all array is cleared (grow-reduce algo) 303 304 if (doLogWidenArray) { 305 logInfo("widenIntArray[" + res.length + "]: usedSize=\t" 306 + usedSize + "\tlength=\t" + length + "\tneeded length=\t" 307 + needSize); 308 } 309 return res; 310 } 311 312 void putIntArray(final int[] array, final int fromIndex, 313 final int toIndex) 314 { 315 final int length = array.length; 316 // odd sized array are non-cached arrays (initial arrays) 317 // ensure to never store initial arrays in cache: 318 if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) { 319 getIntArrayCache(length).putArray(array, length, fromIndex, toIndex); 320 } 321 } 322 323 // dirty int array cache 324 IntArrayCache getDirtyIntArrayCache(final int length) { 325 final int bucket = ArrayCache.getBucket(length); 326 return getArrayCachesHolder().dirtyIntArrayCaches[bucket]; 327 } 328 329 int[] getDirtyIntArray(final int length) { 330 if (length <= MAX_ARRAY_SIZE) { 331 return getDirtyIntArrayCache(length).getArray(); 332 } 333 334 if (doStats) { 335 incOversize(); 336 } 337 338 if (doLogOverSize) { 339 logInfo("getDirtyIntArray[oversize]: length=\t" + length); 340 } 341 342 return new int[length]; 343 } 344 345 int[] widenDirtyIntArray(final int[] in, 346 final int usedSize, final int needSize) 347 { 348 final int length = in.length; 349 if (doChecks && length >= needSize) { 350 return in; 351 } 352 if (doStats) { 353 incResizeDirtyInt(); 354 } 355 356 // maybe change bucket: 357 // ensure getNewSize() > newSize: 358 final int[] res = getDirtyIntArray(getNewSize(usedSize, needSize)); 359 360 System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements 361 362 // maybe return current array: 363 // NO clean-up of array data = DIRTY ARRAY 364 putDirtyIntArray(in); 365 366 if (doLogWidenArray) { 367 logInfo("widenDirtyIntArray[" + res.length + "]: usedSize=\t" 368 + usedSize + "\tlength=\t" + length + "\tneeded length=\t" 369 + needSize); 370 } 371 return res; 372 } 373 374 void putDirtyIntArray(final int[] array) { 375 final int length = array.length; 376 // odd sized array are non-cached arrays (initial arrays) 377 // ensure to never store initial arrays in cache: 378 if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) { 379 getDirtyIntArrayCache(length).putDirtyArray(array, length); 380 } 381 } 382 383 // dirty float array cache 384 FloatArrayCache getDirtyFloatArrayCache(final int length) { 385 final int bucket = ArrayCache.getBucket(length); 386 return getArrayCachesHolder().dirtyFloatArrayCaches[bucket]; 387 } 388 389 float[] getDirtyFloatArray(final int length) { 390 if (length <= MAX_ARRAY_SIZE) { 391 return getDirtyFloatArrayCache(length).getArray(); 392 } 393 394 if (doStats) { 395 incOversize(); 396 } 397 398 if (doLogOverSize) { 399 logInfo("getDirtyFloatArray[oversize]: length=\t" + length); 400 } 401 402 return new float[length]; 403 } 404 405 float[] widenDirtyFloatArray(final float[] in, 406 final int usedSize, final int needSize) 407 { 408 final int length = in.length; 409 if (doChecks && length >= needSize) { 410 return in; 411 } 412 if (doStats) { 413 incResizeDirtyFloat(); 414 } 415 416 // maybe change bucket: 417 // ensure getNewSize() > newSize: 418 final float[] res = getDirtyFloatArray(getNewSize(usedSize, needSize)); 419 420 System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements 421 422 // maybe return current array: 423 // NO clean-up of array data = DIRTY ARRAY 424 putDirtyFloatArray(in); 425 426 if (doLogWidenArray) { 427 logInfo("widenDirtyFloatArray[" + res.length + "]: usedSize=\t" 428 + usedSize + "\tlength=\t" + length + "\tneeded length=\t" 429 + needSize); 430 } 431 return res; 432 } 433 434 void putDirtyFloatArray(final float[] array) { 435 final int length = array.length; 436 // odd sized array are non-cached arrays (initial arrays) 437 // ensure to never store initial arrays in cache: 438 if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) { 439 getDirtyFloatArrayCache(length).putDirtyArray(array, length); 440 } 441 } 442 443 /* class holding all array cache instances */ 444 static final class ArrayCachesHolder { 445 // zero-filled int array cache: 446 final IntArrayCache[] intArrayCaches; 447 // dirty array caches: 448 final IntArrayCache[] dirtyIntArrayCaches; 449 final FloatArrayCache[] dirtyFloatArrayCaches; 450 final ByteArrayCache[] dirtyByteArrayCaches; 451 452 ArrayCachesHolder() { 453 intArrayCaches = new IntArrayCache[BUCKETS]; 454 dirtyIntArrayCaches = new IntArrayCache[BUCKETS]; 455 dirtyFloatArrayCaches = new FloatArrayCache[BUCKETS]; 456 dirtyByteArrayCaches = new ByteArrayCache[BUCKETS]; 457 458 for (int i = 0; i < BUCKETS; i++) { 459 intArrayCaches[i] = new IntArrayCache(ARRAY_SIZES[i]); 460 // dirty array caches: 461 dirtyIntArrayCaches[i] = new IntArrayCache(ARRAY_SIZES[i]); 462 dirtyFloatArrayCaches[i] = new FloatArrayCache(ARRAY_SIZES[i]); 463 dirtyByteArrayCaches[i] = new ByteArrayCache(DIRTY_BYTE_ARRAY_SIZES[i]); 464 } 465 } 466 } 467 }