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