Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/java2d/marlin/MarlinCache.java
38918 views
/*1* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d.marlin;2627import sun.misc.Unsafe;2829/**30* An object used to cache pre-rendered complex paths.31*32* @see Renderer33*/34public final class MarlinCache implements MarlinConst {3536static final boolean FORCE_RLE = MarlinProperties.isForceRLE();37static final boolean FORCE_NO_RLE = MarlinProperties.isForceNoRLE();38// minimum width to try using RLE encoding:39static final int RLE_MIN_WIDTH40= Math.max(BLOCK_SIZE, MarlinProperties.getRLEMinWidth());41// maximum width for RLE encoding:42// values are stored as int [x|alpha] where alpha is 8 bits43static final int RLE_MAX_WIDTH = 1 << (24 - 1);4445// 2048 (pixelSize) alpha values (width) x 32 rows (tile) = 64K bytes46// x1 instead of 4 bytes (RLE) ie 1/4 capacity or average good RLE compression47static final long INITIAL_CHUNK_ARRAY = TILE_SIZE * INITIAL_PIXEL_DIM; // 64K4849// The alpha map used by this object (taken out of our map cache) to convert50// pixel coverage counts gotten from MarlinCache (which are in the range51// [0, maxalpha]) into alpha values, which are in [0,256).52static final byte[] ALPHA_MAP;5354static final OffHeapArray ALPHA_MAP_UNSAFE;5556static {57final byte[] _ALPHA_MAP = buildAlphaMap(MAX_AA_ALPHA);5859ALPHA_MAP_UNSAFE = new OffHeapArray(_ALPHA_MAP, _ALPHA_MAP.length); // 1K60ALPHA_MAP =_ALPHA_MAP;6162final Unsafe _unsafe = OffHeapArray.unsafe;63final long addr = ALPHA_MAP_UNSAFE.address;6465for (int i = 0; i < _ALPHA_MAP.length; i++) {66_unsafe.putByte(addr + i, _ALPHA_MAP[i]);67}68}6970int bboxX0, bboxY0, bboxX1, bboxY1;7172// 1D dirty arrays73// row index in rowAAChunk[]74final long[] rowAAChunkIndex = new long[TILE_SIZE];75// first pixel (inclusive) for each row76final int[] rowAAx0 = new int[TILE_SIZE];77// last pixel (exclusive) for each row78final int[] rowAAx1 = new int[TILE_SIZE];79// encoding mode (0=raw, 1=RLE encoding) for each row80final int[] rowAAEnc = new int[TILE_SIZE];81// coded length (RLE encoding) for each row82final long[] rowAALen = new long[TILE_SIZE];83// last position in RLE decoding for each row (getAlpha):84final long[] rowAAPos = new long[TILE_SIZE];8586// dirty off-heap array containing pixel coverages for (32) rows (packed)87// if encoding=raw, it contains alpha coverage values (val) as integer88// if encoding=RLE, it contains tuples (val, last x-coordinate exclusive)89// use rowAAx0/rowAAx1 to get row indices within this chunk90final OffHeapArray rowAAChunk;9192// current position in rowAAChunk array93long rowAAChunkPos;9495// touchedTile[i] is the sum of all the alphas in the tile with96// x=j*TILE_SIZE+bboxX0.97int[] touchedTile;9899// per-thread renderer context100final RendererContext rdrCtx;101102// large cached touchedTile (dirty)103final int[] touchedTile_initial = new int[INITIAL_ARRAY]; // 1 tile line104105int tileMin, tileMax;106107boolean useRLE = false;108109MarlinCache(final RendererContext rdrCtx) {110this.rdrCtx = rdrCtx;111112rowAAChunk = new OffHeapArray(rdrCtx, INITIAL_CHUNK_ARRAY);113114touchedTile = touchedTile_initial;115116// tile used marks:117tileMin = Integer.MAX_VALUE;118tileMax = Integer.MIN_VALUE;119}120121void init(int minx, int miny, int maxx, int maxy, int edgeSumDeltaY)122{123// assert maxy >= miny && maxx >= minx;124bboxX0 = minx;125bboxY0 = miny;126bboxX1 = maxx;127bboxY1 = maxy;128129final int width = (maxx - minx);130131if (FORCE_NO_RLE) {132useRLE = false;133} else if (FORCE_RLE) {134useRLE = true;135} else {136// heuristics: use both bbox area and complexity137// ie number of primitives:138139// fast check min and max width (maxx < 23bits):140if (width <= RLE_MIN_WIDTH || width >= RLE_MAX_WIDTH) {141useRLE = false;142} else {143// perimeter approach: how fit the total length into given height:144145// if stroking: meanCrossings /= 2 => divide edgeSumDeltaY by 2146final int heightSubPixel147= (((maxy - miny) << SUBPIXEL_LG_POSITIONS_Y) << rdrCtx.stroking);148149// check meanDist > block size:150// check width / (meanCrossings - 1) >= RLE_THRESHOLD151152// fast case: (meanCrossingPerPixel <= 2) means 1 span only153useRLE = (edgeSumDeltaY <= (heightSubPixel << 1))154// note: already checked (meanCrossingPerPixel <= 2)155// rewritten to avoid division:156|| (width * heightSubPixel) >157((edgeSumDeltaY - heightSubPixel) << BLOCK_SIZE_LG);158159if (doTrace && !useRLE) {160final float meanCrossings161= ((float) edgeSumDeltaY) / heightSubPixel;162final float meanDist = width / (meanCrossings - 1);163164System.out.println("High complexity: "165+ " for bbox[width = " + width166+ " height = " + (maxy - miny)167+ "] edgeSumDeltaY = " + edgeSumDeltaY168+ " heightSubPixel = " + heightSubPixel169+ " meanCrossings = "+ meanCrossings170+ " meanDist = " + meanDist171+ " width = " + (width * heightSubPixel)172+ " <= criteria: " + ((edgeSumDeltaY - heightSubPixel) << BLOCK_SIZE_LG)173);174}175}176}177178// the ceiling of (maxy - miny + 1) / TILE_SIZE;179final int nxTiles = (width + TILE_SIZE) >> TILE_SIZE_LG;180181if (nxTiles > INITIAL_ARRAY) {182if (doStats) {183RendererContext.stats.stat_array_marlincache_touchedTile184.add(nxTiles);185}186touchedTile = rdrCtx.getIntArray(nxTiles);187}188}189190/**191* Disposes this cache:192* clean up before reusing this instance193*/194void dispose() {195// Reset touchedTile if needed:196resetTileLine(0);197198// Return arrays:199if (touchedTile != touchedTile_initial) {200rdrCtx.putIntArray(touchedTile, 0, 0); // already zero filled201touchedTile = touchedTile_initial;202}203// At last: resize back off-heap rowAA to initial size204if (rowAAChunk.length != INITIAL_CHUNK_ARRAY) {205// note: may throw OOME:206rowAAChunk.resize(INITIAL_CHUNK_ARRAY);207}208if (doCleanDirty) {209// Force zero-fill dirty arrays:210rowAAChunk.fill(BYTE_0);211}212}213214void resetTileLine(final int pminY) {215// update bboxY0 to process a complete tile line [0 - 32]216bboxY0 = pminY;217218// reset current pos219if (doStats) {220RendererContext.stats.stat_cache_rowAAChunk.add(rowAAChunkPos);221}222rowAAChunkPos = 0L;223224// Reset touchedTile:225if (tileMin != Integer.MAX_VALUE) {226if (doStats) {227RendererContext.stats.stat_cache_tiles.add(tileMax - tileMin);228}229// clean only dirty touchedTile:230if (tileMax == 1) {231touchedTile[0] = 0;232} else {233IntArrayCache.fill(touchedTile, tileMin, tileMax, 0);234}235// reset tile used marks:236tileMin = Integer.MAX_VALUE;237tileMax = Integer.MIN_VALUE;238}239240if (doCleanDirty) {241// Force zero-fill dirty arrays:242rowAAChunk.fill(BYTE_0);243}244}245246void clearAARow(final int y) {247// process tile line [0 - 32]248final int row = y - bboxY0;249250// update pixel range:251rowAAx0[row] = 0; // first pixel inclusive252rowAAx1[row] = 0; // last pixel exclusive253rowAAEnc[row] = 0; // raw encoding254255// note: leave rowAAChunkIndex[row] undefined256// and rowAALen[row] & rowAAPos[row] (RLE)257}258259/**260* Copy the given alpha data into the rowAA cache261* @param alphaRow alpha data to copy from262* @param y y pixel coordinate263* @param px0 first pixel inclusive x0264* @param px1 last pixel exclusive x1265*/266void copyAARowNoRLE(final int[] alphaRow, final int y,267final int px0, final int px1)268{269if (doMonitors) {270RendererContext.stats.mon_rdr_copyAARow.start();271}272273// skip useless pixels above boundary274final int px_bbox1 = FloatMath.min(px1, bboxX1);275276if (doLogBounds) {277MarlinUtils.logInfo("row = [" + px0 + " ... " + px_bbox1278+ " (" + px1 + ") [ for y=" + y);279}280281final int row = y - bboxY0;282283// update pixel range:284rowAAx0[row] = px0; // first pixel inclusive285rowAAx1[row] = px_bbox1; // last pixel exclusive286rowAAEnc[row] = 0; // raw encoding287288// get current position (bytes):289final long pos = rowAAChunkPos;290// update row index to current position:291rowAAChunkIndex[row] = pos;292293// determine need array size:294// for RLE encoding, position must be aligned to 4 bytes (int):295// align - 1 = 3 so add +3 and round-off by mask ~3 = -4296final long needSize = pos + ((px_bbox1 - px0 + 3) & -4);297298// update next position (bytes):299rowAAChunkPos = needSize;300301// update row data:302final OffHeapArray _rowAAChunk = rowAAChunk;303// ensure rowAAChunk capacity:304if (_rowAAChunk.length < needSize) {305expandRowAAChunk(needSize);306}307if (doStats) {308RendererContext.stats.stat_cache_rowAA.add(px_bbox1 - px0);309}310311// rowAA contains only alpha values for range[x0; x1[312final int[] _touchedTile = touchedTile;313final int _TILE_SIZE_LG = TILE_SIZE_LG;314315final int from = px0 - bboxX0; // first pixel inclusive316final int to = px_bbox1 - bboxX0; // last pixel exclusive317318final Unsafe _unsafe = OffHeapArray.unsafe;319final long SIZE_BYTE = 1L;320final long addr_alpha = ALPHA_MAP_UNSAFE.address;321long addr_off = _rowAAChunk.address + pos;322323// compute alpha sum into rowAA:324for (int x = from, val = 0; x < to; x++) {325// alphaRow is in [0; MAX_COVERAGE]326val += alphaRow[x]; // [from; to[327328// ensure values are in [0; MAX_AA_ALPHA] range329if (DO_AA_RANGE_CHECK) {330if (val < 0) {331System.out.println("Invalid coverage = " + val);332val = 0;333}334if (val > MAX_AA_ALPHA) {335System.out.println("Invalid coverage = " + val);336val = MAX_AA_ALPHA;337}338}339340// store alpha sum (as byte):341if (val == 0) {342_unsafe.putByte(addr_off, (byte)0); // [0..255]343} else {344_unsafe.putByte(addr_off, _unsafe.getByte(addr_alpha + val)); // [0..255]345346// update touchedTile347_touchedTile[x >> _TILE_SIZE_LG] += val;348}349addr_off += SIZE_BYTE;350}351352// update tile used marks:353int tx = from >> _TILE_SIZE_LG; // inclusive354if (tx < tileMin) {355tileMin = tx;356}357358tx = ((to - 1) >> _TILE_SIZE_LG) + 1; // exclusive (+1 to be sure)359if (tx > tileMax) {360tileMax = tx;361}362363if (doLogBounds) {364MarlinUtils.logInfo("clear = [" + from + " ... " + to + "[");365}366367// Clear alpha row for reuse:368IntArrayCache.fill(alphaRow, from, px1 - bboxX0, 0);369370if (doMonitors) {371RendererContext.stats.mon_rdr_copyAARow.stop();372}373}374375void copyAARowRLE_WithBlockFlags(final int[] blkFlags, final int[] alphaRow,376final int y, final int px0, final int px1)377{378if (doMonitors) {379RendererContext.stats.mon_rdr_copyAARow.start();380}381382// Copy rowAA data into the piscesCache if one is present383final int _bboxX0 = bboxX0;384385// process tile line [0 - 32]386final int row = y - bboxY0;387final int from = px0 - _bboxX0; // first pixel inclusive388389// skip useless pixels above boundary390final int px_bbox1 = FloatMath.min(px1, bboxX1);391final int to = px_bbox1 - _bboxX0; // last pixel exclusive392393if (doLogBounds) {394MarlinUtils.logInfo("row = [" + px0 + " ... " + px_bbox1395+ " (" + px1 + ") [ for y=" + y);396}397398// get current position:399final long initialPos = startRLERow(row, px0, px_bbox1);400401// determine need array size:402// pessimistic: max needed size = deltaX x 4 (1 int)403final long needSize = initialPos + ((to - from) << 2);404405// update row data:406OffHeapArray _rowAAChunk = rowAAChunk;407// ensure rowAAChunk capacity:408if (_rowAAChunk.length < needSize) {409expandRowAAChunk(needSize);410}411412final Unsafe _unsafe = OffHeapArray.unsafe;413final long SIZE_INT = 4L;414final long addr_alpha = ALPHA_MAP_UNSAFE.address;415long addr_off = _rowAAChunk.address + initialPos;416417final int[] _touchedTile = touchedTile;418final int _TILE_SIZE_LG = TILE_SIZE_LG;419final int _BLK_SIZE_LG = BLOCK_SIZE_LG;420421// traverse flagged blocks:422final int blkW = (from >> _BLK_SIZE_LG);423final int blkE = (to >> _BLK_SIZE_LG) + 1;424425// Perform run-length encoding and store results in the piscesCache426int val = 0;427int cx0 = from;428int runLen;429430final int _MAX_VALUE = Integer.MAX_VALUE;431int last_t0 = _MAX_VALUE;432433int skip = 0;434435for (int t = blkW, blk_x0, blk_x1, cx, delta; t <= blkE; t++) {436if (blkFlags[t] != 0) {437blkFlags[t] = 0;438439if (last_t0 == _MAX_VALUE) {440last_t0 = t;441}442continue;443}444if (last_t0 != _MAX_VALUE) {445// emit blocks:446blk_x0 = FloatMath.max(last_t0 << _BLK_SIZE_LG, from);447last_t0 = _MAX_VALUE;448449// (last block pixel+1) inclusive => +1450blk_x1 = FloatMath.min((t << _BLK_SIZE_LG) + 1, to);451452for (cx = blk_x0; cx < blk_x1; cx++) {453if ((delta = alphaRow[cx]) != 0) {454alphaRow[cx] = 0;455456// not first rle entry:457if (cx != cx0) {458runLen = cx - cx0;459460// store alpha coverage (ensure within bounds):461// as [absX|val] where:462// absX is the absolute x-coordinate:463// note: last pixel exclusive (>= 0)464// note: it should check X is smaller than 23bits (overflow)!465466// check address alignment to 4 bytes:467if (doCheckUnsafe) {468if ((addr_off & 3) != 0) {469MarlinUtils.logInfo("Misaligned Unsafe address: " + addr_off);470}471}472473// special case to encode entries into a single int:474if (val == 0) {475_unsafe.putInt(addr_off,476((_bboxX0 + cx) << 8)477);478} else {479_unsafe.putInt(addr_off,480((_bboxX0 + cx) << 8)481| (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0..255]482);483484if (runLen == 1) {485_touchedTile[cx0 >> _TILE_SIZE_LG] += val;486} else {487touchTile(cx0, val, cx, runLen, _touchedTile);488}489}490addr_off += SIZE_INT;491492if (doStats) {493RendererContext.stats.hist_tile_generator_encoding_runLen494.add(runLen);495}496cx0 = cx;497}498499// alpha value = running sum of coverage delta:500val += delta;501502// ensure values are in [0; MAX_AA_ALPHA] range503if (DO_AA_RANGE_CHECK) {504if (val < 0) {505System.out.println("Invalid coverage = " + val);506val = 0;507}508if (val > MAX_AA_ALPHA) {509System.out.println("Invalid coverage = " + val);510val = MAX_AA_ALPHA;511}512}513}514}515} else if (doStats) {516skip++;517}518}519520// Process remaining RLE run:521runLen = to - cx0;522523// store alpha coverage (ensure within bounds):524// as (int)[absX|val] where:525// absX is the absolute x-coordinate in bits 31 to 8 and val in bits 0..7526// note: last pixel exclusive (>= 0)527// note: it should check X is smaller than 23bits (overflow)!528529// check address alignment to 4 bytes:530if (doCheckUnsafe) {531if ((addr_off & 3) != 0) {532MarlinUtils.logInfo("Misaligned Unsafe address: " + addr_off);533}534}535536// special case to encode entries into a single int:537if (val == 0) {538_unsafe.putInt(addr_off,539((_bboxX0 + to) << 8)540);541} else {542_unsafe.putInt(addr_off,543((_bboxX0 + to) << 8)544| (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0..255]545);546547if (runLen == 1) {548_touchedTile[cx0 >> _TILE_SIZE_LG] += val;549} else {550touchTile(cx0, val, to, runLen, _touchedTile);551}552}553addr_off += SIZE_INT;554555if (doStats) {556RendererContext.stats.hist_tile_generator_encoding_runLen557.add(runLen);558}559560long len = (addr_off - _rowAAChunk.address);561562// update coded length as bytes:563rowAALen[row] = (len - initialPos);564565// update current position:566rowAAChunkPos = len;567568if (doStats) {569RendererContext.stats.stat_cache_rowAA.add(rowAALen[row]);570RendererContext.stats.hist_tile_generator_encoding_ratio.add(571(100 * skip) / (blkE - blkW)572);573}574575// update tile used marks:576int tx = from >> _TILE_SIZE_LG; // inclusive577if (tx < tileMin) {578tileMin = tx;579}580581tx = ((to - 1) >> _TILE_SIZE_LG) + 1; // exclusive (+1 to be sure)582if (tx > tileMax) {583tileMax = tx;584}585586// Clear alpha row for reuse:587if (px1 > bboxX1) {588alphaRow[to ] = 0;589alphaRow[to + 1] = 0;590}591if (doChecks) {592IntArrayCache.check(blkFlags, blkW, blkE, 0);593IntArrayCache.check(alphaRow, from, px1 - bboxX0, 0);594}595596if (doMonitors) {597RendererContext.stats.mon_rdr_copyAARow.stop();598}599}600601long startRLERow(final int row, final int x0, final int x1) {602// rows are supposed to be added by increasing y.603rowAAx0[row] = x0; // first pixel inclusive604rowAAx1[row] = x1; // last pixel exclusive605rowAAEnc[row] = 1; // RLE encoding606rowAAPos[row] = 0L; // position = 0607608// update row index to current position:609return (rowAAChunkIndex[row] = rowAAChunkPos);610}611612private void expandRowAAChunk(final long needSize) {613if (doStats) {614RendererContext.stats.stat_array_marlincache_rowAAChunk615.add(needSize);616}617618// note: throw IOOB if neededSize > 2Gb:619final long newSize = ArrayCache.getNewLargeSize(rowAAChunk.length, needSize);620621rowAAChunk.resize(newSize);622}623624private void touchTile(final int x0, final int val, final int x1,625final int runLen,626final int[] _touchedTile)627{628// the x and y of the current row, minus bboxX0, bboxY0629// process tile line [0 - 32]630final int _TILE_SIZE_LG = TILE_SIZE_LG;631632// update touchedTile633int tx = (x0 >> _TILE_SIZE_LG);634635// handle trivial case: same tile (x0, x0+runLen)636if (tx == (x1 >> _TILE_SIZE_LG)) {637// same tile:638_touchedTile[tx] += val * runLen;639return;640}641642final int tx1 = (x1 - 1) >> _TILE_SIZE_LG;643644if (tx <= tx1) {645final int nextTileXCoord = (tx + 1) << _TILE_SIZE_LG;646_touchedTile[tx++] += val * (nextTileXCoord - x0);647}648if (tx < tx1) {649// don't go all the way to tx1 - we need to handle the last650// tile as a special case (just like we did with the first651final int tileVal = (val << _TILE_SIZE_LG);652for (; tx < tx1; tx++) {653_touchedTile[tx] += tileVal;654}655}656// they will be equal unless x0 >> TILE_SIZE_LG == tx1657if (tx == tx1) {658final int txXCoord = tx << _TILE_SIZE_LG;659final int nextTileXCoord = (tx + 1) << _TILE_SIZE_LG;660661final int lastXCoord = (nextTileXCoord <= x1) ? nextTileXCoord : x1;662_touchedTile[tx] += val * (lastXCoord - txXCoord);663}664}665666int alphaSumInTile(final int x) {667return touchedTile[(x - bboxX0) >> TILE_SIZE_LG];668}669670@Override671public String toString() {672return "bbox = ["673+ bboxX0 + ", " + bboxY0 + " => "674+ bboxX1 + ", " + bboxY1 + "]\n";675}676677private static byte[] buildAlphaMap(final int maxalpha) {678// double size !679final byte[] alMap = new byte[maxalpha << 1];680final int halfmaxalpha = maxalpha >> 2;681for (int i = 0; i <= maxalpha; i++) {682alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha);683// System.out.println("alphaMap[" + i + "] = "684// + Byte.toUnsignedInt(alMap[i]));685}686return alMap;687}688}689690691