1 /* 2 * Copyright (c) 2017, 2018, 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 package sun.java2d.xr; 26 27 import java.awt.Point; 28 import java.awt.geom.AffineTransform; 29 import java.awt.geom.Point2D; 30 import java.nio.ByteBuffer; 31 import java.nio.ByteOrder; 32 import sun.java2d.pipe.Region; 33 import static sun.java2d.xr.XRUtils.XDoubleToFixed; 34 35 /** 36 * XRBackendDeferred 37 * 38 * @author Clemens Eisserer 39 */ 40 public class XRBackendDeferred extends XRBackendNative { 41 42 private static final byte RENDER_CHANGE_PICTURE = 5; 43 private static final byte RENDER_SET_PICTURE_CLIP_RECTANGLES = 6; 44 private static final byte RENDER_FREE_PICTURE = 7; 45 private static final byte RENDER_COMPOSITE = 8; 46 private static final byte RENDER_FILL_RECTANGLES = 26; 47 private static final byte RENDER_SET_PICTURE_TRANSFORM = 28; 48 private static final byte RENDER_SET_PICTURE_FILTER = 30; 49 private static final byte RENDER_COMPOSITE_GLYPH32 = 25; 50 private static final byte RENDER_CREATE_LINEAR_GRADIENT = 34; 51 private static final byte RENDER_CREATE_RADIAL_GRADIENT = 35; 52 private static final byte FREE_PIXMAP = 54; 53 54 private static final int BUFFER_SIZE = 128*1024; 55 private static int RENDER_MAJOR_OPCODE; 56 57 ByteBuffer buffer; 58 59 boolean socketTaken; 60 int requestCounter; 61 int xcbReqSinceFlush; 62 63 64 AATileBufMan aaTileMan; 65 66 //TODO: Check for RadialGradient Correctness 67 //TODO: Check for Text32 mask attribute 68 69 public XRBackendDeferred() { 70 buffer = ByteBuffer.allocateDirect(BUFFER_SIZE); 71 buffer.order(ByteOrder.LITTLE_ENDIAN); 72 73 nativeInit(buffer); 74 75 socketTaken = false; 76 77 aaTileMan = new AATileBufMan(); 78 } 79 80 public void initResources(int parentXID) { 81 aaTileMan.initResources(this, parentXID); 82 } 83 84 protected void takeSocket() { 85 if (!socketTaken) { 86 socketTaken = true; 87 88 aaTileMan.pollPendingFences(); 89 90 long shmFenceSeq = takeSocketNative(aaTileMan.isFencePending()); 91 if(shmFenceSeq != -1) { 92 aaTileMan.registerFenceSeqForActiveBuffer(shmFenceSeq); 93 } 94 95 xcbReqSinceFlush = 0; 96 } 97 } 98 99 protected void releaseSocket() { 100 if (socketTaken) { 101 flushBuffer(true); 102 socketTaken = false; 103 } 104 } 105 106 protected void flushBuffer(boolean handoff) { 107 if (requestCounter > 0) { 108 AATileBuffer tileBuffer = aaTileMan.getActiveTileBuffer(); 109 110 // if(System.getProperty("sun.java2d.debugxrdeferred") != null) { 111 // System.out.println("BufferFlush with AA tiles queued: " + tileBuffer.getTileCount() + " buffer: " + tileBuffer.getBufferBounds()); 112 // System.out.println("Flush xreq: "+xcbReqSinceFlush+ " req:"+requestCounter+" bytes: "+buffer.position()); 113 // } 114 115 Point maskBufferBounds = tileBuffer.getBufferBounds(); 116 boolean useShmPutImg = tileBuffer.isUploadWithShmProfitable(); 117 118 releaseSocketNative(requestCounter, buffer.position(), aaTileMan.getMaskPixmapXid(), aaTileMan.getMaskGCPtr(), maskBufferBounds.x, 119 maskBufferBounds.y, tileBuffer.getBufferScan(), tileBuffer.getYOffset(), useShmPutImg, tileBuffer.getByteBuffer()); 120 121 // System.out.println("was shm: " + shmMaskUsed); 122 123 aaTileMan.markActiveBufferFlushed(useShmPutImg); 124 125 buffer.clear(); 126 requestCounter = 0; 127 128 if(!handoff) { 129 forceSocketReturn(); 130 } 131 } 132 } 133 134 private native void releaseSocketNative(int requests, int bufferSize, int maskPixmap, long maskGC, int maskWidth, int maskHeight, int maskScan, int maskYOffset, boolean useShmPutImage, ByteBuffer buffer); 135 136 private native long takeSocketNative(boolean queueShmFence); 137 138 private static native void forceSocketReturn(); 139 140 private native void issueSyncReq(); 141 142 private native int generateXID(); 143 144 private native void nativeInit(ByteBuffer protoBuf); 145 146 private void initNextRequest(int requestLength) { 147 takeSocket(); 148 149 if (xcbReqSinceFlush > 65500) { 150 issueSyncReq(); 151 takeSocket(); 152 } 153 154 int maskTilesQueued = aaTileMan.getActiveTileBuffer().getTileCount(); 155 if ((maskTilesQueued == 0 && buffer.position() > 4 * 1024) 156 || (buffer.position() + (requestLength * 4) > 128 * 1024)) { 157 flushBuffer(false); 158 } 159 160 requestCounter++; 161 xcbReqSinceFlush++; 162 } 163 164 @Override 165 public void renderRectangle(int dst, byte op, XRColor color, 166 int x, int y, int width, int height) { 167 168 if(socketTaken) { 169 initNextRequest(7); 170 putRectHeader(dst, op, color, 7); 171 172 buffer.putShort((short) x); 173 buffer.putShort((short) y); 174 buffer.putShort((short) width); 175 buffer.putShort((short) height); 176 } else { 177 super.renderRectangle(dst, op, color, x, y, width, height); 178 } 179 } 180 181 @Override 182 public void renderRectangles(int dst, byte op, XRColor color, 183 GrowableRectArray rects) { 184 int reqLen = 5 + 2 * rects.getSize(); 185 186 if (socketTaken && reqLen <= BUFFER_SIZE) { 187 initNextRequest(reqLen); 188 putRectHeader(dst, op, color, reqLen); 189 putRects(rects); 190 } else { 191 super.renderRectangles(dst, op, color, rects); 192 } 193 } 194 195 @Override 196 public void setPictureRepeat(int picture, int repeat) { 197 if(socketTaken) { 198 initNextRequest(4); 199 200 buffer.put((byte) RENDER_MAJOR_OPCODE); 201 buffer.put(RENDER_CHANGE_PICTURE); 202 buffer.putShort((short) 4); 203 204 buffer.putInt(picture); 205 buffer.putInt(1); //CPRepeat 206 207 buffer.putInt(repeat); 208 } else { 209 super.setPictureRepeat(picture, repeat); 210 } 211 } 212 213 @Override 214 public void setFilter(int picture, int filter) { 215 if(socketTaken) { 216 initNextRequest(4); 217 218 buffer.put((byte) RENDER_MAJOR_OPCODE); 219 buffer.put(RENDER_SET_PICTURE_FILTER); 220 buffer.putShort((short) 4); 221 222 buffer.putInt(picture); 223 buffer.putShort((short) 4); //filtertLen 224 buffer.putShort((short) 0); //pad 225 226 buffer.put(XRUtils.getFilterName(filter)); 227 }else { 228 super.setFilter(picture, filter); 229 } 230 } 231 232 @Override 233 public void renderComposite(byte op, int src, int mask, 234 int dst, int srcX, int srcY, 235 int maskX, int maskY, int dstX, int dstY, 236 int width, int height) { 237 238 if(socketTaken) { 239 initNextRequest(9); 240 241 buffer.put((byte) RENDER_MAJOR_OPCODE); 242 buffer.put(RENDER_COMPOSITE); 243 buffer.putShort((short) 9); 244 buffer.put(op); //op 245 246 //padding 247 buffer.put((byte) 0); 248 buffer.putShort((short) 0); 249 250 buffer.putInt(src); 251 buffer.putInt(mask); 252 buffer.putInt(dst); 253 254 buffer.putShort((short) srcX); 255 buffer.putShort((short) srcY); 256 buffer.putShort((short) maskX); 257 buffer.putShort((short) maskY); 258 buffer.putShort((short) dstX); 259 buffer.putShort((short) dstY); 260 buffer.putShort((short) width); 261 buffer.putShort((short) height); 262 } else { 263 super.renderComposite(op, src, mask, dst, srcX, srcY, maskX, maskY, dstX, dstY, width, height); 264 } 265 } 266 267 @Override 268 public void maskedComposite(byte op, int src, int eaMask, int dst, 269 int srcX, int srcY, int dstX, int dstY, int width, 270 int height, int maskScan, int maskOff, byte[] mask, float ea) { 271 272 if(mask == null) { 273 renderComposite(op, src, eaMask, dst, srcX, srcY, 0, 0, dstX, dstY, width, height); 274 } else { 275 AATileBuffer tileBuffer = aaTileMan.getActiveTileBuffer(); 276 277 Point tilePos = tileBuffer.storeMaskTile(mask, width, height, maskOff, maskScan, ea); 278 279 if(tilePos == null) { 280 flushBuffer(false); 281 maskedComposite(op, src, eaMask, dst, srcX, srcY, dstX, dstY, width, height, maskScan, maskOff, mask, ea); 282 return; 283 } 284 285 // Taking the socket here ensures, we emit the XRenderComposite ourself 286 // so we can generate the XPutImage later when we have to hand the socket back to XCB 287 takeSocket(); 288 renderComposite(op, src, aaTileMan.getMaskPictureXid(), dst, srcX, srcY, tilePos.x, tilePos.y, dstX, dstY, width, height); 289 } 290 } 291 292 293 @Override 294 public void freePixmap(int pixmap) { 295 if(socketTaken) { 296 initNextRequest(2); 297 298 buffer.put(FREE_PIXMAP); 299 buffer.put((byte) 0); 300 buffer.putShort((short) 2); 301 buffer.putInt(pixmap); 302 } else { 303 super.freePixmap(pixmap); 304 } 305 } 306 307 @Override 308 public void freePicture(int picture) { 309 if(socketTaken) { 310 initNextRequest(2); 311 312 buffer.put((byte) RENDER_MAJOR_OPCODE); 313 buffer.put(RENDER_FREE_PICTURE); 314 buffer.putShort((short) 2); 315 316 buffer.putInt(picture); 317 } else { 318 super.freePicture(picture); 319 } 320 } 321 322 @Override 323 public void setPictureTransform(int picture, AffineTransform transform) { 324 if(socketTaken) { 325 initNextRequest(11); 326 327 buffer.put((byte) RENDER_MAJOR_OPCODE); 328 buffer.put(RENDER_SET_PICTURE_TRANSFORM); 329 buffer.putShort((short) 11); 330 331 buffer.putInt(picture); 332 333 buffer.putInt(XDoubleToFixed(transform.getScaleX())); 334 buffer.putInt(XDoubleToFixed(transform.getShearX())); 335 buffer.putInt(XDoubleToFixed(transform.getTranslateX())); 336 buffer.putInt(XDoubleToFixed(transform.getShearY())); 337 buffer.putInt(XDoubleToFixed(transform.getScaleY())); 338 buffer.putInt(XDoubleToFixed(transform.getTranslateY())); 339 buffer.putInt(0); 340 buffer.putInt(0); 341 buffer.putInt(1 << 16); 342 } else { 343 super.setPictureTransform(picture, transform); 344 } 345 } 346 347 @Override 348 public int createLinearGradient(Point2D p1, Point2D p2, float[] fractions, 349 int[] pixels, int repeat) { 350 int reqLen = 7 + fractions.length + 2 * pixels.length; 351 352 if (socketTaken && reqLen < BUFFER_SIZE) { 353 354 int xid = generateXID(); 355 356 initNextRequest(reqLen); 357 358 buffer.put((byte) RENDER_MAJOR_OPCODE); 359 buffer.put(RENDER_CREATE_LINEAR_GRADIENT); 360 buffer.putShort((short) reqLen); 361 362 buffer.putInt(xid); 363 364 buffer.putInt(XDoubleToFixed(p1.getX())); 365 buffer.putInt(XDoubleToFixed(p1.getY())); 366 buffer.putInt(XDoubleToFixed(p2.getX())); 367 buffer.putInt(XDoubleToFixed(p2.getY())); 368 369 putFractions(fractions); 370 371 putPixels(pixels); 372 373 return xid; 374 } else { 375 return super.createLinearGradient(p1, p2, fractions, pixels, repeat); 376 } 377 } 378 379 @Override 380 public int createRadialGradient(float centerX, float centerY, 381 float innerRadius, float outerRadius, 382 float[] fractions, int[] pixels, int repeat) { 383 384 int reqLen = 8 + fractions.length + 2 * pixels.length; 385 386 if (socketTaken && reqLen < BUFFER_SIZE) { 387 int xid = generateXID(); 388 389 initNextRequest(reqLen); 390 391 buffer.put((byte) RENDER_MAJOR_OPCODE); 392 buffer.put(RENDER_CREATE_RADIAL_GRADIENT); 393 buffer.putShort((short) reqLen); 394 395 buffer.putInt(xid); 396 397 //inner 398 buffer.putInt(XDoubleToFixed(centerX)); 399 buffer.putInt(XDoubleToFixed(centerY)); 400 //outer 401 buffer.putInt(XDoubleToFixed(centerX)); 402 buffer.putInt(XDoubleToFixed(centerY)); 403 404 buffer.putInt(XDoubleToFixed(innerRadius)); 405 buffer.putInt(XDoubleToFixed(outerRadius)); 406 407 putFractions(fractions); 408 putPixels(pixels); 409 410 return xid; 411 } else { 412 return super.createRadialGradient(centerX, centerY, innerRadius, outerRadius, fractions, pixels, repeat); 413 } 414 } 415 416 // @Override 417 public void XRenderCompositeText(byte op, int src, int dst, 418 int maskFormatID, 419 int sx, int sy, int dx, int dy, 420 int glyphset, GrowableEltArray elts) { 421 422 if(!socketTaken) { 423 super.XRenderCompositeText(op, src, dst, maskFormatID, sx, sy, dx, dy, glyphset, elts); 424 return; 425 } 426 427 // super.XRenderCompositeText(op, src, dst, maskFormatID, sx, sy, dx, dy, glyphset, elts); 428 int len = 7 + elts.getGlyphs().getSize() + elts.getSize() * 2; 429 int activeGlyphSet = elts.getGlyphSet(0); 430 431 //calculate request length 432 for (int elt = 0; elt < elts.getSize(); elt++) { 433 int newGlyphSet = elts.getGlyphSet(elt); 434 if (activeGlyphSet != newGlyphSet) { 435 len += 3; 436 } 437 } 438 activeGlyphSet = elts.getGlyphSet(0); 439 440 if (len <= BUFFER_SIZE) { 441 initNextRequest(len); 442 443 buffer.put((byte) RENDER_MAJOR_OPCODE); 444 buffer.put(RENDER_COMPOSITE_GLYPH32); 445 buffer.putShort((short) len); 446 447 //TODO: Implement glyphset change!top 448 buffer.put(op); //op 449 450 //padding 451 buffer.put((byte) 0); 452 buffer.putShort((short) 0); 453 454 buffer.putInt(src); 455 buffer.putInt(dst); 456 buffer.putInt(0); 457 458 buffer.putInt(activeGlyphSet); 459 460 buffer.putShort(XRUtils.clampToShort(sx)); 461 buffer.putShort(XRUtils.clampToShort(sy)); 462 463 int glyphsWritten = 0; 464 465 for (int elt = 0; elt < elts.getSize(); elt++) { 466 int newGlyphSet = elts.getGlyphSet(elt); 467 if (activeGlyphSet != newGlyphSet) { 468 putGlyphSet(255, 0, 0); 469 buffer.putInt(newGlyphSet); 470 } 471 472 putGlyphSet(elts.getCharCnt(elt), elts.getXOff(elt), elts.getYOff(elt)); 473 474 for (int g = 0; g < elts.getCharCnt(elt); g++) { 475 buffer.putInt(elts.getGlyphs().getInt(glyphsWritten++)); 476 } 477 } 478 } else { 479 super.XRenderCompositeText(op, src, dst, maskFormatID, sx, sy, dx, dy, glyphset, elts); 480 } 481 } 482 483 @Override 484 public void setClipRectangles(int picture, Region clip) { 485 if (socketTaken && clip == null) { 486 XRSetClipRectangle(picture, 0, 0, 32767, 32767); 487 } else if (socketTaken && clip.isRectangular()) { 488 XRSetClipRectangle(picture, clip.getLoX(), clip.getLoY(), 489 clip.getHiX(), clip.getHiY()); 490 } else { 491 super.setClipRectangles(picture, clip); 492 } 493 } 494 495 private void putFractions(float[] fractions) { 496 buffer.putInt(fractions.length); 497 for (int i = 0; i < fractions.length; i++) { 498 buffer.putInt(XRUtils.XDoubleToFixed(fractions[i])); 499 } 500 } 501 502 private void putPixels(int[] pixels) { 503 for (int i = 0; i < pixels.length; i++) { 504 XRColor c = new XRColor(); 505 c.setColorValues(pixels[i]); 506 buffer.putShort((short) c.red); 507 buffer.putShort((short) c.green); 508 buffer.putShort((short) c.blue); 509 buffer.putShort((short) c.alpha); 510 } 511 } 512 513 private void putGlyphSet(int charCnt, int xOff, int yOff) { 514 buffer.put((byte) charCnt); 515 //padding 516 buffer.put((byte) 0); 517 buffer.putShort((short) 0); 518 519 buffer.putShort((short) xOff); 520 buffer.putShort((short) yOff); 521 } 522 523 private void XRSetClipRectangle(int picture, int x, int y, int x2, int y2) { 524 initNextRequest(5); 525 526 // System.out.println("Set clip: "+x+"/"+y+" "+width+"/"+height); 527 buffer.put((byte) RENDER_MAJOR_OPCODE); 528 buffer.put(RENDER_SET_PICTURE_CLIP_RECTANGLES); 529 buffer.putShort((short) 5); 530 531 buffer.putInt(picture); 532 buffer.putShort((short) 0); 533 buffer.putShort((short) 0); 534 535 buffer.putShort(XRUtils.clampToShort(x)); 536 buffer.putShort(XRUtils.clampToShort(y)); 537 buffer.putShort((short) XRUtils.clampToUShort((x2 - x))); 538 buffer.putShort((short) XRUtils.clampToUShort((y2 - y))); 539 } 540 541 private void putRectHeader(int dst, byte op, XRColor color, int reqLen) { 542 buffer.put((byte) RENDER_MAJOR_OPCODE); 543 buffer.put(RENDER_FILL_RECTANGLES); 544 buffer.putShort((short) reqLen); 545 buffer.put(op); //op 546 547 //padding 548 buffer.put((byte) 0); 549 buffer.putShort((short) 0); 550 551 buffer.putInt(dst); 552 553 putXRColor(color); 554 } 555 556 private void putRects(GrowableRectArray rects) { 557 for (int i = 0; i < rects.getSize(); i++) { 558 buffer.putShort((short) rects.getX(i)); 559 buffer.putShort((short) rects.getY(i)); 560 buffer.putShort((short) rects.getWidth(i)); 561 buffer.putShort((short) rects.getHeight(i)); 562 } 563 } 564 565 private void putXRColor(XRColor color) { 566 buffer.putShort((short) color.red); 567 buffer.putShort((short) color.green); 568 buffer.putShort((short) color.blue); 569 buffer.putShort((short) color.alpha); 570 } 571 }