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 }