--- /dev/null 2015-11-23 13:25:23.000000000 -0800 +++ new/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java 2015-11-23 13:25:23.000000000 -0800 @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.java2d.marlin; + +import java.awt.geom.Path2D; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.util.concurrent.atomic.AtomicInteger; +import static sun.java2d.marlin.ArrayCache.*; +import sun.java2d.marlin.MarlinRenderingEngine.NormalizingPathIterator; +import static sun.java2d.marlin.MarlinUtils.getCallerInfo; +import static sun.java2d.marlin.MarlinUtils.logInfo; + +/** + * This class is a renderer context dedicated to a single thread + */ +final class RendererContext implements MarlinConst { + + private static final String className = RendererContext.class.getName(); + // RendererContext creation counter + private static final AtomicInteger contextCount = new AtomicInteger(1); + // RendererContext statistics + static final RendererStats stats = (doStats || doMonitors) + ? RendererStats.getInstance(): null; + + private static final boolean USE_CACHE_HARD_REF = doStats + || (MarlinRenderingEngine.REF_TYPE == MarlinRenderingEngine.REF_WEAK); + + /** + * Create a new renderer context + * + * @return new RendererContext instance + */ + static RendererContext createContext() { + final RendererContext newCtx = new RendererContext("ctx" + + Integer.toString(contextCount.getAndIncrement())); + if (RendererContext.stats != null) { + RendererContext.stats.allContexts.add(newCtx); + } + return newCtx; + } + + // context name (debugging purposes) + final String name; + /* + * Reference to this instance (hard, soft or weak). + * @see MarlinRenderingEngine#REF_TYPE + */ + final Object reference; + // dirty flag indicating an exception occured during pipeline in pathTo() + boolean dirty = false; + // dynamic array caches kept using weak reference (low memory footprint) + WeakReference refArrayCaches = null; + // hard reference to array caches (for statistics) + ArrayCachesHolder hardRefArrayCaches = null; + // shared data + final float[] float6 = new float[6]; + // shared curve (dirty) (Renderer / Stroker) + final Curve curve = new Curve(); + // MarlinRenderingEngine NormalizingPathIterator NearestPixelCenter: + final NormalizingPathIterator nPCPathIterator; + // MarlinRenderingEngine NearestPixelQuarter NormalizingPathIterator: + final NormalizingPathIterator nPQPathIterator; + // MarlinRenderingEngine.TransformingPathConsumer2D + final TransformingPathConsumer2D transformerPC2D; + // recycled Path2D instance + Path2D.Float p2d = null; + final Renderer renderer; + final Stroker stroker; + // Simplifies out collinear lines + final CollinearSimplifier simplifier = new CollinearSimplifier(); + final Dasher dasher; + final MarlinTileGenerator ptg; + final MarlinCache cache; + // flag indicating the shape is stroked (1) or filled (0) + int stroking = 0; + + /** + * Constructor + * + * @param name + */ + RendererContext(final String name) { + if (logCreateContext) { + MarlinUtils.logInfo("new RendererContext = " + name); + } + + this.name = name; + + // NormalizingPathIterator instances: + nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(float6); + nPQPathIterator = new NormalizingPathIterator.NearestPixelQuarter(float6); + + // MarlinRenderingEngine.TransformingPathConsumer2D + transformerPC2D = new TransformingPathConsumer2D(); + + // Renderer: + cache = new MarlinCache(this); + renderer = new Renderer(this); // needs MarlinCache from rdrCtx.cache + ptg = new MarlinTileGenerator(renderer); + + stroker = new Stroker(this); + dasher = new Dasher(this); + + // Create the reference to this instance (hard, soft or weak): + switch (MarlinRenderingEngine.REF_TYPE) { + default: + case MarlinRenderingEngine.REF_HARD: + reference = this; + break; + case MarlinRenderingEngine.REF_SOFT: + reference = new SoftReference(this); + break; + case MarlinRenderingEngine.REF_WEAK: + reference = new WeakReference(this); + break; + } + } + + /** + * Disposes this renderer context: + * clean up before reusing this context + */ + void dispose() { + stroking = 0; + // reset hard reference to array caches if needed: + if (!USE_CACHE_HARD_REF) { + hardRefArrayCaches = null; + } + // if context is maked as DIRTY: + if (dirty) { + // may happen if an exception if thrown in the pipeline processing: + // force cleanup of all possible pipelined blocks (except Renderer): + + // NormalizingPathIterator instances: + this.nPCPathIterator.dispose(); + this.nPQPathIterator.dispose(); + // Dasher: + this.dasher.dispose(); + // Stroker: + this.stroker.dispose(); + + // mark context as CLEAN: + dirty = false; + } + } + + // Array caches + ArrayCachesHolder getArrayCachesHolder() { + // Use hard reference first (cached resolved weak reference): + ArrayCachesHolder holder = hardRefArrayCaches; + if (holder == null) { + // resolve reference: + holder = (refArrayCaches != null) + ? refArrayCaches.get() + : null; + // create a new ArrayCachesHolder if none is available + if (holder == null) { + if (logCreateContext) { + MarlinUtils.logInfo("new ArrayCachesHolder for " + + "RendererContext = " + name); + } + + holder = new ArrayCachesHolder(); + + if (USE_CACHE_HARD_REF) { + // update hard reference: + hardRefArrayCaches = holder; + } + + // update weak reference: + refArrayCaches = new WeakReference(holder); + } + } + return holder; + } + + // dirty byte array cache + ByteArrayCache getDirtyByteArrayCache(final int length) { + final int bucket = ArrayCache.getBucketDirtyBytes(length); + return getArrayCachesHolder().dirtyByteArrayCaches[bucket]; + } + + byte[] getDirtyByteArray(final int length) { + if (length <= MAX_DIRTY_BYTE_ARRAY_SIZE) { + return getDirtyByteArrayCache(length).getArray(); + } + + if (doStats) { + incOversize(); + } + + if (doLogOverSize) { + logInfo("getDirtyByteArray[oversize]: length=\t" + length + + "\tfrom=\t" + getCallerInfo(className)); + } + + return new byte[length]; + } + + void putDirtyByteArray(final byte[] array) { + final int length = array.length; + // odd sized array are non-cached arrays (initial arrays) + // ensure to never store initial arrays in cache: + if (((length & 0x1) == 0) && (length <= MAX_DIRTY_BYTE_ARRAY_SIZE)) { + getDirtyByteArrayCache(length).putDirtyArray(array, length); + } + } + + byte[] widenDirtyByteArray(final byte[] in, + final int usedSize, final int needSize) + { + final int length = in.length; + if (doChecks && length >= needSize) { + return in; + } + if (doStats) { + incResizeDirtyByte(); + } + + // maybe change bucket: + // ensure getNewSize() > newSize: + final byte[] res = getDirtyByteArray(getNewSize(usedSize, needSize)); + + System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements + + // maybe return current array: + // NO clean-up of array data = DIRTY ARRAY + putDirtyByteArray(in); + + if (doLogWidenArray) { + logInfo("widenDirtyByteArray[" + res.length + "]: usedSize=\t" + + usedSize + "\tlength=\t" + length + "\tneeded length=\t" + + needSize + "\tfrom=\t" + getCallerInfo(className)); + } + return res; + } + + // int array cache + IntArrayCache getIntArrayCache(final int length) { + final int bucket = ArrayCache.getBucket(length); + return getArrayCachesHolder().intArrayCaches[bucket]; + } + + int[] getIntArray(final int length) { + if (length <= MAX_ARRAY_SIZE) { + return getIntArrayCache(length).getArray(); + } + + if (doStats) { + incOversize(); + } + + if (doLogOverSize) { + logInfo("getIntArray[oversize]: length=\t" + length + "\tfrom=\t" + + getCallerInfo(className)); + } + + return new int[length]; + } + + // unused + int[] widenIntArray(final int[] in, final int usedSize, + final int needSize, final int clearTo) + { + final int length = in.length; + if (doChecks && length >= needSize) { + return in; + } + if (doStats) { + incResizeInt(); + } + + // maybe change bucket: + // ensure getNewSize() > newSize: + final int[] res = getIntArray(getNewSize(usedSize, needSize)); + + System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements + + // maybe return current array: + putIntArray(in, 0, clearTo); // ensure all array is cleared (grow-reduce algo) + + if (doLogWidenArray) { + logInfo("widenIntArray[" + res.length + "]: usedSize=\t" + + usedSize + "\tlength=\t" + length + "\tneeded length=\t" + + needSize + "\tfrom=\t" + getCallerInfo(className)); + } + return res; + } + + void putIntArray(final int[] array, final int fromIndex, + final int toIndex) + { + final int length = array.length; + // odd sized array are non-cached arrays (initial arrays) + // ensure to never store initial arrays in cache: + if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) { + getIntArrayCache(length).putArray(array, length, fromIndex, toIndex); + } + } + + // dirty int array cache + IntArrayCache getDirtyIntArrayCache(final int length) { + final int bucket = ArrayCache.getBucket(length); + return getArrayCachesHolder().dirtyIntArrayCaches[bucket]; + } + + int[] getDirtyIntArray(final int length) { + if (length <= MAX_ARRAY_SIZE) { + return getDirtyIntArrayCache(length).getArray(); + } + + if (doStats) { + incOversize(); + } + + if (doLogOverSize) { + logInfo("getDirtyIntArray[oversize]: length=\t" + length + + "\tfrom=\t" + getCallerInfo(className)); + } + + return new int[length]; + } + + int[] widenDirtyIntArray(final int[] in, + final int usedSize, final int needSize) + { + final int length = in.length; + if (doChecks && length >= needSize) { + return in; + } + if (doStats) { + incResizeDirtyInt(); + } + + // maybe change bucket: + // ensure getNewSize() > newSize: + final int[] res = getDirtyIntArray(getNewSize(usedSize, needSize)); + + System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements + + // maybe return current array: + // NO clean-up of array data = DIRTY ARRAY + putDirtyIntArray(in); + + if (doLogWidenArray) { + logInfo("widenDirtyIntArray[" + res.length + "]: usedSize=\t" + + usedSize + "\tlength=\t" + length + "\tneeded length=\t" + + needSize + "\tfrom=\t" + getCallerInfo(className)); + } + return res; + } + + void putDirtyIntArray(final int[] array) { + final int length = array.length; + // odd sized array are non-cached arrays (initial arrays) + // ensure to never store initial arrays in cache: + if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) { + getDirtyIntArrayCache(length).putDirtyArray(array, length); + } + } + + // dirty float array cache + FloatArrayCache getDirtyFloatArrayCache(final int length) { + final int bucket = ArrayCache.getBucket(length); + return getArrayCachesHolder().dirtyFloatArrayCaches[bucket]; + } + + float[] getDirtyFloatArray(final int length) { + if (length <= MAX_ARRAY_SIZE) { + return getDirtyFloatArrayCache(length).getArray(); + } + + if (doStats) { + incOversize(); + } + + if (doLogOverSize) { + logInfo("getDirtyFloatArray[oversize]: length=\t" + length + + "\tfrom=\t" + getCallerInfo(className)); + } + + return new float[length]; + } + + float[] widenDirtyFloatArray(final float[] in, + final int usedSize, final int needSize) + { + final int length = in.length; + if (doChecks && length >= needSize) { + return in; + } + if (doStats) { + incResizeDirtyFloat(); + } + + // maybe change bucket: + // ensure getNewSize() > newSize: + final float[] res = getDirtyFloatArray(getNewSize(usedSize, needSize)); + + System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements + + // maybe return current array: + // NO clean-up of array data = DIRTY ARRAY + putDirtyFloatArray(in); + + if (doLogWidenArray) { + logInfo("widenDirtyFloatArray[" + res.length + "]: usedSize=\t" + + usedSize + "\tlength=\t" + length + "\tneeded length=\t" + + needSize + "\tfrom=\t" + getCallerInfo(className)); + } + return res; + } + + void putDirtyFloatArray(final float[] array) { + final int length = array.length; + // odd sized array are non-cached arrays (initial arrays) + // ensure to never store initial arrays in cache: + if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) { + getDirtyFloatArrayCache(length).putDirtyArray(array, length); + } + } + + /* class holding all array cache instances */ + static final class ArrayCachesHolder { + // zero-filled int array cache: + final IntArrayCache[] intArrayCaches; + // dirty array caches: + final IntArrayCache[] dirtyIntArrayCaches; + final FloatArrayCache[] dirtyFloatArrayCaches; + final ByteArrayCache[] dirtyByteArrayCaches; + + ArrayCachesHolder() { + intArrayCaches = new IntArrayCache[BUCKETS]; + dirtyIntArrayCaches = new IntArrayCache[BUCKETS]; + dirtyFloatArrayCaches = new FloatArrayCache[BUCKETS]; + dirtyByteArrayCaches = new ByteArrayCache[BUCKETS]; + + for (int i = 0; i < BUCKETS; i++) { + intArrayCaches[i] = new IntArrayCache(ARRAY_SIZES[i]); + // dirty array caches: + dirtyIntArrayCaches[i] = new IntArrayCache(ARRAY_SIZES[i]); + dirtyFloatArrayCaches[i] = new FloatArrayCache(ARRAY_SIZES[i]); + dirtyByteArrayCaches[i] = new ByteArrayCache(DIRTY_BYTE_ARRAY_SIZES[i]); + } + } + } +}