1 /* 2 * Copyright (c) 2015, 2016, 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 * @param storageQueue true = queue storage; false = thread-Local storage 54 * @return new RendererContext instance 55 */ 56 static RendererContext createContext(final boolean storageQueue) { 57 final RendererContext newCtx = new RendererContext("ctx" 58 + Integer.toString(contextCount.getAndIncrement()), 59 storageQueue); 60 61 if (RendererContext.stats != null) { 62 RendererContext.stats.allContexts.add(newCtx); 63 } 64 return newCtx; 65 } 66 67 // context name (debugging purposes) 68 final String name; 69 // true = queue storage; false = thread-Local storage 70 final boolean storageQueue; 71 /* 72 * Used flag indicating this thread-Local context is already used 73 * (used to detect reentrance) 74 */ 75 boolean usedTL = false; 76 /* 77 * Reference to this instance (hard, soft or weak). 78 * @see MarlinRenderingEngine#REF_TYPE 79 */ 80 final Object reference; 81 // Smallest object used as Cleaner's parent reference 82 final Object cleanerObj = new Object(); 83 // dirty flag indicating an exception occured during pipeline in pathTo() 84 boolean dirty = false; 85 // dynamic array caches kept using weak reference (low memory footprint) 86 WeakReference<ArrayCachesHolder> refArrayCaches = null; 87 // hard reference to array caches (for statistics) 88 ArrayCachesHolder hardRefArrayCaches = null; 89 // shared data 90 final float[] float6 = new float[6]; 91 // shared curve (dirty) (Renderer / Stroker) 92 final Curve curve = new Curve(); 93 // MarlinRenderingEngine NormalizingPathIterator NearestPixelCenter: 94 final NormalizingPathIterator nPCPathIterator; 95 // MarlinRenderingEngine NearestPixelQuarter NormalizingPathIterator: 96 final NormalizingPathIterator nPQPathIterator; 97 // MarlinRenderingEngine.TransformingPathConsumer2D 98 final TransformingPathConsumer2D transformerPC2D; 99 // recycled Path2D instance 100 Path2D.Float p2d = null; 101 final Renderer renderer; 102 final Stroker stroker; 103 // Simplifies out collinear lines 104 final CollinearSimplifier simplifier = new CollinearSimplifier(); 105 final Dasher dasher; 106 final MarlinTileGenerator ptg; 107 final MarlinCache cache; 108 // flag indicating the shape is stroked (1) or filled (0) 109 int stroking = 0; 110 111 /** 112 * Constructor 113 * 114 * @param name context name (debugging) 115 * @param storageQueue true = queue storage; false = thread-Local storage 116 */ 117 RendererContext(final String name, final boolean storageQueue) { 118 if (logCreateContext) { 119 MarlinUtils.logInfo("new RendererContext = " + name); 120 } 121 122 this.name = name; 123 this.storageQueue = storageQueue; 124 125 // NormalizingPathIterator instances: 126 nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(float6); 127 nPQPathIterator = new NormalizingPathIterator.NearestPixelQuarter(float6); 128 129 // MarlinRenderingEngine.TransformingPathConsumer2D 130 transformerPC2D = new TransformingPathConsumer2D(); 131 132 // Renderer: 133 cache = new MarlinCache(this); 134 renderer = new Renderer(this); // needs MarlinCache from rdrCtx.cache 135 ptg = new MarlinTileGenerator(renderer); 136 137 stroker = new Stroker(this); 138 dasher = new Dasher(this); 139 140 // Create the reference to this instance (hard, soft or weak): 141 switch (MarlinRenderingEngine.REF_TYPE) { 142 default: 143 case MarlinRenderingEngine.REF_HARD: 144 reference = this; 145 break; 146 case MarlinRenderingEngine.REF_SOFT: 147 reference = new SoftReference<RendererContext>(this); 148 break; 149 case MarlinRenderingEngine.REF_WEAK: 150 reference = new WeakReference<RendererContext>(this); 151 break; 152 } 153 } 154 155 /** 156 * Disposes this renderer context: 157 * clean up before reusing this context 158 */ 159 void dispose() { 160 stroking = 0; 161 // reset hard reference to array caches if needed: 162 if (!USE_CACHE_HARD_REF) { 163 hardRefArrayCaches = null; 164 } 165 // if context is maked as DIRTY: 166 if (dirty) { 167 // may happen if an exception if thrown in the pipeline processing: 168 // force cleanup of all possible pipelined blocks (except Renderer): 169 170 // NormalizingPathIterator instances: 171 this.nPCPathIterator.dispose(); 172 this.nPQPathIterator.dispose(); 173 // Dasher: 174 this.dasher.dispose(); 175 // Stroker: 176 this.stroker.dispose(); 177 178 // mark context as CLEAN: 179 dirty = false; 180 } 181 } 182 183 // Array caches 184 ArrayCachesHolder getArrayCachesHolder() { 185 // Use hard reference first (cached resolved weak reference): 186 ArrayCachesHolder holder = hardRefArrayCaches; 187 if (holder == null) { 188 // resolve reference: 189 holder = (refArrayCaches != null) 190 ? refArrayCaches.get() 191 : null; 192 // create a new ArrayCachesHolder if none is available 193 if (holder == null) { 194 if (logCreateContext) { 195 MarlinUtils.logInfo("new ArrayCachesHolder for " 196 + "RendererContext = " + name); 197 } 198 199 holder = new ArrayCachesHolder(); 200 201 if (USE_CACHE_HARD_REF) { 202 // update hard reference: 203 hardRefArrayCaches = holder; 204 } else { 205 // update weak reference: 206 refArrayCaches = new WeakReference<ArrayCachesHolder>(holder); 207 } 208 } 209 } 210 return holder; 211 } 212 213 // dirty byte array cache 214 ByteArrayCache getDirtyByteArrayCache(final int length) { 215 final int bucket = ArrayCache.getBucketDirtyBytes(length); 216 return getArrayCachesHolder().dirtyByteArrayCaches[bucket]; 217 } 218 219 byte[] getDirtyByteArray(final int length) { 220 if (length <= MAX_DIRTY_BYTE_ARRAY_SIZE) { 221 return getDirtyByteArrayCache(length).getArray(); 222 } 223 224 if (doStats) { 225 incOversize(); 226 } 227 228 if (doLogOverSize) { 229 logInfo("getDirtyByteArray[oversize]: length=\t" + length); 230 } 231 232 return new byte[length]; 233 } 234 235 void putDirtyByteArray(final byte[] array) { 236 final int length = array.length; 237 // odd sized array are non-cached arrays (initial arrays) 238 // ensure to never store initial arrays in cache: 239 if (((length & 0x1) == 0) && (length <= MAX_DIRTY_BYTE_ARRAY_SIZE)) { 240 getDirtyByteArrayCache(length).putDirtyArray(array, length); 241 } 242 } 243 244 byte[] widenDirtyByteArray(final byte[] in, 245 final int usedSize, final int needSize) 246 { 247 final int length = in.length; 248 if (doChecks && length >= needSize) { 249 return in; 250 } 251 if (doStats) { 252 incResizeDirtyByte(); 253 } 254 255 // maybe change bucket: 256 // ensure getNewSize() > newSize: 257 final byte[] res = getDirtyByteArray(getNewSize(usedSize, needSize)); 258 259 System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements 260 261 // maybe return current array: 262 // NO clean-up of array data = DIRTY ARRAY 263 putDirtyByteArray(in); 264 265 if (doLogWidenArray) { 266 logInfo("widenDirtyByteArray[" + res.length + "]: usedSize=\t" 267 + usedSize + "\tlength=\t" + length + "\tneeded length=\t" 268 + needSize); 269 } 270 return res; 271 } 272 273 // int array cache 274 IntArrayCache getIntArrayCache(final int length) { 275 final int bucket = ArrayCache.getBucket(length); 276 return getArrayCachesHolder().intArrayCaches[bucket]; 277 } 278 279 int[] getIntArray(final int length) { 280 if (length <= MAX_ARRAY_SIZE) { 281 return getIntArrayCache(length).getArray(); 282 } 283 284 if (doStats) { 285 incOversize(); 286 } 287 288 if (doLogOverSize) { 289 logInfo("getIntArray[oversize]: length=\t" + length); 290 } 291 292 return new int[length]; 293 } 294 295 // unused 296 int[] widenIntArray(final int[] in, final int usedSize, 297 final int needSize, final int clearTo) 298 { 299 final int length = in.length; 300 if (doChecks && length >= needSize) { 301 return in; 302 } 303 if (doStats) { 304 incResizeInt(); 305 } 306 307 // maybe change bucket: 308 // ensure getNewSize() > newSize: 309 final int[] res = getIntArray(getNewSize(usedSize, needSize)); 310 311 System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements 312 313 // maybe return current array: 314 putIntArray(in, 0, clearTo); // ensure all array is cleared (grow-reduce algo) 315 316 if (doLogWidenArray) { 317 logInfo("widenIntArray[" + res.length + "]: usedSize=\t" 318 + usedSize + "\tlength=\t" + length + "\tneeded length=\t" 319 + needSize); 320 } 321 return res; 322 } 323 324 void putIntArray(final int[] array, final int fromIndex, 325 final int toIndex) 326 { 327 final int length = array.length; 328 // odd sized array are non-cached arrays (initial arrays) 329 // ensure to never store initial arrays in cache: 330 if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) { 331 getIntArrayCache(length).putArray(array, length, fromIndex, toIndex); 332 } 333 } 334 335 // dirty int array cache 336 IntArrayCache getDirtyIntArrayCache(final int length) { 337 final int bucket = ArrayCache.getBucket(length); 338 return getArrayCachesHolder().dirtyIntArrayCaches[bucket]; 339 } 340 341 int[] getDirtyIntArray(final int length) { 342 if (length <= MAX_ARRAY_SIZE) { 343 return getDirtyIntArrayCache(length).getArray(); 344 } 345 346 if (doStats) { 347 incOversize(); 348 } 349 350 if (doLogOverSize) { 351 logInfo("getDirtyIntArray[oversize]: length=\t" + length); 352 } 353 354 return new int[length]; 355 } 356 357 int[] widenDirtyIntArray(final int[] in, 358 final int usedSize, final int needSize) 359 { 360 final int length = in.length; 361 if (doChecks && length >= needSize) { 362 return in; 363 } 364 if (doStats) { 365 incResizeDirtyInt(); 366 } 367 368 // maybe change bucket: 369 // ensure getNewSize() > newSize: 370 final int[] res = getDirtyIntArray(getNewSize(usedSize, needSize)); 371 372 System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements 373 374 // maybe return current array: 375 // NO clean-up of array data = DIRTY ARRAY 376 putDirtyIntArray(in); 377 378 if (doLogWidenArray) { 379 logInfo("widenDirtyIntArray[" + res.length + "]: usedSize=\t" 380 + usedSize + "\tlength=\t" + length + "\tneeded length=\t" 381 + needSize); 382 } 383 return res; 384 } 385 386 void putDirtyIntArray(final int[] array) { 387 final int length = array.length; 388 // odd sized array are non-cached arrays (initial arrays) 389 // ensure to never store initial arrays in cache: 390 if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) { 391 getDirtyIntArrayCache(length).putDirtyArray(array, length); 392 } 393 } 394 395 // dirty float array cache 396 FloatArrayCache getDirtyFloatArrayCache(final int length) { 397 final int bucket = ArrayCache.getBucket(length); 398 return getArrayCachesHolder().dirtyFloatArrayCaches[bucket]; 399 } 400 401 float[] getDirtyFloatArray(final int length) { 402 if (length <= MAX_ARRAY_SIZE) { 403 return getDirtyFloatArrayCache(length).getArray(); 404 } 405 406 if (doStats) { 407 incOversize(); 408 } 409 410 if (doLogOverSize) { 411 logInfo("getDirtyFloatArray[oversize]: length=\t" + length); 412 } 413 414 return new float[length]; 415 } 416 417 float[] widenDirtyFloatArray(final float[] in, 418 final int usedSize, final int needSize) 419 { 420 final int length = in.length; 421 if (doChecks && length >= needSize) { 422 return in; 423 } 424 if (doStats) { 425 incResizeDirtyFloat(); 426 } 427 428 // maybe change bucket: 429 // ensure getNewSize() > newSize: 430 final float[] res = getDirtyFloatArray(getNewSize(usedSize, needSize)); 431 432 System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements 433 434 // maybe return current array: 435 // NO clean-up of array data = DIRTY ARRAY 436 putDirtyFloatArray(in); 437 438 if (doLogWidenArray) { 439 logInfo("widenDirtyFloatArray[" + res.length + "]: usedSize=\t" 440 + usedSize + "\tlength=\t" + length + "\tneeded length=\t" 441 + needSize); 442 } 443 return res; 444 } 445 446 void putDirtyFloatArray(final float[] array) { 447 final int length = array.length; 448 // odd sized array are non-cached arrays (initial arrays) 449 // ensure to never store initial arrays in cache: 450 if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) { 451 getDirtyFloatArrayCache(length).putDirtyArray(array, length); 452 } 453 } 454 455 /* class holding all array cache instances */ 456 static final class ArrayCachesHolder { 457 // zero-filled int array cache: 458 final IntArrayCache[] intArrayCaches; 459 // dirty array caches: 460 final IntArrayCache[] dirtyIntArrayCaches; 461 final FloatArrayCache[] dirtyFloatArrayCaches; 462 final ByteArrayCache[] dirtyByteArrayCaches; 463 464 ArrayCachesHolder() { 465 intArrayCaches = new IntArrayCache[BUCKETS]; 466 dirtyIntArrayCaches = new IntArrayCache[BUCKETS]; 467 dirtyFloatArrayCaches = new FloatArrayCache[BUCKETS]; 468 dirtyByteArrayCaches = new ByteArrayCache[BUCKETS]; 469 470 for (int i = 0; i < BUCKETS; i++) { 471 intArrayCaches[i] = new IntArrayCache(ARRAY_SIZES[i]); 472 // dirty array caches: 473 dirtyIntArrayCaches[i] = new IntArrayCache(ARRAY_SIZES[i]); 474 dirtyFloatArrayCaches[i] = new FloatArrayCache(ARRAY_SIZES[i]); 475 dirtyByteArrayCaches[i] = new ByteArrayCache(DIRTY_BYTE_ARRAY_SIZES[i]); 476 } 477 } 478 } 479 }