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 }