1 /* 2 * Copyright (c) 2007, 2017, 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 jdk.internal.misc.Unsafe; 29 30 /** 31 * An object used to cache pre-rendered complex paths. 32 * 33 * @see Renderer 34 */ 35 public final class MarlinCache implements MarlinConst { 36 37 static final boolean FORCE_RLE = MarlinProperties.isForceRLE(); 38 static final boolean FORCE_NO_RLE = MarlinProperties.isForceNoRLE(); 39 // minimum width to try using RLE encoding: 40 static final int RLE_MIN_WIDTH 41 = Math.max(BLOCK_SIZE, MarlinProperties.getRLEMinWidth()); 42 // maximum width for RLE encoding: 43 // values are stored as int [x|alpha] where alpha is 8 bits 44 static final int RLE_MAX_WIDTH = 1 << (24 - 1); 45 46 // 2048 (pixelSize) alpha values (width) x 32 rows (tile) = 64K bytes 47 // x1 instead of 4 bytes (RLE) ie 1/4 capacity or average good RLE compression 48 static final long INITIAL_CHUNK_ARRAY = TILE_H * INITIAL_PIXEL_DIM; // 64K 49 50 // The alpha map used by this object (taken out of our map cache) to convert 51 // pixel coverage counts gotten from MarlinCache (which are in the range 52 // [0, maxalpha]) into alpha values, which are in [0,256). 53 static final byte[] ALPHA_MAP; 54 55 static final OffHeapArray ALPHA_MAP_UNSAFE; 56 57 static { 58 final byte[] _ALPHA_MAP = buildAlphaMap(MAX_AA_ALPHA); 59 60 ALPHA_MAP_UNSAFE = new OffHeapArray(_ALPHA_MAP, _ALPHA_MAP.length); // 1K 61 ALPHA_MAP =_ALPHA_MAP; 62 63 final Unsafe _unsafe = OffHeapArray.UNSAFE; 64 final long addr = ALPHA_MAP_UNSAFE.address; 65 66 for (int i = 0; i < _ALPHA_MAP.length; i++) { 67 _unsafe.putByte(addr + i, _ALPHA_MAP[i]); 68 } 69 } 70 71 int bboxX0, bboxY0, bboxX1, bboxY1; 72 73 // 1D dirty arrays 74 // row index in rowAAChunk[] 75 final long[] rowAAChunkIndex = new long[TILE_H]; 76 // first pixel (inclusive) for each row 77 final int[] rowAAx0 = new int[TILE_H]; 78 // last pixel (exclusive) for each row 79 final int[] rowAAx1 = new int[TILE_H]; 80 // encoding mode (0=raw, 1=RLE encoding) for each row 81 final int[] rowAAEnc = new int[TILE_H]; 82 // coded length (RLE encoding) for each row 83 final long[] rowAALen = new long[TILE_H]; 84 // last position in RLE decoding for each row (getAlpha): 85 final long[] rowAAPos = new long[TILE_H]; 86 87 // dirty off-heap array containing pixel coverages for (32) rows (packed) 88 // if encoding=raw, it contains alpha coverage values (val) as integer 89 // if encoding=RLE, it contains tuples (val, last x-coordinate exclusive) 90 // use rowAAx0/rowAAx1 to get row indices within this chunk 91 final OffHeapArray rowAAChunk; 92 93 // current position in rowAAChunk array 94 long rowAAChunkPos; 95 96 // touchedTile[i] is the sum of all the alphas in the tile with 97 // x=j*TILE_SIZE+bboxX0. 98 int[] touchedTile; 99 100 // per-thread renderer stats 101 final RendererStats rdrStats; 102 103 // touchedTile ref (clean) 104 private final IntArrayCache.Reference touchedTile_ref; 105 106 int tileMin, tileMax; 107 108 boolean useRLE = false; 109 110 MarlinCache(final IRendererContext rdrCtx) { 111 this.rdrStats = rdrCtx.stats(); 112 113 rowAAChunk = rdrCtx.newOffHeapArray(INITIAL_CHUNK_ARRAY); // 64K 114 115 touchedTile_ref = rdrCtx.newCleanIntArrayRef(INITIAL_ARRAY); // 1K = 1 tile line 116 touchedTile = touchedTile_ref.initial; 117 118 // tile used marks: 119 tileMin = Integer.MAX_VALUE; 120 tileMax = Integer.MIN_VALUE; 121 } 122 123 void init(int minx, int miny, int maxx, int maxy) 124 { 125 // assert maxy >= miny && maxx >= minx; 126 bboxX0 = minx; 127 bboxY0 = miny; 128 bboxX1 = maxx; 129 bboxY1 = maxy; 130 131 final int width = (maxx - minx); 132 133 if (FORCE_NO_RLE) { 134 useRLE = false; 135 } else if (FORCE_RLE) { 136 useRLE = true; 137 } else { 138 // heuristics: use both bbox area and complexity 139 // ie number of primitives: 140 141 // fast check min and max width (maxx < 23bits): 142 useRLE = (width > RLE_MIN_WIDTH && width < RLE_MAX_WIDTH); 143 } 144 145 // the ceiling of (maxy - miny + 1) / TILE_SIZE; 146 final int nxTiles = (width + TILE_W) >> TILE_W_LG; 147 148 if (nxTiles > INITIAL_ARRAY) { 149 if (DO_STATS) { 150 rdrStats.stat_array_marlincache_touchedTile.add(nxTiles); 151 } 152 touchedTile = touchedTile_ref.getArray(nxTiles); 153 } 154 } 155 156 /** 157 * Disposes this cache: 158 * clean up before reusing this instance 159 */ 160 void dispose() { 161 // Reset touchedTile if needed: 162 resetTileLine(0); 163 164 if (DO_STATS) { 165 rdrStats.totalOffHeap += rowAAChunk.length; 166 } 167 168 // Return arrays: 169 touchedTile = touchedTile_ref.putArray(touchedTile, 0, 0); // already zero filled 170 171 // At last: resize back off-heap rowAA to initial size 172 if (rowAAChunk.length != INITIAL_CHUNK_ARRAY) { 173 // note: may throw OOME: 174 rowAAChunk.resize(INITIAL_CHUNK_ARRAY); 175 } 176 if (DO_CLEAN_DIRTY) { 177 // Force zero-fill dirty arrays: 178 rowAAChunk.fill(BYTE_0); 179 } 180 } 181 182 void resetTileLine(final int pminY) { 183 // update bboxY0 to process a complete tile line [0 - 32] 184 bboxY0 = pminY; 185 186 // reset current pos 187 if (DO_STATS) { 188 rdrStats.stat_cache_rowAAChunk.add(rowAAChunkPos); 189 } 190 rowAAChunkPos = 0L; 191 192 // Reset touchedTile: 193 if (tileMin != Integer.MAX_VALUE) { 194 if (DO_STATS) { 195 rdrStats.stat_cache_tiles.add(tileMax - tileMin); 196 } 197 // clean only dirty touchedTile: 198 if (tileMax == 1) { 199 touchedTile[0] = 0; 200 } else { 201 IntArrayCache.fill(touchedTile, tileMin, tileMax, 0); 202 } 203 // reset tile used marks: 204 tileMin = Integer.MAX_VALUE; 205 tileMax = Integer.MIN_VALUE; 206 } 207 208 if (DO_CLEAN_DIRTY) { 209 // Force zero-fill dirty arrays: 210 rowAAChunk.fill(BYTE_0); 211 } 212 } 213 214 void clearAARow(final int y) { 215 // process tile line [0 - 32] 216 final int row = y - bboxY0; 217 218 // update pixel range: 219 rowAAx0[row] = 0; // first pixel inclusive 220 rowAAx1[row] = 0; // last pixel exclusive 221 rowAAEnc[row] = 0; // raw encoding 222 223 // note: leave rowAAChunkIndex[row] undefined 224 // and rowAALen[row] & rowAAPos[row] (RLE) 225 } 226 227 /** 228 * Copy the given alpha data into the rowAA cache 229 * @param alphaRow alpha data to copy from 230 * @param y y pixel coordinate 231 * @param px0 first pixel inclusive x0 232 * @param px1 last pixel exclusive x1 233 */ 234 void copyAARowNoRLE(final int[] alphaRow, final int y, 235 final int px0, final int px1) 236 { 237 // skip useless pixels above boundary 238 final int px_bbox1 = FloatMath.min(px1, bboxX1); 239 240 if (DO_LOG_BOUNDS) { 241 MarlinUtils.logInfo("row = [" + px0 + " ... " + px_bbox1 242 + " (" + px1 + ") [ for y=" + y); 243 } 244 245 final int row = y - bboxY0; 246 247 // update pixel range: 248 rowAAx0[row] = px0; // first pixel inclusive 249 rowAAx1[row] = px_bbox1; // last pixel exclusive 250 rowAAEnc[row] = 0; // raw encoding 251 252 // get current position (bytes): 253 final long pos = rowAAChunkPos; 254 // update row index to current position: 255 rowAAChunkIndex[row] = pos; 256 257 // determine need array size: 258 // for RLE encoding, position must be aligned to 4 bytes (int): 259 // align - 1 = 3 so add +3 and round-off by mask ~3 = -4 260 final long needSize = pos + ((px_bbox1 - px0 + 3) & -4); 261 262 // update next position (bytes): 263 rowAAChunkPos = needSize; 264 265 // update row data: 266 final OffHeapArray _rowAAChunk = rowAAChunk; 267 // ensure rowAAChunk capacity: 268 if (_rowAAChunk.length < needSize) { 269 expandRowAAChunk(needSize); 270 } 271 if (DO_STATS) { 272 rdrStats.stat_cache_rowAA.add(px_bbox1 - px0); 273 } 274 275 // rowAA contains only alpha values for range[x0; x1[ 276 final int[] _touchedTile = touchedTile; 277 final int _TILE_SIZE_LG = TILE_W_LG; 278 279 final int from = px0 - bboxX0; // first pixel inclusive 280 final int to = px_bbox1 - bboxX0; // last pixel exclusive 281 282 final Unsafe _unsafe = OffHeapArray.UNSAFE; 283 final long SIZE_BYTE = 1L; 284 final long addr_alpha = ALPHA_MAP_UNSAFE.address; 285 long addr_off = _rowAAChunk.address + pos; 286 287 // compute alpha sum into rowAA: 288 for (int x = from, val = 0; x < to; x++) { 289 // alphaRow is in [0; MAX_COVERAGE] 290 val += alphaRow[x]; // [from; to[ 291 292 // ensure values are in [0; MAX_AA_ALPHA] range 293 if (DO_AA_RANGE_CHECK) { 294 if (val < 0) { 295 System.out.println("Invalid coverage = " + val); 296 val = 0; 297 } 298 if (val > MAX_AA_ALPHA) { 299 System.out.println("Invalid coverage = " + val); 300 val = MAX_AA_ALPHA; 301 } 302 } 303 304 // store alpha sum (as byte): 305 if (val == 0) { 306 _unsafe.putByte(addr_off, (byte)0); // [0-255] 307 } else { 308 _unsafe.putByte(addr_off, _unsafe.getByte(addr_alpha + val)); // [0-255] 309 310 // update touchedTile 311 _touchedTile[x >> _TILE_SIZE_LG] += val; 312 } 313 addr_off += SIZE_BYTE; 314 } 315 316 // update tile used marks: 317 int tx = from >> _TILE_SIZE_LG; // inclusive 318 if (tx < tileMin) { 319 tileMin = tx; 320 } 321 322 tx = ((to - 1) >> _TILE_SIZE_LG) + 1; // exclusive (+1 to be sure) 323 if (tx > tileMax) { 324 tileMax = tx; 325 } 326 327 if (DO_LOG_BOUNDS) { 328 MarlinUtils.logInfo("clear = [" + from + " ... " + to + "["); 329 } 330 331 // Clear alpha row for reuse: 332 IntArrayCache.fill(alphaRow, from, px1 + 1 - bboxX0, 0); 333 } 334 335 void copyAARowRLE_WithBlockFlags(final int[] blkFlags, final int[] alphaRow, 336 final int y, final int px0, final int px1) 337 { 338 // Copy rowAA data into the piscesCache if one is present 339 final int _bboxX0 = bboxX0; 340 341 // process tile line [0 - 32] 342 final int row = y - bboxY0; 343 final int from = px0 - _bboxX0; // first pixel inclusive 344 345 // skip useless pixels above boundary 346 final int px_bbox1 = FloatMath.min(px1, bboxX1); 347 final int to = px_bbox1 - _bboxX0; // last pixel exclusive 348 349 if (DO_LOG_BOUNDS) { 350 MarlinUtils.logInfo("row = [" + px0 + " ... " + px_bbox1 351 + " (" + px1 + ") [ for y=" + y); 352 } 353 354 // get current position: 355 final long initialPos = startRLERow(row, px0, px_bbox1); 356 357 // determine need array size: 358 // pessimistic: max needed size = deltaX x 4 (1 int) 359 final long needSize = initialPos + ((to - from) << 2); 360 361 // update row data: 362 OffHeapArray _rowAAChunk = rowAAChunk; 363 // ensure rowAAChunk capacity: 364 if (_rowAAChunk.length < needSize) { 365 expandRowAAChunk(needSize); 366 } 367 368 final Unsafe _unsafe = OffHeapArray.UNSAFE; 369 final long SIZE_INT = 4L; 370 final long addr_alpha = ALPHA_MAP_UNSAFE.address; 371 long addr_off = _rowAAChunk.address + initialPos; 372 373 final int[] _touchedTile = touchedTile; 374 final int _TILE_SIZE_LG = TILE_W_LG; 375 final int _BLK_SIZE_LG = BLOCK_SIZE_LG; 376 377 // traverse flagged blocks: 378 final int blkW = (from >> _BLK_SIZE_LG); 379 final int blkE = (to >> _BLK_SIZE_LG) + 1; 380 // ensure last block flag = 0 to process final block: 381 blkFlags[blkE] = 0; 382 383 // Perform run-length encoding and store results in the piscesCache 384 int val = 0; 385 int cx0 = from; 386 int runLen; 387 388 final int _MAX_VALUE = Integer.MAX_VALUE; 389 int last_t0 = _MAX_VALUE; 390 391 int skip = 0; 392 393 for (int t = blkW, blk_x0, blk_x1, cx, delta; t <= blkE; t++) { 394 if (blkFlags[t] != 0) { 395 blkFlags[t] = 0; 396 397 if (last_t0 == _MAX_VALUE) { 398 last_t0 = t; 399 } 400 continue; 401 } 402 if (last_t0 != _MAX_VALUE) { 403 // emit blocks: 404 blk_x0 = FloatMath.max(last_t0 << _BLK_SIZE_LG, from); 405 last_t0 = _MAX_VALUE; 406 407 // (last block pixel+1) inclusive => +1 408 blk_x1 = FloatMath.min((t << _BLK_SIZE_LG) + 1, to); 409 410 for (cx = blk_x0; cx < blk_x1; cx++) { 411 if ((delta = alphaRow[cx]) != 0) { 412 alphaRow[cx] = 0; 413 414 // not first rle entry: 415 if (cx != cx0) { 416 runLen = cx - cx0; 417 418 // store alpha coverage (ensure within bounds): 419 // as [absX|val] where: 420 // absX is the absolute x-coordinate: 421 // note: last pixel exclusive (>= 0) 422 // note: it should check X is smaller than 23bits (overflow)! 423 424 // check address alignment to 4 bytes: 425 if (DO_CHECK_UNSAFE) { 426 if ((addr_off & 3) != 0) { 427 MarlinUtils.logInfo("Misaligned Unsafe address: " + addr_off); 428 } 429 } 430 431 // special case to encode entries into a single int: 432 if (val == 0) { 433 _unsafe.putInt(addr_off, 434 ((_bboxX0 + cx) << 8) 435 ); 436 } else { 437 _unsafe.putInt(addr_off, 438 ((_bboxX0 + cx) << 8) 439 | (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0-255] 440 ); 441 442 if (runLen == 1) { 443 _touchedTile[cx0 >> _TILE_SIZE_LG] += val; 444 } else { 445 touchTile(cx0, val, cx, runLen, _touchedTile); 446 } 447 } 448 addr_off += SIZE_INT; 449 450 if (DO_STATS) { 451 rdrStats.hist_tile_generator_encoding_runLen 452 .add(runLen); 453 } 454 cx0 = cx; 455 } 456 457 // alpha value = running sum of coverage delta: 458 val += delta; 459 460 // ensure values are in [0; MAX_AA_ALPHA] range 461 if (DO_AA_RANGE_CHECK) { 462 if (val < 0) { 463 System.out.println("Invalid coverage = " + val); 464 val = 0; 465 } 466 if (val > MAX_AA_ALPHA) { 467 System.out.println("Invalid coverage = " + val); 468 val = MAX_AA_ALPHA; 469 } 470 } 471 } 472 } 473 } else if (DO_STATS) { 474 skip++; 475 } 476 } 477 478 // Process remaining RLE run: 479 runLen = to - cx0; 480 481 // store alpha coverage (ensure within bounds): 482 // as (int)[absX|val] where: 483 // absX is the absolute x-coordinate in bits 31 to 8 and val in bits 0..7 484 // note: last pixel exclusive (>= 0) 485 // note: it should check X is smaller than 23bits (overflow)! 486 487 // check address alignment to 4 bytes: 488 if (DO_CHECK_UNSAFE) { 489 if ((addr_off & 3) != 0) { 490 MarlinUtils.logInfo("Misaligned Unsafe address: " + addr_off); 491 } 492 } 493 494 // special case to encode entries into a single int: 495 if (val == 0) { 496 _unsafe.putInt(addr_off, 497 ((_bboxX0 + to) << 8) 498 ); 499 } else { 500 _unsafe.putInt(addr_off, 501 ((_bboxX0 + to) << 8) 502 | (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0-255] 503 ); 504 505 if (runLen == 1) { 506 _touchedTile[cx0 >> _TILE_SIZE_LG] += val; 507 } else { 508 touchTile(cx0, val, to, runLen, _touchedTile); 509 } 510 } 511 addr_off += SIZE_INT; 512 513 if (DO_STATS) { 514 rdrStats.hist_tile_generator_encoding_runLen.add(runLen); 515 } 516 517 long len = (addr_off - _rowAAChunk.address); 518 519 // update coded length as bytes: 520 rowAALen[row] = (len - initialPos); 521 522 // update current position: 523 rowAAChunkPos = len; 524 525 if (DO_STATS) { 526 rdrStats.stat_cache_rowAA.add(rowAALen[row]); 527 rdrStats.hist_tile_generator_encoding_ratio.add( 528 (100 * skip) / (blkE - blkW) 529 ); 530 } 531 532 // update tile used marks: 533 int tx = from >> _TILE_SIZE_LG; // inclusive 534 if (tx < tileMin) { 535 tileMin = tx; 536 } 537 538 tx = ((to - 1) >> _TILE_SIZE_LG) + 1; // exclusive (+1 to be sure) 539 if (tx > tileMax) { 540 tileMax = tx; 541 } 542 543 // Clear alpha row for reuse: 544 alphaRow[to] = 0; 545 if (DO_CHECKS) { 546 IntArrayCache.check(blkFlags, blkW, blkE, 0); 547 IntArrayCache.check(alphaRow, from, px1 + 1 - bboxX0, 0); 548 } 549 } 550 551 long startRLERow(final int row, final int x0, final int x1) { 552 // rows are supposed to be added by increasing y. 553 rowAAx0[row] = x0; // first pixel inclusive 554 rowAAx1[row] = x1; // last pixel exclusive 555 rowAAEnc[row] = 1; // RLE encoding 556 rowAAPos[row] = 0L; // position = 0 557 558 // update row index to current position: 559 return (rowAAChunkIndex[row] = rowAAChunkPos); 560 } 561 562 private void expandRowAAChunk(final long needSize) { 563 if (DO_STATS) { 564 rdrStats.stat_array_marlincache_rowAAChunk.add(needSize); 565 } 566 567 // note: throw IOOB if neededSize > 2Gb: 568 final long newSize = ArrayCacheConst.getNewLargeSize(rowAAChunk.length, 569 needSize); 570 571 rowAAChunk.resize(newSize); 572 } 573 574 private void touchTile(final int x0, final int val, final int x1, 575 final int runLen, 576 final int[] _touchedTile) 577 { 578 // the x and y of the current row, minus bboxX0, bboxY0 579 // process tile line [0 - 32] 580 final int _TILE_SIZE_LG = TILE_W_LG; 581 582 // update touchedTile 583 int tx = (x0 >> _TILE_SIZE_LG); 584 585 // handle trivial case: same tile (x0, x0+runLen) 586 if (tx == (x1 >> _TILE_SIZE_LG)) { 587 // same tile: 588 _touchedTile[tx] += val * runLen; 589 return; 590 } 591 592 final int tx1 = (x1 - 1) >> _TILE_SIZE_LG; 593 594 if (tx <= tx1) { 595 final int nextTileXCoord = (tx + 1) << _TILE_SIZE_LG; 596 _touchedTile[tx++] += val * (nextTileXCoord - x0); 597 } 598 if (tx < tx1) { 599 // don't go all the way to tx1 - we need to handle the last 600 // tile as a special case (just like we did with the first 601 final int tileVal = (val << _TILE_SIZE_LG); 602 for (; tx < tx1; tx++) { 603 _touchedTile[tx] += tileVal; 604 } 605 } 606 // they will be equal unless x0 >> TILE_SIZE_LG == tx1 607 if (tx == tx1) { 608 final int txXCoord = tx << _TILE_SIZE_LG; 609 final int nextTileXCoord = (tx + 1) << _TILE_SIZE_LG; 610 611 final int lastXCoord = (nextTileXCoord <= x1) ? nextTileXCoord : x1; 612 _touchedTile[tx] += val * (lastXCoord - txXCoord); 613 } 614 } 615 616 int alphaSumInTile(final int x) { 617 return touchedTile[(x - bboxX0) >> TILE_W_LG]; 618 } 619 620 @Override 621 public String toString() { 622 return "bbox = [" 623 + bboxX0 + ", " + bboxY0 + " => " 624 + bboxX1 + ", " + bboxY1 + "]\n"; 625 } 626 627 private static byte[] buildAlphaMap(final int maxalpha) { 628 // double size ! 629 final byte[] alMap = new byte[maxalpha << 1]; 630 final int halfmaxalpha = maxalpha >> 2; 631 for (int i = 0; i <= maxalpha; i++) { 632 alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha); 633 // System.out.println("alphaMap[" + i + "] = " 634 // + Byte.toUnsignedInt(alMap[i])); 635 } 636 return alMap; 637 } 638 }